mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-26 18:20:44 +00:00
New HLS, DASH and MSS adaptive demuxer elements
This provides new HLS, DASH and MSS adaptive demuxer elements as a single plugin. These elements offer many improvements over the legacy elements. They will only work within a streams-aware context (`urisourcebin`, `uridecodebin3`, `decodebin3`, `playbin3`, ...). Stream selection and buffering is handled internally, this allows them to directly manage the elementary streams and stream selection. Authors: * Edward Hervey <edward@centricular.com> * Jan Schmidt <jan@centricular.com> * Piotrek Brzeziński <piotr@centricular.com> * Tim-Philipp Müller <tim@centricular.com> Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2117>
This commit is contained in:
parent
8dcb8a28af
commit
af78c16dd5
101 changed files with 45968 additions and 0 deletions
|
@ -0,0 +1,319 @@
|
|||
# Adaptive Demuxers v2
|
||||
|
||||
The existing adaptive demuxer support in `gst-plugins-bad` has several pitfalls
|
||||
that prevents improving it easily. The existing design uses a model where an
|
||||
adaptive streaming element (`dashdemux`, `hlsdemux`) downloads multiplexed
|
||||
fragments of media, but then relies on other components in the pipeline to
|
||||
provide download buffering, demuxing, elementary stream handling and decoding.
|
||||
|
||||
|
||||
The problems with the old design include:
|
||||
|
||||
1. An assumption that fragment streams (to download) are equal to output
|
||||
(elementary) streams.
|
||||
|
||||
* This made it hard to expose `GstStream` and `GstStreamCollection`
|
||||
describing the available media streams, and by extension it is difficult to
|
||||
provide efficient stream selection support
|
||||
|
||||
2. By performing download buffering outside the adaptive streaming element,
|
||||
the download scheduling has no visibility into the presentation timeline.
|
||||
|
||||
* This made it impossible to handle more efficient variant selection and
|
||||
download strategy
|
||||
|
||||
3. Several issues with establishing accurate timing/duration of fragments due to
|
||||
not dealing with parsed data
|
||||
|
||||
* Especially with HLS, which does not provide detailed timing information
|
||||
about the underlying media streams to the same extent that DASH does.
|
||||
|
||||
4. Aging design that grew organically since initial adaptive demuxers and miss
|
||||
better understanding of how they should work in 2020
|
||||
|
||||
* The code is complicated and interwoven in ways that are hard to follow
|
||||
and reason about.
|
||||
|
||||
5. Use of GStreamer pipeline sources for downloading.
|
||||
|
||||
* An internal download pipeline that contains a `httpsrc -> queue2 -> src`
|
||||
chain makes download management, bandwidth estimation and stream parsing
|
||||
more difficult, and uses a new thread for each download.
|
||||
|
||||
|
||||
# New design
|
||||
|
||||
## High-level overview of the new AdaptiveDemux base class:
|
||||
|
||||
* Buffering is handled inside the adaptive streaming element, based on
|
||||
elementary streams (i.e. de-multiplexed from the downloaded fragments) and
|
||||
stored inside the `adaptivedemux`-based element.
|
||||
|
||||
* The download strategy has full visibility on bitrates, bandwidth, per-stream
|
||||
queueing level (in time and bytes), playback position, etc. This opens up the
|
||||
possibility of much more intelligent adaptive download strategies.
|
||||
|
||||
* Output pads are not handled directly by the subclasses. Instead subclasses
|
||||
specify which `tracks` of elementary streams they can provide and what
|
||||
"download streams" can provide contents for those tracks. The baseclass
|
||||
handles usage and activation of the `tracks` based on application
|
||||
`select-streams` requests, and activation of the `stream` needed to feed each
|
||||
selected `track`.
|
||||
|
||||
* Output is done from a single thread, with the various elementary streams
|
||||
packets being output in time order (i.e. behaving like a regular demuxer, with
|
||||
interleaving reduced to its minimum). There is minimal buffering downstream
|
||||
in the pipeline - only the amount required to perform decode and display.
|
||||
|
||||
* The adaptive streaming element only exposes `src` pads for the selected
|
||||
`GstStream`s. Typically, there will be one video track, one audio track and
|
||||
perhaps one subtitle track exposed at a time, for example.
|
||||
|
||||
* Stream selection is handled by the element directly. When switching on a
|
||||
new media stream, the output to the relevant source pad is switched once
|
||||
there is enough content buffered on the newly requested stream,
|
||||
providing rapid and seamless stream switching.
|
||||
|
||||
* Only 3 threads are used regardless of the number of streams/tracks. One is
|
||||
dedicated to download, one for output, and one for scheduling and feeding
|
||||
contents to the tracks.
|
||||
|
||||
|
||||
The main components of the new adaptive demuxers are:
|
||||
|
||||
* `GstAdaptiveDemuxTrack` : end-user meaningful elementary streams. Those can be
|
||||
selected by the user. They are provided by the subclasses based on the
|
||||
manifest.
|
||||
|
||||
* They each correspond to a `GstStream` of a `GstStreamCollection`
|
||||
* They are unique by `GstStreamType` and any other unique identifier specified
|
||||
by the manifest (ex: language)
|
||||
* The caps *can* change through time
|
||||
|
||||
* `OutputSlot` : A track being exposed via a source pad. This is handled by the
|
||||
parent class.
|
||||
|
||||
* `GstAdaptiveDemuxStream` : implementation-specific download stream. This is
|
||||
linked to one or more `GstAdaptiveDemuxTrack`. The contents of that stream
|
||||
will be parsed (via `parsebin`) and fed to the target tracks.
|
||||
|
||||
* What tracks are provided by a given `GstAdaptiveDemuxStream` is specified by
|
||||
the subclass. But can also be discovered at runtime if the manifest did not
|
||||
provide enough information (for example with HLS).
|
||||
|
||||
* Download thread : Receives download requests from the scheduling thread that
|
||||
can be queried and interrupted. Performs all download servicing in a
|
||||
single dedicated thread that can estimate download bandwidth across all
|
||||
simultaneous requests.
|
||||
|
||||
* Scheduling thread : In charge of deciding what new downloads should be started
|
||||
based on overall position, track buffering levels, selected tracks, current
|
||||
time ... It is also in charge of handling completed downloads. Fragment
|
||||
downloads are sent to dedicated `parsebin` elements that feed the parsed
|
||||
elementary data to `GstAdaptiveDemuxTrack`
|
||||
|
||||
* Output thread : In charge of deciding which track should be
|
||||
outputted/removed/switched (via `OutputSlot`) based on requested selection and
|
||||
track levels.
|
||||
|
||||
|
||||
## Track(s) and Stream(s)
|
||||
|
||||
Adaptive Demuxers provide one or more *Track* of elementary streams. They are
|
||||
each unique in terms of:
|
||||
|
||||
* Their type (audio, video, text, ..). Ex : `GST_STREAM_TYPE_AUDIO`
|
||||
* Optional: Their codec. Ex : `video/x-h264`
|
||||
* Optional: Their language. ex : `GST_TAG_LANGUAGE : "fr"`
|
||||
* Optional: Their number of channels (ex: stereo vs 5.1). ex
|
||||
`audio/x-vorbis,channels=2`
|
||||
* Any other feature which would make the stream "unique" either because of their
|
||||
nature (ex: video angle) or specified by the manifest as being "unique".
|
||||
|
||||
But tracks can vary over time by:
|
||||
|
||||
* bitrate
|
||||
* profile or level
|
||||
* resolution
|
||||
|
||||
They correspond to what an end-user might want to select (i.e. will be exposed
|
||||
in a `GstStreamCollection`). They are each identified by a `stream_id` provided
|
||||
by the subclass.
|
||||
|
||||
> **Note:** A manifest *can* specify that tracks that would normally be separate
|
||||
> based on the above rules (for example different codecs or channels) are
|
||||
> actually the same "end-user selectable stream" (i.e. track). In such case only
|
||||
> one track is provided and the nature of the elementary stream can change
|
||||
> through time.
|
||||
|
||||
Adaptive Demuxers subclasses also need to provide one or more *Download Stream*
|
||||
(`GstAdaptiveDemuxStream`) which are the implementation-/manifest-specific
|
||||
"streams" that each feed one or more *Track*. Those streams can also vary over
|
||||
time by bitrate/profile/resolution/... but always target the same tracks.
|
||||
|
||||
The downloaded data from each of those `GstAdaptiveDemuxStream` is fed to a
|
||||
`parsebin` element which will put the output in the associated
|
||||
`GstAdaptiveDemuxTrack`.
|
||||
|
||||
The tracks have some buffering capability, handled by the baseclass.
|
||||
|
||||
|
||||
This separation allows the base-class to:
|
||||
|
||||
* decide which download stream(s) should be (de)activated based on the current
|
||||
track selection
|
||||
* decide when to (re)start download requests based on buffering levels, positions and
|
||||
external actions.
|
||||
* Handle buffering, output and stream selection.
|
||||
|
||||
The subclass is responsible for deciding:
|
||||
|
||||
* *Which* next download should be requested for that stream based on current
|
||||
playback position, the provided encoded bitrates, estimates of download
|
||||
bandwidth, buffering levels, etc..
|
||||
|
||||
|
||||
Subclasses can also decide, before passing the downloaded data over, to:
|
||||
|
||||
* pre-parse specific headers (ex: ID3 and webvtt headers in HLS, MP4 fragment
|
||||
position, etc..).
|
||||
|
||||
* pre-parse actual content if needed because a position estimation is needed
|
||||
(ex: HLS missing accurate positioning of fragments in the overall timeline)
|
||||
|
||||
* rewrite the content altogether (for example webvtt fragments which require
|
||||
timing to be re-computed)
|
||||
|
||||
|
||||
## Timeline, position, playout
|
||||
|
||||
Adaptive Demuxers decide what to download based on a *Timeline* made of one or
|
||||
more *Tracks*.
|
||||
|
||||
The output of that *Timeline* is synchronized (each *Track* pushes downstream at
|
||||
more or less the same position in time). That position is the "Global Output
|
||||
Position".
|
||||
|
||||
The *Timeline* should have sufficient data in each track to allow all tracks to
|
||||
be decoded and played back downstream without introducing stalls. It is the goal
|
||||
of the *Scheduling thread* of adaptive demuxers to determine which fragment of
|
||||
data to download and at which moment, in order for:
|
||||
|
||||
* each track to have sufficient data for continuous playback downstream
|
||||
* the overall buffering to not exceed specified limits (in time and/or bytes)
|
||||
* the playback position to not stray away in case of live sources and
|
||||
low-latency scenarios.
|
||||
|
||||
Which *Track* is selected on that *Timeline* is either:
|
||||
|
||||
* decided by the element (default choices)
|
||||
* decided by the user (via `GST_EVENT_SELECT_STREAMS`)
|
||||
|
||||
The goal of an Adaptive Demuxer is to establish *which* fragment to download and
|
||||
*when* based on:
|
||||
|
||||
* The selected *Tracks*
|
||||
* The current *Timeline* output position
|
||||
* The current *Track* download position (i.e. how much is buffered)
|
||||
* The available bandwidth (calculated based on download speed)
|
||||
* The bitrate of each fragment stream provided
|
||||
* The current time (for live sources)
|
||||
|
||||
In the future, an Adaptive Demuxer will be able to decide to discard a fragment
|
||||
if it estimates it can switch to a higher/lower variant in time to still satisfy
|
||||
the above requirements.
|
||||
|
||||
|
||||
## Download helper and thread
|
||||
|
||||
Based on the above, each Adaptive Demuxer implementation specifies to a
|
||||
*Download Loop* which fragment to download next and when.
|
||||
|
||||
Multiple downloads can be requested at the same time on that thread. It is the
|
||||
responsibility of the *Scheduler thread* to decide what to do when a download is
|
||||
completed.
|
||||
|
||||
Since all downloads are done in a dedicated thread without any blocking, it can
|
||||
estimate current bandwidth and latency, which the element can use to switch
|
||||
variants and improve buffering strategy.
|
||||
|
||||
> **Note**: Unlike the old design, the `libsoup` library is used directly for
|
||||
> downloading, and not via external GStreamer elements. In the future, this
|
||||
> could be made modular so that other HTTP libraries can be used instead.
|
||||
|
||||
|
||||
## Stream Selection
|
||||
|
||||
When sending `GST_EVENT_STREAM_COLLECTION` downstream, the adaptive demuxer also
|
||||
specifies on the event that it can handle stream-selection. Downstream elements
|
||||
(i.e. `decodebin3`) won't attempt to do any selection but will
|
||||
handle/decode/expose all the streams provided by the adaptive demuxer (including
|
||||
streams that get added/removed at runtime).
|
||||
|
||||
When handling a `GST_EVENT_SELECT_STREAMS`, the adaptive demuxer will:
|
||||
|
||||
* mark the requested tracks as `selected` (and no-longer requested ones as not
|
||||
selected)
|
||||
* instruct the streams associated to no-longer selected tracks to stop
|
||||
* set the current output position on streams associated to newly selected
|
||||
tracks and instruct them to be started
|
||||
* return
|
||||
|
||||
The actual changes in output (because of a stream selection change) are done in
|
||||
the output thread
|
||||
|
||||
* If a track is no longer selected and there are no candidate replacement tracks
|
||||
of the same type, the associated output/pad is removed and the track is
|
||||
drained.
|
||||
|
||||
* If a track is selected and doesn't have a candidate replacement slot of the
|
||||
same type, a new output/pad is added for that track
|
||||
|
||||
* If a track is selected and has a candidate replacement slot, it will only be
|
||||
switched if the track it is replacing is empty *OR* when it has enough
|
||||
buffering so the switch can happen without re-triggering buffering.
|
||||
|
||||
## Periods
|
||||
|
||||
The number and type of `GstAdaptiveDemuxTrack` and `GstAdaptiveDemuxStream` can
|
||||
not change once the initial manifests are parsed.
|
||||
|
||||
In order to change that (for example in the case of a new DASH period), a new
|
||||
`GstAdaptiveDemuxPeriod` must be started.
|
||||
|
||||
All the tracks and streams that are created at any given time are associated to
|
||||
the current `input period`. The streams of the input period are the ones that
|
||||
are active (i.e. downloading), and by extension the tracks of that input period
|
||||
are the ones that are being filled (if selected).
|
||||
|
||||
That period *could* also be the `output period`. The (selected) tracks of that
|
||||
period are the ones that are used for output by the output thread.
|
||||
|
||||
But due to buffering, the input and output period *could* be different, the
|
||||
baseclass will automatically handle switch over.
|
||||
|
||||
The only requirement for subclasses is to ask the parent class to start a new
|
||||
period when needed and then create the new tracks and streams.
|
||||
|
||||
|
||||
## Responsibility split
|
||||
|
||||
The `GstAdaptiveDemux2` base class is in charge of:
|
||||
|
||||
* helper for all downloads.
|
||||
* helper for parsing (using `parsebin` and custom parsing functions) stream data.
|
||||
* provides *parsed* elementary content for each fragment (note: could be more
|
||||
than one output stream for a given fragment)
|
||||
* helper for providing `Tracks` that can be filled by subclasses.
|
||||
* dealing with stream selection and output, including notifying subclasses which
|
||||
of those *are* active or not
|
||||
* handling buffering and deciding when to request new data from associated stream
|
||||
|
||||
Subclasses are in charge of:
|
||||
|
||||
* specifying which `GstAdaptiveDemuxTrack` and `GstAdaptiveDemuxStream` they
|
||||
provide (based on the manifest) and their relationship.
|
||||
* when requested by the base class, specify which `GstAdaptiveDemuxFragment`
|
||||
should be downloaded next for a given (selected) stream.
|
||||
|
||||
|
|
@ -992,6 +992,426 @@
|
|||
"tracers": {},
|
||||
"url": "Unknown package origin"
|
||||
},
|
||||
"adaptivedemux2": {
|
||||
"description": "Adaptive Streaming 2 plugin",
|
||||
"elements": {
|
||||
"dashdemux2": {
|
||||
"author": "Edward Hervey <edward@centricular.com>\nJan Schmidt <jan@centricular.com>",
|
||||
"description": "Dynamic Adaptive Streaming over HTTP demuxer",
|
||||
"hierarchy": [
|
||||
"GstDashDemux2",
|
||||
"GstAdaptiveDemux2",
|
||||
"GstBin",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"interfaces": [
|
||||
"GstChildProxy"
|
||||
],
|
||||
"klass": "Codec/Demuxer/Adaptive",
|
||||
"long-name": "DASH Demuxer",
|
||||
"pad-templates": {
|
||||
"audio_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
},
|
||||
"sink": {
|
||||
"caps": "application/dash+xml:\n",
|
||||
"direction": "sink",
|
||||
"presence": "always"
|
||||
},
|
||||
"subtitle_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
},
|
||||
"video_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"max-bitrate": {
|
||||
"blurb": "Max of bitrate supported by target video decoder (0 = no maximum)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"max-video-framerate": {
|
||||
"blurb": "Max video framerate to select (0/1 = no maximum)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0/1",
|
||||
"max": "2147483647/1",
|
||||
"min": "0/1",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstFraction",
|
||||
"writable": true
|
||||
},
|
||||
"max-video-height": {
|
||||
"blurb": "Max video height to select (0 = no maximum)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"max-video-width": {
|
||||
"blurb": "Max video width to select (0 = no maximum)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"presentation-delay": {
|
||||
"blurb": "Default presentation delay (in seconds, milliseconds or fragments) (e.g. 12s, 2500ms, 3f)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "10s",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
}
|
||||
},
|
||||
"rank": "primary + 1"
|
||||
},
|
||||
"hlsdemux2": {
|
||||
"author": "Edward Hervey <edward@centricular.com>\nJan Schmidt <jan@centricular.com>",
|
||||
"description": "HTTP Live Streaming demuxer",
|
||||
"hierarchy": [
|
||||
"GstHLSDemux2",
|
||||
"GstAdaptiveDemux2",
|
||||
"GstBin",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"interfaces": [
|
||||
"GstChildProxy"
|
||||
],
|
||||
"klass": "Codec/Demuxer/Adaptive",
|
||||
"long-name": "HLS Demuxer",
|
||||
"pad-templates": {
|
||||
"audio_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
},
|
||||
"sink": {
|
||||
"caps": "application/x-hls:\n",
|
||||
"direction": "sink",
|
||||
"presence": "always"
|
||||
},
|
||||
"subtitle_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
},
|
||||
"video_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"start-bitrate": {
|
||||
"blurb": "Initial bitrate to use to choose first alternate (0 = automatic) (bits/s)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
}
|
||||
},
|
||||
"rank": "primary + 1"
|
||||
},
|
||||
"mssdemux2": {
|
||||
"author": "Thiago Santos <thiago.sousa.santos@collabora.com>",
|
||||
"description": "Parse and demultiplex a Smooth Streaming manifest into audio and video streams",
|
||||
"hierarchy": [
|
||||
"GstMssDemux2",
|
||||
"GstAdaptiveDemux2",
|
||||
"GstBin",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"interfaces": [
|
||||
"GstChildProxy"
|
||||
],
|
||||
"klass": "Codec/Demuxer/Adaptive",
|
||||
"long-name": "Smooth Streaming demuxer (v2)",
|
||||
"pad-templates": {
|
||||
"audio_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
},
|
||||
"sink": {
|
||||
"caps": "application/vnd.ms-sstr+xml:\n",
|
||||
"direction": "sink",
|
||||
"presence": "always"
|
||||
},
|
||||
"subtitle_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
},
|
||||
"video_%%02u": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes"
|
||||
}
|
||||
},
|
||||
"rank": "primary + 1"
|
||||
}
|
||||
},
|
||||
"filename": "gstadaptivedemux2",
|
||||
"license": "LGPL",
|
||||
"other-types": {
|
||||
"GstAdaptiveDemux2": {
|
||||
"hierarchy": [
|
||||
"GstAdaptiveDemux2",
|
||||
"GstBin",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"interfaces": [
|
||||
"GstChildProxy"
|
||||
],
|
||||
"kind": "object",
|
||||
"properties": {
|
||||
"bandwidth-target-ratio": {
|
||||
"blurb": "Limit of the available bitrate to use when switching to alternates",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0.8",
|
||||
"max": "1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gfloat",
|
||||
"writable": true
|
||||
},
|
||||
"connection-bitrate": {
|
||||
"blurb": "Network connection speed to use (0 = automatic) (bits/s)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"connection-speed": {
|
||||
"blurb": "Network connection speed to use in kbps (0 = calculate from downloaded fragments)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "4294967",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"current-bandwidth": {
|
||||
"blurb": "Report of current download bandwidth (based on arriving data) (bits/s)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": false
|
||||
},
|
||||
"current-level-time-audio": {
|
||||
"blurb": "Currently buffered level of audio track(s) (ns)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "playing",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": false
|
||||
},
|
||||
"current-level-time-video": {
|
||||
"blurb": "Currently buffered level of video track(s) (ns)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "playing",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": false
|
||||
},
|
||||
"high-watermark-fragments": {
|
||||
"blurb": "High watermark for parsed data above which downloads are paused (in fragments, 0=disable)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "3.40282e+38",
|
||||
"min": "0",
|
||||
"mutable": "playing",
|
||||
"readable": true,
|
||||
"type": "gdouble",
|
||||
"writable": true
|
||||
},
|
||||
"high-watermark-time": {
|
||||
"blurb": "High watermark for parsed data above which downloads are paused (in ns, 0=disable)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "30000000000",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "playing",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": true
|
||||
},
|
||||
"low-watermark-fragments": {
|
||||
"blurb": "Low watermark for parsed data below which downloads are resumed (in fragments, 0=disable)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "3.40282e+38",
|
||||
"min": "0",
|
||||
"mutable": "playing",
|
||||
"readable": true,
|
||||
"type": "gdouble",
|
||||
"writable": true
|
||||
},
|
||||
"low-watermark-time": {
|
||||
"blurb": "Low watermark for parsed data below which downloads are resumed (in ns, 0=disable)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "3000000000",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "playing",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": true
|
||||
},
|
||||
"max-bitrate": {
|
||||
"blurb": "Maximum bitrate to use when switching to alternates (bits/s)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"max-buffering-time": {
|
||||
"blurb": "Upper limit on the high watermark for parsed data, above which downloads are paused (in ns, 0=disable)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "30000000000",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "playing",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": true
|
||||
},
|
||||
"min-bitrate": {
|
||||
"blurb": "Minimum bitrate to use when switching to alternates (bits/s)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"package": "GStreamer Good Plug-ins",
|
||||
"source": "gst-plugins-good",
|
||||
"tracers": {},
|
||||
"url": "Unknown package origin"
|
||||
},
|
||||
"alaw": {
|
||||
"description": "ALaw audio conversion routines",
|
||||
"elements": {
|
||||
|
|
|
@ -95,6 +95,7 @@ foreach plugin_name: list_plugin_res.stdout().split(':')
|
|||
gst_c_sources: [
|
||||
'../sys/*/*.[cmh]',
|
||||
'../ext/*/*.[ch]',
|
||||
'../ext/*/*/*.[ch]',
|
||||
'../gst/*/*.[ch]',
|
||||
],
|
||||
gst_c_source_filters: excludes,
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef __GST_DASH_DEBUG_H__
|
||||
#define __GST_DASH_DEBUG_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_dash_demux2_debug);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_DASH_DEBUG_H__ */
|
||||
|
3969
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstdashdemux.c
Normal file
3969
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstdashdemux.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* DASH demux plugin for GStreamer
|
||||
*
|
||||
* gstdashdemux.h
|
||||
*
|
||||
* Copyright (C) 2012 Orange
|
||||
* Authors:
|
||||
* David Corvoysier <david.corvoysier@orange.com>
|
||||
* Hamid Zakari <hamid.zakari@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2013 Smart TV Alliance
|
||||
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
|
||||
*
|
||||
* Copyright (C) 2021 Centricular Ltd
|
||||
* Author: Edward Hervey <edward@centricular.com>
|
||||
* Author: Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_DASH_DEMUX_H__
|
||||
#define __GST_DASH_DEMUX_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <gst/base/gstdataqueue.h>
|
||||
|
||||
#include "gstisoff.h"
|
||||
#include "gstadaptivedemux.h"
|
||||
|
||||
#include "gstmpdclient.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GST_TYPE_DASH_DEMUX2 \
|
||||
(gst_dash_demux2_get_type())
|
||||
#define GST_DASH_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DASH_DEMUX2,GstDashDemux2))
|
||||
#define GST_DASH_DEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DASH_DEMUX2,GstDashDemux2Class))
|
||||
#define GST_IS_DASH_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DASH_DEMUX2))
|
||||
#define GST_IS_DASH_DEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DASH_DEMUX2))
|
||||
#define GST_DASH_DEMUX_CAST(obj) \
|
||||
((GstDashDemux2 *)obj)
|
||||
|
||||
#define GST_TYPE_DASH_DEMUX_STREAM \
|
||||
(gst_dash_demux_stream_get_type())
|
||||
#define GST_DASH_DEMUX_STREAM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DASH_DEMUX_STREAM,GstDashDemux2Stream))
|
||||
#define GST_DASH_DEMUX_STREAM_CAST(obj) ((GstDashDemux2Stream *)obj)
|
||||
|
||||
typedef struct _GstDashDemux2Stream GstDashDemux2Stream;
|
||||
typedef GstAdaptiveDemux2StreamClass GstDashDemux2StreamClass;
|
||||
|
||||
typedef struct _GstDashDemux2ClockDrift GstDashDemux2ClockDrift;
|
||||
typedef struct _GstDashDemux2 GstDashDemux2;
|
||||
typedef struct _GstDashDemux2Class GstDashDemux2Class;
|
||||
|
||||
struct _GstDashDemux2Stream
|
||||
{
|
||||
GstAdaptiveDemux2Stream parent;
|
||||
|
||||
gint index;
|
||||
GstActiveStream *active_stream;
|
||||
|
||||
/* Track provided by this stream */
|
||||
GstAdaptiveDemuxTrack *track;
|
||||
|
||||
GstMediaFragmentInfo current_fragment;
|
||||
|
||||
/* index parsing */
|
||||
GstSidxParser sidx_parser;
|
||||
GstClockTime sidx_position;
|
||||
gint64 sidx_base_offset;
|
||||
gboolean allow_sidx;
|
||||
GstClockTime pending_seek_ts;
|
||||
|
||||
GstAdapter *adapter;
|
||||
/* current offset of the first byte in the adapter / last byte we pushed or
|
||||
* dropped*/
|
||||
guint64 current_offset;
|
||||
/* index = 1, header = 2, data = 3 */
|
||||
guint current_index_header_or_data;
|
||||
|
||||
/* ISOBMFF box parsing */
|
||||
gboolean is_isobmff;
|
||||
struct {
|
||||
/* index = 1, header = 2, data = 3 */
|
||||
guint32 current_fourcc;
|
||||
guint64 current_start_offset;
|
||||
guint64 current_size;
|
||||
} isobmff_parser;
|
||||
|
||||
GstMoofBox *moof;
|
||||
guint64 moof_offset, moof_size;
|
||||
GArray *moof_sync_samples;
|
||||
guint current_sync_sample;
|
||||
|
||||
guint64 moof_average_size;
|
||||
guint64 keyframe_average_size;
|
||||
guint64 keyframe_average_distance;
|
||||
gboolean first_sync_sample_after_moof, first_sync_sample_always_after_moof;
|
||||
|
||||
/* Internal position value, at the keyframe/entry level */
|
||||
GstClockTime actual_position;
|
||||
/* Timestamp of the beginning of the current fragment */
|
||||
GstClockTime current_fragment_timestamp;
|
||||
GstClockTime current_fragment_duration;
|
||||
GstClockTime current_fragment_keyframe_distance;
|
||||
|
||||
/* Average keyframe download time (only in trickmode-key-units) */
|
||||
GstClockTime average_download_time;
|
||||
/* Cached target time (only in trickmode-key-units) */
|
||||
GstClockTime target_time;
|
||||
/* Average skip-ahead time (only in trickmode-key-units) */
|
||||
GstClockTime average_skip_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* GstDashDemux2:
|
||||
*
|
||||
* Opaque #GstDashDemux2 data structure.
|
||||
*/
|
||||
struct _GstDashDemux2
|
||||
{
|
||||
GstAdaptiveDemux parent;
|
||||
|
||||
GSList *next_periods;
|
||||
|
||||
GstMPDClient2 *client; /* MPD client */
|
||||
GMutex client_lock;
|
||||
|
||||
GstDashDemux2ClockDrift *clock_drift;
|
||||
|
||||
gboolean end_of_period;
|
||||
gboolean end_of_manifest;
|
||||
|
||||
/* Properties */
|
||||
gint max_video_width, max_video_height;
|
||||
gint max_video_framerate_n, max_video_framerate_d;
|
||||
gchar* default_presentation_delay; /* presentation time delay if MPD@suggestedPresentationDelay is not present */
|
||||
|
||||
gboolean allow_trickmode_key_units;
|
||||
};
|
||||
|
||||
struct _GstDashDemux2Class
|
||||
{
|
||||
GstAdaptiveDemuxClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_dash_demux2_get_type (void);
|
||||
GType gst_dash_demux_stream_get_type (void);
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (dashdemux2);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_DASH_DEMUX_H__ */
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdadaptationsetnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDAdaptationSetNode2, gst_mpd_adaptation_set_node,
|
||||
GST_TYPE_MPD_REPRESENTATION_BASE_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_ADAPTATION_SET_0,
|
||||
PROP_MPD_ADAPTATION_SET_ID,
|
||||
PROP_MPD_ADAPTATION_SET_CONTENT_TYPE,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_adaptation_set_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDAdaptationSetNode *self = GST_MPD_ADAPTATION_SET_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_ADAPTATION_SET_ID:
|
||||
self->id = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MPD_ADAPTATION_SET_CONTENT_TYPE:
|
||||
g_free (self->contentType);
|
||||
self->contentType = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_adaptation_set_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDAdaptationSetNode *self = GST_MPD_ADAPTATION_SET_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_ADAPTATION_SET_ID:
|
||||
g_value_set_int (value, self->id);
|
||||
break;
|
||||
case PROP_MPD_ADAPTATION_SET_CONTENT_TYPE:
|
||||
g_value_set_string (value, self->contentType);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_adaptation_set_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDAdaptationSetNode *self = GST_MPD_ADAPTATION_SET_NODE (object);
|
||||
|
||||
if (self->lang)
|
||||
xmlFree (self->lang);
|
||||
if (self->contentType)
|
||||
xmlFree (self->contentType);
|
||||
g_slice_free (GstXMLRatio, self->par);
|
||||
g_slice_free (GstXMLConditionalUintType, self->segmentAlignment);
|
||||
g_slice_free (GstXMLConditionalUintType, self->subsegmentAlignment);
|
||||
g_list_free_full (self->Accessibility,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->Role,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->Rating,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->Viewpoint,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
gst_mpd_segment_base_node_free (self->SegmentBase);
|
||||
gst_mpd_segment_list_node_free (self->SegmentList);
|
||||
gst_mpd_segment_template_node_free (self->SegmentTemplate);
|
||||
g_list_free_full (self->BaseURLs, (GDestroyNotify) gst_mpd_baseurl_node_free);
|
||||
g_list_free_full (self->Representations,
|
||||
(GDestroyNotify) gst_mpd_representation_node_free);
|
||||
g_list_free_full (self->ContentComponents,
|
||||
(GDestroyNotify) gst_mpd_content_component_node_free);
|
||||
if (self->xlink_href)
|
||||
xmlFree (self->xlink_href);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_adaptation_set_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_adaptation_set_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr adaptation_set_xml_node = NULL;
|
||||
GstMPDAdaptationSetNode *self = GST_MPD_ADAPTATION_SET_NODE (node);
|
||||
|
||||
adaptation_set_xml_node = xmlNewNode (NULL, (xmlChar *) "AdaptationSet");
|
||||
|
||||
if (self->id)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "id", self->id);
|
||||
if (self->group)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "group",
|
||||
self->group);
|
||||
|
||||
if (self->lang)
|
||||
gst_xml_helper_set_prop_string (adaptation_set_xml_node, "lang",
|
||||
self->lang);
|
||||
|
||||
if (self->contentType)
|
||||
gst_xml_helper_set_prop_string (adaptation_set_xml_node, "contentType",
|
||||
self->contentType);
|
||||
|
||||
if (self->minBandwidth)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "minBandwidth",
|
||||
self->minBandwidth);
|
||||
if (self->maxBandwidth)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "maxBandwidth",
|
||||
self->maxBandwidth);
|
||||
if (self->minWidth)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "minWidth",
|
||||
self->minWidth);
|
||||
if (self->maxWidth)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "maxWidth",
|
||||
self->maxWidth);
|
||||
if (self->minHeight)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "minHeight",
|
||||
self->minHeight);
|
||||
if (self->maxHeight)
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node, "maxHeight",
|
||||
self->maxHeight);
|
||||
|
||||
if (self->par)
|
||||
gst_xml_helper_set_prop_ratio (adaptation_set_xml_node, "par", self->par);
|
||||
|
||||
gst_xml_helper_set_prop_cond_uint (adaptation_set_xml_node,
|
||||
"segmentAlignment", self->segmentAlignment);
|
||||
gst_xml_helper_set_prop_cond_uint (adaptation_set_xml_node,
|
||||
"subsegmentAlignment", self->subsegmentAlignment);
|
||||
gst_xml_helper_set_prop_uint (adaptation_set_xml_node,
|
||||
"subsegmentStartsWithSAP", self->subsegmentStartsWithSAP);
|
||||
gst_xml_helper_set_prop_boolean (adaptation_set_xml_node,
|
||||
"bitstreamSwitching", self->bitstreamSwitching);
|
||||
|
||||
g_list_foreach (self->Accessibility, gst_mpd_node_get_list_item,
|
||||
adaptation_set_xml_node);
|
||||
g_list_foreach (self->Role, gst_mpd_node_get_list_item,
|
||||
adaptation_set_xml_node);
|
||||
g_list_foreach (self->Rating, gst_mpd_node_get_list_item,
|
||||
adaptation_set_xml_node);
|
||||
g_list_foreach (self->Viewpoint, gst_mpd_node_get_list_item,
|
||||
adaptation_set_xml_node);
|
||||
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->SegmentBase),
|
||||
adaptation_set_xml_node);
|
||||
gst_mpd_mult_segment_base_node_add_child_node (GST_MPD_NODE
|
||||
(self->SegmentList), adaptation_set_xml_node);
|
||||
gst_mpd_mult_segment_base_node_add_child_node (GST_MPD_NODE
|
||||
(self->SegmentTemplate), adaptation_set_xml_node);
|
||||
|
||||
g_list_foreach (self->BaseURLs, gst_mpd_node_get_list_item,
|
||||
adaptation_set_xml_node);
|
||||
g_list_foreach (self->Representations,
|
||||
gst_mpd_representation_base_node_get_list_item, adaptation_set_xml_node);
|
||||
g_list_foreach (self->ContentComponents, gst_mpd_node_get_list_item,
|
||||
adaptation_set_xml_node);
|
||||
|
||||
if (self->xlink_href)
|
||||
gst_xml_helper_set_prop_string (adaptation_set_xml_node, "xlink_href",
|
||||
self->xlink_href);
|
||||
if (self->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD)
|
||||
gst_xml_helper_set_prop_string (adaptation_set_xml_node, "actuate",
|
||||
(gchar *) GST_MPD_XLINK_ACTUATE_ON_LOAD_STR);
|
||||
return adaptation_set_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_adaptation_set_node_class_init (GstMPDAdaptationSetNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_adaptation_set_node_finalize;
|
||||
object_class->set_property = gst_mpd_adaptation_set_node_set_property;
|
||||
object_class->get_property = gst_mpd_adaptation_set_node_get_property;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_adaptation_set_get_xml_node;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_MPD_ADAPTATION_SET_ID,
|
||||
g_param_spec_int ("id", "id",
|
||||
"adaptation set id", 0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ADAPTATION_SET_CONTENT_TYPE, g_param_spec_string ("content-type",
|
||||
"content type", "content type of the adaptation set", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_adaptation_set_node_init (GstMPDAdaptationSetNode * self)
|
||||
{
|
||||
self->id = 0;
|
||||
self->group = 0;
|
||||
self->lang = NULL; /* LangVectorType RFC 5646 */
|
||||
self->contentType = NULL;
|
||||
self->par = 0;
|
||||
self->minBandwidth = 0;
|
||||
self->maxBandwidth = 0;
|
||||
self->minWidth = 0;
|
||||
self->maxWidth = 0;
|
||||
self->minHeight = 0;
|
||||
self->maxHeight = 0;
|
||||
self->segmentAlignment = NULL;
|
||||
self->subsegmentAlignment = NULL;
|
||||
self->subsegmentStartsWithSAP = GST_SAP_TYPE_0;
|
||||
self->bitstreamSwitching = FALSE;
|
||||
/* list of Accessibility DescriptorType nodes */
|
||||
self->Accessibility = NULL;
|
||||
/* list of Role DescriptorType nodes */
|
||||
self->Role = NULL;
|
||||
/* list of Rating DescriptorType nodes */
|
||||
self->Rating = NULL;
|
||||
/* list of Viewpoint DescriptorType nodes */
|
||||
self->Viewpoint = NULL;
|
||||
/* SegmentBase node */
|
||||
self->SegmentBase = NULL;
|
||||
/* SegmentList node */
|
||||
self->SegmentList = NULL;
|
||||
/* SegmentTemplate node */
|
||||
self->SegmentTemplate = NULL;
|
||||
/* list of BaseURL nodes */
|
||||
self->BaseURLs = NULL;
|
||||
/* list of Representation nodes */
|
||||
self->Representations = NULL;
|
||||
/* list of ContentComponent nodes */
|
||||
self->ContentComponents = NULL;
|
||||
|
||||
self->xlink_href = NULL;
|
||||
self->actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST;
|
||||
}
|
||||
|
||||
GstMPDAdaptationSetNode *
|
||||
gst_mpd_adaptation_set_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_ADAPTATION_SET_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_adaptation_set_node_free (GstMPDAdaptationSetNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDADAPTATIONSETNODE_H__
|
||||
#define __GSTMPDADAPTATIONSETNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstmpdrepresentationbasenode.h"
|
||||
#include "gstmpdsegmentlistnode.h"
|
||||
#include "gstmpdsegmenttemplatenode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_ADAPTATION_SET_NODE gst_mpd_adaptation_set_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDAdaptationSetNode2, gst_mpd_adaptation_set_node, GST, MPD_ADAPTATION_SET_NODE, GstMPDRepresentationBaseNode)
|
||||
|
||||
typedef GstMPDAdaptationSetNode2 GstMPDAdaptationSetNode;
|
||||
typedef GstMPDAdaptationSetNode2Class GstMPDAdaptationSetNodeClass;
|
||||
|
||||
struct _GstMPDAdaptationSetNode2
|
||||
{
|
||||
GstMPDRepresentationBaseNode parent_instance;
|
||||
guint id;
|
||||
guint group;
|
||||
gchar *lang; /* LangVectorType RFC 5646 */
|
||||
gchar *contentType;
|
||||
GstXMLRatio *par;
|
||||
guint minBandwidth;
|
||||
guint maxBandwidth;
|
||||
guint minWidth;
|
||||
guint maxWidth;
|
||||
guint minHeight;
|
||||
guint maxHeight;
|
||||
GstXMLConditionalUintType *segmentAlignment;
|
||||
GstXMLConditionalUintType *subsegmentAlignment;
|
||||
GstMPDSAPType subsegmentStartsWithSAP;
|
||||
gboolean bitstreamSwitching;
|
||||
/* list of Accessibility DescriptorType nodes */
|
||||
GList *Accessibility;
|
||||
/* list of Role DescriptorType nodes */
|
||||
GList *Role;
|
||||
/* list of Rating DescriptorType nodes */
|
||||
GList *Rating;
|
||||
/* list of Viewpoint DescriptorType nodes */
|
||||
GList *Viewpoint;
|
||||
/* SegmentBase node */
|
||||
GstMPDSegmentBaseNode *SegmentBase;
|
||||
/* SegmentList node */
|
||||
GstMPDSegmentListNode *SegmentList;
|
||||
/* SegmentTemplate node */
|
||||
GstMPDSegmentTemplateNode *SegmentTemplate;
|
||||
/* list of BaseURL nodes */
|
||||
GList *BaseURLs;
|
||||
/* list of Representation nodes */
|
||||
GList *Representations;
|
||||
/* list of ContentComponent nodes */
|
||||
GList *ContentComponents;
|
||||
|
||||
gchar *xlink_href;
|
||||
GstMPDXLinkActuate actuate;
|
||||
};
|
||||
|
||||
GstMPDAdaptationSetNode * gst_mpd_adaptation_set_node_new (void);
|
||||
void gst_mpd_adaptation_set_node_free (GstMPDAdaptationSetNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDADAPTATIONSETNODE_H__ */
|
|
@ -0,0 +1,166 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdbaseurlnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDBaseURLNode2, gst_mpd_baseurl_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_BASEURL_0,
|
||||
PROP_MPD_BASEURL_URL,
|
||||
PROP_MPD_BASEURL_SERVICE_LOCATION,
|
||||
PROP_MPD_BASEURL_BYTE_RANGE,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_baseurl_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDBaseURLNode *self = GST_MPD_BASEURL_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_BASEURL_URL:
|
||||
g_free (self->baseURL);
|
||||
self->baseURL = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_BASEURL_SERVICE_LOCATION:
|
||||
g_free (self->serviceLocation);
|
||||
self->serviceLocation = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_BASEURL_BYTE_RANGE:
|
||||
g_free (self->byteRange);
|
||||
self->byteRange = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_baseurl_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDBaseURLNode *self = GST_MPD_BASEURL_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_BASEURL_URL:
|
||||
g_value_set_string (value, self->baseURL);
|
||||
break;
|
||||
case PROP_MPD_BASEURL_SERVICE_LOCATION:
|
||||
g_value_set_string (value, self->serviceLocation);
|
||||
break;
|
||||
case PROP_MPD_BASEURL_BYTE_RANGE:
|
||||
g_value_set_string (value, self->byteRange);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_baseurl_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDBaseURLNode *self = GST_MPD_BASEURL_NODE (object);
|
||||
|
||||
g_free (self->baseURL);
|
||||
g_free (self->serviceLocation);
|
||||
g_free (self->byteRange);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_baseurl_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_baseurl_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr baseurl_xml_node = NULL;
|
||||
GstMPDBaseURLNode *self = GST_MPD_BASEURL_NODE (node);
|
||||
|
||||
baseurl_xml_node = xmlNewNode (NULL, (xmlChar *) "BaseURL");
|
||||
|
||||
if (self->serviceLocation)
|
||||
gst_xml_helper_set_prop_string (baseurl_xml_node, "serviceLocation",
|
||||
self->serviceLocation);
|
||||
|
||||
if (self->byteRange)
|
||||
gst_xml_helper_set_prop_string (baseurl_xml_node, "byteRange",
|
||||
self->byteRange);
|
||||
|
||||
if (self->baseURL)
|
||||
gst_xml_helper_set_content (baseurl_xml_node, self->baseURL);
|
||||
|
||||
return baseurl_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_baseurl_node_class_init (GstMPDBaseURLNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_baseurl_node_finalize;
|
||||
object_class->set_property = gst_mpd_baseurl_node_set_property;
|
||||
object_class->get_property = gst_mpd_baseurl_node_get_property;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_baseurl_get_xml_node;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_MPD_BASEURL_URL,
|
||||
g_param_spec_string ("url", "base url",
|
||||
"url of the base url", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_BASEURL_SERVICE_LOCATION,
|
||||
g_param_spec_string ("service-location", "service location",
|
||||
"service location", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_MPD_BASEURL_BYTE_RANGE,
|
||||
g_param_spec_string ("byte-range", "byte range", "byte range", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_baseurl_node_init (GstMPDBaseURLNode * self)
|
||||
{
|
||||
self->baseURL = NULL;
|
||||
self->serviceLocation = NULL;
|
||||
self->byteRange = NULL;
|
||||
}
|
||||
|
||||
GstMPDBaseURLNode *
|
||||
gst_mpd_baseurl_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_BASEURL_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_baseurl_node_free (GstMPDBaseURLNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDBASEURLNODE_H__
|
||||
#define __GSTMPDBASEURLNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_BASEURL_NODE gst_mpd_baseurl_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDBaseURLNode2, gst_mpd_baseurl_node, GST, MPD_BASEURL_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDBaseURLNode2 GstMPDBaseURLNode;
|
||||
typedef GstMPDBaseURLNode2Class GstMPDBaseURLNodeClass;
|
||||
|
||||
struct _GstMPDBaseURLNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *baseURL;
|
||||
gchar *serviceLocation;
|
||||
gchar *byteRange;
|
||||
/* TODO add missing fields such as weight etc.*/
|
||||
};
|
||||
|
||||
GstMPDBaseURLNode * gst_mpd_baseurl_node_new (void);
|
||||
void gst_mpd_baseurl_node_free (GstMPDBaseURLNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDBASEURLNODE_H__ */
|
3449
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstmpdclient.c
Normal file
3449
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstmpdclient.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,191 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GST_MPDCLIENT_H__
|
||||
#define __GST_MPDCLIENT_H__
|
||||
|
||||
#include "gstmpdparser.h"
|
||||
#include "downloadhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_CLIENT gst_mpd_client2_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDClient2, gst_mpd_client2, GST, MPD_CLIENT, GstObject)
|
||||
|
||||
struct _GstMPDClient2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
GstMPDRootNode *mpd_root_node; /* mpd root node */
|
||||
|
||||
GList *periods; /* list of GstStreamPeriod */
|
||||
guint period_idx; /* index of current Period */
|
||||
|
||||
GList *active_streams; /* list of GstActiveStream */
|
||||
|
||||
guint update_failed_count;
|
||||
gchar *mpd_uri; /* manifest file URI */
|
||||
gchar *mpd_base_uri; /* base URI for resolving relative URIs.
|
||||
* this will be different for redirects */
|
||||
|
||||
/* profiles */
|
||||
gboolean profile_isoff_ondemand;
|
||||
|
||||
DownloadHelper *download_helper;
|
||||
};
|
||||
|
||||
/* Basic initialization/deinitialization functions */
|
||||
|
||||
GstMPDClient2 *gst_mpd_client2_new (void);
|
||||
GstMPDClient2 *gst_mpd_client2_new_static (void);
|
||||
|
||||
void gst_mpd_client2_active_streams_free (GstMPDClient2 * client);
|
||||
void gst_mpd_client2_free (GstMPDClient2 * client);
|
||||
|
||||
/* main mpd parsing methods from xml data */
|
||||
gboolean gst_mpd_client2_parse (GstMPDClient2 * client, const gchar * data, gint size);
|
||||
|
||||
/* xml generator */
|
||||
gboolean gst_mpd_client2_get_xml_content (GstMPDClient2 * client, gchar ** data, gint * size);
|
||||
|
||||
void gst_mpd_client2_set_download_helper (GstMPDClient2 * client, DownloadHelper *dh);
|
||||
void gst_mpd_client2_check_profiles (GstMPDClient2 * client);
|
||||
void gst_mpd_client2_fetch_on_load_external_resources (GstMPDClient2 * client);
|
||||
|
||||
/* Streaming management */
|
||||
gboolean gst_mpd_client2_setup_media_presentation (GstMPDClient2 *client, GstClockTime time, gint period_index, const gchar *period_id);
|
||||
gboolean gst_mpd_client2_setup_streaming (GstMPDClient2 * client, GstMPDAdaptationSetNode * adapt_set);
|
||||
gboolean gst_mpd_client2_setup_representation (GstMPDClient2 *client, GstActiveStream *stream, GstMPDRepresentationNode *representation);
|
||||
|
||||
GstClockTime gst_mpd_client2_get_next_fragment_duration (GstMPDClient2 * client, GstActiveStream * stream);
|
||||
GstClockTime gst_mpd_client2_get_media_presentation_duration (GstMPDClient2 *client);
|
||||
GstClockTime gst_mpd_client2_get_maximum_segment_duration (GstMPDClient2 * client);
|
||||
gboolean gst_mpd_client2_get_last_fragment_timestamp_end (GstMPDClient2 * client, guint stream_idx, GstClockTime * ts);
|
||||
gboolean gst_mpd_client2_get_next_fragment_timestamp (GstMPDClient2 * client, guint stream_idx, GstClockTime * ts);
|
||||
gboolean gst_mpd_client2_get_next_fragment (GstMPDClient2 *client, guint indexStream, GstMediaFragmentInfo * fragment);
|
||||
gboolean gst_mpd_client2_get_next_header (GstMPDClient2 *client, gchar **uri, guint stream_idx, gint64 * range_start, gint64 * range_end);
|
||||
gboolean gst_mpd_client2_get_next_header_index (GstMPDClient2 *client, gchar **uri, guint stream_idx, gint64 * range_start, gint64 * range_end);
|
||||
gboolean gst_mpd_client2_is_live (GstMPDClient2 * client);
|
||||
gboolean gst_mpd_client2_stream_seek (GstMPDClient2 * client, GstActiveStream * stream, gboolean forward, GstSeekFlags flags, GstClockTime ts, GstClockTime * final_ts);
|
||||
gboolean gst_mpd_client2_seek_to_time (GstMPDClient2 * client, GDateTime * time);
|
||||
GstClockTime gst_mpd_client2_get_stream_presentation_offset (GstMPDClient2 *client, guint stream_idx);
|
||||
gchar** gst_mpd_client2_get_utc_timing_sources (GstMPDClient2 *client, guint methods, GstMPDUTCTimingType *selected_method);
|
||||
GstClockTime gst_mpd_client2_get_period_start_time (GstMPDClient2 *client);
|
||||
|
||||
GstCaps *gst_mpd_client2_get_codec_caps (GstActiveStream *stream);
|
||||
|
||||
/* Period selection */
|
||||
guint gst_mpd_client2_get_period_index_at_time (GstMPDClient2 * client, GstDateTime * time);
|
||||
gboolean gst_mpd_client2_set_period_index (GstMPDClient2 *client, guint period_idx);
|
||||
gboolean gst_mpd_client2_set_period_id (GstMPDClient2 *client, const gchar * period_id);
|
||||
guint gst_mpd_client2_get_period_index (GstMPDClient2 *client);
|
||||
const gchar *gst_mpd_client2_get_period_id (GstMPDClient2 *client);
|
||||
gboolean gst_mpd_client2_has_next_period (GstMPDClient2 *client);
|
||||
gboolean gst_mpd_client2_has_previous_period (GstMPDClient2 * client);
|
||||
|
||||
/* Representation selection */
|
||||
gint gst_mpd_client2_get_rep_idx_with_max_bandwidth (GList *Representations, gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint max_video_framerate_n, gint max_video_framerate_d);
|
||||
gint gst_mpd_client2_get_rep_idx_with_min_bandwidth (GList * Representations);
|
||||
|
||||
GstDateTime *
|
||||
gst_mpd_client2_get_availability_start_time (GstMPDClient2 * client);
|
||||
|
||||
/* URL management */
|
||||
const gchar *gst_mpd_client2_get_baseURL (GstMPDClient2 *client, guint indexStream);
|
||||
gchar *gst_mpd_client2_parse_baseURL (GstMPDClient2 * client, GstActiveStream * stream, gchar ** query);
|
||||
|
||||
/* Active stream */
|
||||
guint gst_mpd_client2_get_nb_active_stream (GstMPDClient2 *client);
|
||||
GstActiveStream *gst_mpd_client2_get_active_stream_by_index (GstMPDClient2 *client, guint stream_idx);
|
||||
gboolean gst_mpd_client2_active_stream_contains_subtitles (GstActiveStream * stream);
|
||||
|
||||
/* AdaptationSet */
|
||||
guint gst_mpd_client2_get_nb_adaptationSet (GstMPDClient2 *client);
|
||||
GList * gst_mpd_client2_get_adaptation_sets (GstMPDClient2 * client);
|
||||
|
||||
/* Segment */
|
||||
gboolean gst_mpd_client2_has_next_segment (GstMPDClient2 * client, GstActiveStream * stream, gboolean forward);
|
||||
GstFlowReturn gst_mpd_client2_advance_segment (GstMPDClient2 * client, GstActiveStream * stream, gboolean forward);
|
||||
void gst_mpd_client2_seek_to_first_segment (GstMPDClient2 * client);
|
||||
GstDateTime *gst_mpd_client2_get_next_segment_availability_start_time (GstMPDClient2 * client, GstActiveStream * stream);
|
||||
|
||||
/* Get audio/video stream parameters (caps, width, height, rate, number of channels) */
|
||||
GstCaps * gst_mpd_client2_get_stream_caps (GstActiveStream * stream);
|
||||
gboolean gst_mpd_client2_get_bitstream_switching_flag (GstActiveStream * stream);
|
||||
guint gst_mpd_client2_get_video_stream_width (GstActiveStream * stream);
|
||||
guint gst_mpd_client2_get_video_stream_height (GstActiveStream * stream);
|
||||
gboolean gst_mpd_client2_get_video_stream_framerate (GstActiveStream * stream, gint * fps_num, gint * fps_den);
|
||||
guint gst_mpd_client2_get_audio_stream_rate (GstActiveStream * stream);
|
||||
guint gst_mpd_client2_get_audio_stream_num_channels (GstActiveStream * stream);
|
||||
|
||||
/* Support multi language */
|
||||
guint gst_mpd_client2_get_list_and_nb_of_audio_language (GstMPDClient2 *client, GList **lang);
|
||||
|
||||
GstClockTimeDiff gst_mpd_client2_calculate_time_difference (const GstDateTime * t1, const GstDateTime * t2);
|
||||
GstDateTime *gst_mpd_client2_add_time_difference (GstDateTime * t1, GstClockTimeDiff diff);
|
||||
gint64 gst_mpd_client2_parse_default_presentation_delay(GstMPDClient2 * client, const gchar * default_presentation_delay);
|
||||
|
||||
/* profiles */
|
||||
gboolean gst_mpd_client2_has_isoff_ondemand_profile (GstMPDClient2 *client);
|
||||
|
||||
/* add/set node methods */
|
||||
gboolean gst_mpd_client2_set_root_node (GstMPDClient2 * client,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
gchar * gst_mpd_client2_set_period_node (GstMPDClient2 * client,
|
||||
gchar * period_id,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
guint gst_mpd_client2_set_adaptation_set_node (GstMPDClient2 * client,
|
||||
gchar * period_id,
|
||||
guint adap_set_id,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
gchar * gst_mpd_client2_set_representation_node (GstMPDClient2 * client,
|
||||
gchar * period_id,
|
||||
guint adap_set_id,
|
||||
gchar * rep_id,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
gboolean gst_mpd_client2_set_segment_list (GstMPDClient2 * client,
|
||||
gchar * period_id,
|
||||
guint adap_set_id,
|
||||
gchar * rep_id,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
gboolean gst_mpd_client2_set_segment_template (GstMPDClient2 * client,
|
||||
gchar * period_id,
|
||||
guint adap_set_id,
|
||||
gchar * rep_id,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
|
||||
/* create a new node */
|
||||
gboolean gst_mpd_client2_add_baseurl_node (GstMPDClient2 * client,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
gboolean gst_mpd_client2_add_segment_url (GstMPDClient2 * client,
|
||||
gchar * period_id,
|
||||
guint adap_set_id,
|
||||
gchar * rep_id,
|
||||
const gchar * property_name,
|
||||
...);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MPDCLIENT_H__ */
|
|
@ -0,0 +1,120 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdcontentcomponentnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDContentComponentNode2, gst_mpd_content_component_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_content_component_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDContentComponentNode *self = GST_MPD_CONTENT_COMPONENT_NODE (object);
|
||||
|
||||
if (self->lang)
|
||||
xmlFree (self->lang);
|
||||
if (self->contentType)
|
||||
xmlFree (self->contentType);
|
||||
g_slice_free (GstXMLRatio, self->par);
|
||||
g_list_free_full (self->Accessibility,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->Role,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->Rating,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->Viewpoint,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_content_component_node_parent_class)->finalize
|
||||
(object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_content_component_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr content_component_xml_node = NULL;
|
||||
GstMPDContentComponentNode *self = GST_MPD_CONTENT_COMPONENT_NODE (node);
|
||||
content_component_xml_node =
|
||||
xmlNewNode (NULL, (xmlChar *) "ContentComponent");
|
||||
|
||||
gst_xml_helper_set_prop_uint (content_component_xml_node, "id", self->id);
|
||||
gst_xml_helper_set_prop_string (content_component_xml_node, "lang",
|
||||
self->lang);
|
||||
gst_xml_helper_set_prop_string (content_component_xml_node, "contentType",
|
||||
self->contentType);
|
||||
gst_xml_helper_set_prop_ratio (content_component_xml_node, "par", self->par);
|
||||
|
||||
g_list_foreach (self->Accessibility, gst_mpd_node_get_list_item,
|
||||
content_component_xml_node);
|
||||
g_list_foreach (self->Role, gst_mpd_node_get_list_item,
|
||||
content_component_xml_node);
|
||||
g_list_foreach (self->Rating, gst_mpd_node_get_list_item,
|
||||
content_component_xml_node);
|
||||
g_list_foreach (self->Viewpoint, gst_mpd_node_get_list_item,
|
||||
content_component_xml_node);
|
||||
|
||||
return content_component_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_content_component_node_class_init (GstMPDContentComponentNodeClass *
|
||||
klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_content_component_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_content_component_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_content_component_node_init (GstMPDContentComponentNode * self)
|
||||
{
|
||||
self->id = 0;
|
||||
self->lang = NULL;
|
||||
self->contentType = NULL;
|
||||
self->par = 0;
|
||||
self->Accessibility = 0;
|
||||
self->Role = NULL;
|
||||
self->Rating = NULL;
|
||||
self->Viewpoint = NULL;
|
||||
}
|
||||
|
||||
GstMPDContentComponentNode *
|
||||
gst_mpd_content_component_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_CONTENT_COMPONENT_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_content_component_node_free (GstMPDContentComponentNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDCONTENTCOMPONENTNODE_H__
|
||||
#define __GSTMPDCONTENTCOMPONENTNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_CONTENT_COMPONENT_NODE gst_mpd_content_component_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDContentComponentNode2, gst_mpd_content_component_node, GST, MPD_CONTENT_COMPONENT_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDContentComponentNode2 GstMPDContentComponentNode;
|
||||
typedef GstMPDContentComponentNode2Class GstMPDContentComponentNodeClass;
|
||||
|
||||
struct _GstMPDContentComponentNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
guint id;
|
||||
gchar *lang; /* LangVectorType RFC 5646 */
|
||||
gchar *contentType;
|
||||
GstXMLRatio *par;
|
||||
/* list of Accessibility DescriptorType nodes */
|
||||
GList *Accessibility;
|
||||
/* list of Role DescriptorType nodes */
|
||||
GList *Role;
|
||||
/* list of Rating DescriptorType nodes */
|
||||
GList *Rating;
|
||||
/* list of Viewpoint DescriptorType nodes */
|
||||
GList *Viewpoint;
|
||||
};
|
||||
|
||||
GstMPDContentComponentNode * gst_mpd_content_component_node_new (void);
|
||||
void gst_mpd_content_component_node_free (GstMPDContentComponentNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDCONTENTCOMPONENTNODE_H__ */
|
|
@ -0,0 +1,99 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpddescriptortypenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDDescriptorTypeNode2, gst_mpd_descriptor_type_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_descriptor_type_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDDescriptorTypeNode *self = GST_MPD_DESCRIPTOR_TYPE_NODE (object);
|
||||
|
||||
if (self->schemeIdUri)
|
||||
xmlFree (self->schemeIdUri);
|
||||
if (self->value)
|
||||
xmlFree (self->value);
|
||||
g_free (self->node_name);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_descriptor_type_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_descriptor_type_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr descriptor_type_xml_node = NULL;
|
||||
GstMPDDescriptorTypeNode *self = GST_MPD_DESCRIPTOR_TYPE_NODE (node);
|
||||
|
||||
descriptor_type_xml_node = xmlNewNode (NULL, (xmlChar *) self->node_name);
|
||||
|
||||
gst_xml_helper_set_prop_string (descriptor_type_xml_node, "schemeIdUri",
|
||||
self->schemeIdUri);
|
||||
|
||||
gst_xml_helper_set_prop_string (descriptor_type_xml_node, "value",
|
||||
self->value);
|
||||
|
||||
return descriptor_type_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_descriptor_type_node_class_init (GstMPDDescriptorTypeNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_descriptor_type_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_descriptor_type_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_descriptor_type_node_init (GstMPDDescriptorTypeNode * self)
|
||||
{
|
||||
if (self->schemeIdUri)
|
||||
xmlFree (self->schemeIdUri);
|
||||
if (self->value)
|
||||
xmlFree (self->value);
|
||||
}
|
||||
|
||||
GstMPDDescriptorTypeNode *
|
||||
gst_mpd_descriptor_type_node_new (const gchar * name)
|
||||
{
|
||||
GstMPDDescriptorTypeNode *self =
|
||||
g_object_new (GST_TYPE_MPD_DESCRIPTOR_TYPE_NODE, NULL);
|
||||
self->node_name = g_strdup (name);
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_descriptor_type_node_free (GstMPDDescriptorTypeNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDDESCRIPTORTYPENODE_H__
|
||||
#define __GSTMPDDESCRIPTORTYPENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_DESCRIPTOR_TYPE_NODE gst_mpd_descriptor_type_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDDescriptorTypeNode2, gst_mpd_descriptor_type_node, GST, MPD_DESCRIPTOR_TYPE_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDDescriptorTypeNode2 GstMPDDescriptorTypeNode;
|
||||
typedef GstMPDDescriptorTypeNode2Class GstMPDDescriptorTypeNodeClass;
|
||||
|
||||
struct _GstMPDDescriptorTypeNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *node_name;
|
||||
gchar *schemeIdUri;
|
||||
gchar *value;
|
||||
};
|
||||
|
||||
GstMPDDescriptorTypeNode * gst_mpd_descriptor_type_node_new (const gchar* name);
|
||||
void gst_mpd_descriptor_type_node_free (GstMPDDescriptorTypeNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDDESCRIPTORTYPENODE_H__ */
|
|
@ -0,0 +1,214 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstmpdbaseurlnode.h"
|
||||
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#define GST_CAT_DEFAULT gst_dash_demux2_debug
|
||||
|
||||
gboolean
|
||||
gst_mpd_helper_get_mpd_type (xmlNode * a_node,
|
||||
const gchar * property_name, GstMPDFileType * property_value)
|
||||
{
|
||||
xmlChar *prop_string;
|
||||
gboolean exists = FALSE;
|
||||
|
||||
*property_value = GST_MPD_FILE_TYPE_STATIC; /* default */
|
||||
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
|
||||
if (prop_string) {
|
||||
if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
|
||||
|| xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
|
||||
exists = TRUE;
|
||||
*property_value = GST_MPD_FILE_TYPE_STATIC;
|
||||
GST_LOG (" - %s: static", property_name);
|
||||
} else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
|
||||
|| xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
|
||||
exists = TRUE;
|
||||
*property_value = GST_MPD_FILE_TYPE_DYNAMIC;
|
||||
GST_LOG (" - %s: dynamic", property_name);
|
||||
} else {
|
||||
GST_WARNING ("failed to parse MPD type property %s from xml string %s",
|
||||
property_name, prop_string);
|
||||
}
|
||||
xmlFree (prop_string);
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mpd_helper_get_SAP_type (xmlNode * a_node,
|
||||
const gchar * property_name, GstMPDSAPType * property_value)
|
||||
{
|
||||
xmlChar *prop_string;
|
||||
guint prop_SAP_type = 0;
|
||||
gboolean exists = FALSE;
|
||||
|
||||
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
|
||||
if (prop_string) {
|
||||
if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type) == 1
|
||||
&& prop_SAP_type <= 6) {
|
||||
exists = TRUE;
|
||||
*property_value = (GstMPDSAPType) prop_SAP_type;
|
||||
GST_LOG (" - %s: %u", property_name, prop_SAP_type);
|
||||
} else {
|
||||
GST_WARNING
|
||||
("failed to parse unsigned integer property %s from xml string %s",
|
||||
property_name, prop_string);
|
||||
}
|
||||
xmlFree (prop_string);
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gst_mpd_helper_get_audio_codec_from_mime (GstCaps * caps)
|
||||
{
|
||||
GstStructure *s;
|
||||
const gchar *name = "";
|
||||
const gchar *codec_name = NULL;
|
||||
|
||||
if (!caps)
|
||||
return NULL;
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
if (!s)
|
||||
goto done;
|
||||
name = gst_structure_get_name (s);
|
||||
if (!g_strcmp0 (name, "audio/mpeg")) {
|
||||
gint mpeg_version;
|
||||
if (gst_structure_get_int (s, "mpegversion", &mpeg_version)) {
|
||||
if (mpeg_version == 4)
|
||||
return "mp4a";
|
||||
}
|
||||
|
||||
} else {
|
||||
GST_DEBUG ("No codecs for this caps name %s", name);
|
||||
}
|
||||
|
||||
done:
|
||||
return codec_name;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gst_mpd_helper_get_video_codec_from_mime (GstCaps * caps)
|
||||
{
|
||||
GstStructure *s;
|
||||
const gchar *name = "";
|
||||
const gchar *codec_name = NULL;
|
||||
|
||||
if (!caps)
|
||||
return NULL;
|
||||
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
if (!s)
|
||||
goto done;
|
||||
name = gst_structure_get_name (s);
|
||||
if (!g_strcmp0 (name, "video/x-h264")) {
|
||||
return "avc1";
|
||||
} else if (!g_strcmp0 (name, "video/x-h265")) {
|
||||
return "hvc1";
|
||||
} else {
|
||||
GST_DEBUG ("No codecs for this caps name %s", name);
|
||||
}
|
||||
|
||||
done:
|
||||
return codec_name;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gst_mpd_helper_mimetype_to_caps (const gchar * mimeType)
|
||||
{
|
||||
if (mimeType == NULL)
|
||||
return NULL;
|
||||
if (strcmp (mimeType, "video/mp2t") == 0) {
|
||||
return "video/mpegts, systemstream=(bool) true";
|
||||
} else if (strcmp (mimeType, "video/mp4") == 0) {
|
||||
return "video/quicktime";
|
||||
} else if (strcmp (mimeType, "audio/mp4") == 0) {
|
||||
return "audio/x-m4a";
|
||||
} else if (strcmp (mimeType, "text/vtt") == 0) {
|
||||
return "application/x-subtitle-vtt";
|
||||
} else
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/* Some top-level mime types directly tell us which
|
||||
* codec is inside */
|
||||
GstCaps *
|
||||
gst_mpd_helper_mimetype_to_codec_caps (const gchar * mimeType)
|
||||
{
|
||||
if (mimeType == NULL)
|
||||
return NULL;
|
||||
|
||||
if (strcmp (mimeType, "text/vtt") == 0)
|
||||
return gst_caps_new_empty_simple ("application/x-subtitle-vtt");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Combine a base url with the current stream base url from the list of
|
||||
* baseURLs. Takes ownership of base and returns a new base.
|
||||
*/
|
||||
GstUri *
|
||||
gst_mpd_helper_combine_urls (GstUri * base, GList * list, gchar ** query,
|
||||
guint idx)
|
||||
{
|
||||
GstMPDBaseURLNode *baseURL;
|
||||
GstUri *ret = base;
|
||||
|
||||
if (list != NULL) {
|
||||
baseURL = g_list_nth_data (list, idx);
|
||||
if (!baseURL) {
|
||||
baseURL = list->data;
|
||||
}
|
||||
|
||||
ret = gst_uri_from_string_with_base (base, baseURL->baseURL);
|
||||
gst_uri_unref (base);
|
||||
|
||||
if (ret && query) {
|
||||
g_free (*query);
|
||||
*query = gst_uri_get_query_string (ret);
|
||||
if (*query) {
|
||||
ret = gst_uri_make_writable (ret);
|
||||
gst_uri_set_query_table (ret, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* comparison functions */
|
||||
int
|
||||
gst_mpd_helper_strncmp_ext (const char *s1, const char *s2)
|
||||
{
|
||||
if (s1 == NULL && s2 == NULL)
|
||||
return 0;
|
||||
if (s1 == NULL && s2 != NULL)
|
||||
return 1;
|
||||
if (s2 == NULL && s1 != NULL)
|
||||
return 1;
|
||||
return strncmp (s1, s2, strlen (s2));
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GST_MPDHELPER_H__
|
||||
#define __GST_MPDHELPER_H__
|
||||
|
||||
#include "gstxmlhelper.h"
|
||||
#include "gstmpdnode.h"
|
||||
#include "gstmpdurltypenode.h"
|
||||
#include "gstmpddescriptortypenode.h"
|
||||
#include "gstmpdsegmenttimelinenode.h"
|
||||
#include "gstmpdsegmentbasenode.h"
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_SAP_TYPE_0 = 0,
|
||||
GST_SAP_TYPE_1,
|
||||
GST_SAP_TYPE_2,
|
||||
GST_SAP_TYPE_3,
|
||||
GST_SAP_TYPE_4,
|
||||
GST_SAP_TYPE_5,
|
||||
GST_SAP_TYPE_6
|
||||
} GstMPDSAPType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_MPD_FILE_TYPE_STATIC = 0,
|
||||
GST_MPD_FILE_TYPE_DYNAMIC
|
||||
} GstMPDFileType;
|
||||
|
||||
#define GST_MPD_XLINK_ACTUATE_ON_LOAD_STR "onLoad"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_MPD_XLINK_ACTUATE_ON_REQUEST,
|
||||
GST_MPD_XLINK_ACTUATE_ON_LOAD
|
||||
} GstMPDXLinkActuate;
|
||||
|
||||
|
||||
gboolean gst_mpd_helper_get_mpd_type (xmlNode * a_node, const gchar * property_name, GstMPDFileType * property_value);
|
||||
gboolean gst_mpd_helper_get_SAP_type (xmlNode * a_node, const gchar * property_name, GstMPDSAPType * property_value);
|
||||
|
||||
const gchar * gst_mpd_helper_mimetype_to_caps (const gchar * mimeType);
|
||||
GstCaps *gst_mpd_helper_mimetype_to_codec_caps (const gchar * mimeType);
|
||||
const gchar * gst_mpd_helper_get_video_codec_from_mime (GstCaps * caps);
|
||||
const gchar * gst_mpd_helper_get_audio_codec_from_mime (GstCaps * caps);
|
||||
GstUri *gst_mpd_helper_combine_urls (GstUri * base, GList * list, gchar ** query, guint idx);
|
||||
int gst_mpd_helper_strncmp_ext (const char *s1, const char *s2);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_MPDHELPER_H__ */
|
|
@ -0,0 +1,84 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdlocationnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDLocationNode2, gst_mpd_location_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_location_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDLocationNode *self = GST_MPD_LOCATION_NODE (object);
|
||||
|
||||
g_free (self->location);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_location_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_location_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr location_xml_node = NULL;
|
||||
GstMPDLocationNode *self = GST_MPD_LOCATION_NODE (node);
|
||||
|
||||
location_xml_node = xmlNewNode (NULL, (xmlChar *) "Location");
|
||||
|
||||
if (self->location)
|
||||
gst_xml_helper_set_content (location_xml_node, self->location);
|
||||
|
||||
return location_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_location_node_class_init (GstMPDLocationNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->finalize = gst_mpd_location_node_finalize;
|
||||
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
m_klass->get_xml_node = gst_mpd_location_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_location_node_init (GstMPDLocationNode * self)
|
||||
{
|
||||
self->location = NULL;
|
||||
}
|
||||
|
||||
GstMPDLocationNode *
|
||||
gst_mpd_location_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_LOCATION_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_location_node_free (GstMPDLocationNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDLOCATIONNODE_H__
|
||||
#define __GSTMPDLOCATIONNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_LOCATION_NODE gst_mpd_location_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDLocationNode2, gst_mpd_location_node, GST, MPD_LOCATION_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDLocationNode2 GstMPDLocationNode;
|
||||
typedef GstMPDLocationNode2Class GstMPDLocationNodeClass;
|
||||
|
||||
struct _GstMPDLocationNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *location;
|
||||
};
|
||||
|
||||
GstMPDLocationNode * gst_mpd_location_node_new (void);
|
||||
void gst_mpd_location_node_free (GstMPDLocationNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDLOCATIONNODE_H__ */
|
|
@ -0,0 +1,94 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdmetricsnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDMetricsNode2, gst_mpd_metrics_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_metrics_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDMetricsNode *self = GST_MPD_METRICS_NODE (object);
|
||||
|
||||
g_free (self->metrics);
|
||||
g_list_free_full (self->MetricsRanges,
|
||||
(GDestroyNotify) gst_mpd_metrics_range_node_free);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_metrics_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_metrics_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr metrics_xml_node = NULL;
|
||||
GstMPDMetricsNode *self = GST_MPD_METRICS_NODE (node);
|
||||
|
||||
metrics_xml_node = xmlNewNode (NULL, (xmlChar *) "Metrics");
|
||||
|
||||
if (self->metrics)
|
||||
gst_xml_helper_set_prop_string (metrics_xml_node, "metrics", self->metrics);
|
||||
|
||||
g_list_foreach (self->Reportings, gst_mpd_node_get_list_item,
|
||||
metrics_xml_node);
|
||||
g_list_foreach (self->MetricsRanges, gst_mpd_node_get_list_item,
|
||||
metrics_xml_node);
|
||||
|
||||
return metrics_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_metrics_node_class_init (GstMPDMetricsNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_metrics_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_metrics_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_metrics_node_init (GstMPDMetricsNode * self)
|
||||
{
|
||||
self->metrics = NULL;
|
||||
self->MetricsRanges = NULL;
|
||||
self->Reportings = NULL;
|
||||
}
|
||||
|
||||
GstMPDMetricsNode *
|
||||
gst_mpd_metrics_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_METRICS_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_metrics_node_free (GstMPDMetricsNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDMETRICSNODE_H__
|
||||
#define __GSTMPDMETRICSNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_METRICS_NODE gst_mpd_metrics_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDMetricsNode2, gst_mpd_metrics_node, GST, MPD_METRICS_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDMetricsNode2 GstMPDMetricsNode;
|
||||
typedef GstMPDMetricsNode2Class GstMPDMetricsNodeClass;
|
||||
|
||||
struct _GstMPDMetricsNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *metrics;
|
||||
/* list of Metrics Range nodes */
|
||||
GList *MetricsRanges;
|
||||
/* list of Reporting nodes */
|
||||
GList *Reportings;
|
||||
};
|
||||
|
||||
GstMPDMetricsNode * gst_mpd_metrics_node_new (void);
|
||||
void gst_mpd_metrics_node_free (GstMPDMetricsNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDMETRICSNODE_H__ */
|
|
@ -0,0 +1,75 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdmetricsrangenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDMetricsRangeNode2, gst_mpd_metrics_range_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_metrics_range_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr metrics_range_xml_node = NULL;
|
||||
GstMPDMetricsRangeNode *self = GST_MPD_METRICS_RANGE_NODE (node);
|
||||
|
||||
metrics_range_xml_node = xmlNewNode (NULL, (xmlChar *) "Range");
|
||||
|
||||
if (self->starttime)
|
||||
gst_xml_helper_set_prop_duration (metrics_range_xml_node, "starttime",
|
||||
self->starttime);
|
||||
if (self->duration)
|
||||
gst_xml_helper_set_prop_duration (metrics_range_xml_node, "duration",
|
||||
self->duration);
|
||||
|
||||
return metrics_range_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_metrics_range_node_class_init (GstMPDMetricsRangeNodeClass * klass)
|
||||
{
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_metrics_range_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_metrics_range_node_init (GstMPDMetricsRangeNode * self)
|
||||
{
|
||||
self->starttime = 0; /* [ms] */
|
||||
self->duration = 0; /* [ms] */
|
||||
}
|
||||
|
||||
GstMPDMetricsRangeNode *
|
||||
gst_mpd_metrics_range_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_METRICS_RANGE_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_metrics_range_node_free (GstMPDMetricsRangeNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDMETRICSRANGENODE_H__
|
||||
#define __GSTMPDMETRICSRANGENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_METRICS_RANGE_NODE gst_mpd_metrics_range_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDMetricsRangeNode2, gst_mpd_metrics_range_node, GST, MPD_METRICS_RANGE_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDMetricsRangeNode2 GstMPDMetricsRangeNode;
|
||||
typedef GstMPDMetricsRangeNode2Class GstMPDMetricsRangeNodeClass;
|
||||
|
||||
struct _GstMPDMetricsRangeNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
guint64 starttime; /* [ms] */
|
||||
guint64 duration; /* [ms] */
|
||||
};
|
||||
|
||||
GstMPDMetricsRangeNode * gst_mpd_metrics_range_node_new (void);
|
||||
void gst_mpd_metrics_range_node_free (GstMPDMetricsRangeNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDMETRICSRANGENODE_H__ */
|
|
@ -0,0 +1,153 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdmultsegmentbasenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDMultSegmentBaseNode2, gst_mpd_mult_segment_base_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_MULT_SEGMENT_BASE_0 = 100,
|
||||
PROP_MPD_MULT_SEGMENT_BASE_DURATION,
|
||||
PROP_MPD_MULT_SEGMENT_BASE_START_NUMBER,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_mult_segment_base_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDMultSegmentBaseNode *self = GST_MPD_MULT_SEGMENT_BASE_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_MULT_SEGMENT_BASE_DURATION:
|
||||
self->duration = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_MPD_MULT_SEGMENT_BASE_START_NUMBER:
|
||||
self->startNumber = g_value_get_uint (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_mult_segment_base_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDMultSegmentBaseNode *self = GST_MPD_MULT_SEGMENT_BASE_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_MULT_SEGMENT_BASE_DURATION:
|
||||
g_value_set_uint (value, self->duration);
|
||||
break;
|
||||
case PROP_MPD_MULT_SEGMENT_BASE_START_NUMBER:
|
||||
g_value_set_uint (value, self->startNumber);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_mult_segment_base_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDMultSegmentBaseNode *self = GST_MPD_MULT_SEGMENT_BASE_NODE (object);
|
||||
|
||||
gst_mpd_segment_base_node_free (self->SegmentBase);
|
||||
gst_mpd_segment_timeline_node_free (self->SegmentTimeline);
|
||||
gst_mpd_url_type_node_free (self->BitstreamSwitching);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_mult_segment_base_node_parent_class)->finalize
|
||||
(object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static void
|
||||
gst_mpd_mult_segment_base_get_xml_node (GstMPDNode * node,
|
||||
xmlNodePtr mult_segment_base_node)
|
||||
{
|
||||
GstMPDMultSegmentBaseNode *self = GST_MPD_MULT_SEGMENT_BASE_NODE (node);
|
||||
|
||||
if (self->duration)
|
||||
gst_xml_helper_set_prop_uint (mult_segment_base_node, "duration",
|
||||
self->duration);
|
||||
if (self->startNumber)
|
||||
gst_xml_helper_set_prop_uint (mult_segment_base_node, "startNumber",
|
||||
self->startNumber);
|
||||
if (self->SegmentBase)
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->SegmentBase),
|
||||
mult_segment_base_node);
|
||||
if (self->SegmentTimeline)
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->SegmentTimeline),
|
||||
mult_segment_base_node);
|
||||
if (self->BitstreamSwitching)
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->BitstreamSwitching),
|
||||
mult_segment_base_node);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_mult_segment_base_node_class_init (GstMPDMultSegmentBaseNodeClass *
|
||||
klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_mult_segment_base_node_finalize;
|
||||
object_class->set_property = gst_mpd_mult_segment_base_node_set_property;
|
||||
object_class->get_property = gst_mpd_mult_segment_base_node_get_property;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_MULT_SEGMENT_BASE_DURATION, g_param_spec_uint ("duration",
|
||||
"duration", "duration of segment", 0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_MULT_SEGMENT_BASE_START_NUMBER,
|
||||
g_param_spec_uint ("start-number", "start number",
|
||||
"start number in the segment list", 0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_mult_segment_base_node_init (GstMPDMultSegmentBaseNode * self)
|
||||
{
|
||||
self->duration = 0;
|
||||
self->startNumber = 0;
|
||||
self->SegmentBase = NULL;
|
||||
self->SegmentTimeline = NULL;
|
||||
self->BitstreamSwitching = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_mult_segment_base_node_add_child_node (GstMPDNode * node,
|
||||
xmlNodePtr parent_xml_node)
|
||||
{
|
||||
if (node) {
|
||||
xmlNodePtr new_xml_node = gst_mpd_node_get_xml_pointer (node);
|
||||
gst_mpd_mult_segment_base_get_xml_node (node, new_xml_node);
|
||||
xmlAddChild (parent_xml_node, new_xml_node);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDMULTSEGMENTBASENODE_H__
|
||||
#define __GSTMPDMULTSEGMENTBASENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_MULT_SEGMENT_BASE_NODE gst_mpd_mult_segment_base_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDMultSegmentBaseNode2, gst_mpd_mult_segment_base_node, GST, MPD_MULT_SEGMENT_BASE_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDMultSegmentBaseNode2 GstMPDMultSegmentBaseNode;
|
||||
typedef GstMPDMultSegmentBaseNode2Class GstMPDMultSegmentBaseNodeClass;
|
||||
|
||||
struct _GstMPDMultSegmentBaseNode2
|
||||
{
|
||||
GstObject base;
|
||||
guint duration; /* in seconds */
|
||||
guint startNumber;
|
||||
/* SegmentBaseType extension */
|
||||
GstMPDSegmentBaseNode *SegmentBase;
|
||||
/* SegmentTimeline node */
|
||||
GstMPDSegmentTimelineNode *SegmentTimeline;
|
||||
/* BitstreamSwitching node */
|
||||
GstMPDURLTypeNode *BitstreamSwitching;
|
||||
};
|
||||
|
||||
|
||||
void gst_mpd_mult_segment_base_node_add_child_node (GstMPDNode* node, xmlNodePtr parent_xml_node);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GSTMPDMULTSEGMENTBASENODE_H__ */
|
|
@ -0,0 +1,78 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdnode.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDNode2, gst_mpd_node, GST_TYPE_OBJECT);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_node_class_init (GstMPDNode2Class * klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_node_init (GstMPDNode2 * self)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_node_get_list_item (gpointer data, gpointer user_data)
|
||||
{
|
||||
GstMPDNode *node = (GstMPDNode *) data;
|
||||
xmlNodePtr parent_xml_node = (xmlNodePtr) user_data;
|
||||
xmlNodePtr new_xml_node = gst_mpd_node_get_xml_pointer (node);
|
||||
|
||||
xmlAddChild (parent_xml_node, new_xml_node);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_node_add_child_node (GstMPDNode * child, xmlNodePtr parent)
|
||||
{
|
||||
xmlNodePtr new_xml_node = gst_mpd_node_get_xml_pointer (child);
|
||||
xmlAddChild (parent, new_xml_node);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mpd_node_get_xml_buffer (GstMPDNode * node, gchar ** xml_content,
|
||||
int *xml_size)
|
||||
{
|
||||
GstMPDNode2Class *klass;
|
||||
|
||||
klass = GST_MPD_NODE_GET_CLASS (node);
|
||||
if (klass->get_xml_buffer)
|
||||
return klass->get_xml_buffer (node, xml_content, xml_size);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
xmlNodePtr
|
||||
gst_mpd_node_get_xml_pointer (GstMPDNode * node)
|
||||
{
|
||||
GstMPDNode2Class *klass;
|
||||
if (!node)
|
||||
return NULL;
|
||||
klass = GST_MPD_NODE_GET_CLASS (node);
|
||||
if (klass->get_xml_node)
|
||||
return klass->get_xml_node (node);
|
||||
else
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDNODE_H__
|
||||
#define __GSTMPDNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstxmlhelper.h"
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_NODE gst_mpd_node_get_type ()
|
||||
G_DECLARE_DERIVABLE_TYPE (GstMPDNode2, gst_mpd_node,GST, MPD_NODE, GstObject)
|
||||
|
||||
typedef GstMPDNode2 GstMPDNode;
|
||||
typedef GstMPDNode2Class GstMPDNodeClass;
|
||||
|
||||
#define glib_autoptr_clear_GstMPDNode glib_autoptr_clear_GstMPDNode2
|
||||
|
||||
typedef gboolean (*GstMPDGetXMLBuffer) (GstMPDNode * n, gchar ** doc_content, int *doc_size);
|
||||
typedef xmlNodePtr (*GstMPDGetXMLNode) (GstMPDNode * n);
|
||||
|
||||
struct _GstMPDNode2Class {
|
||||
GstObjectClass base;
|
||||
|
||||
GstMPDGetXMLBuffer get_xml_buffer;
|
||||
GstMPDGetXMLNode get_xml_node;
|
||||
};
|
||||
|
||||
gboolean gst_mpd_node_get_xml_buffer (GstMPDNode * node, gchar ** xml_content, int * xml_size);
|
||||
xmlNodePtr gst_mpd_node_get_xml_pointer (GstMPDNode * node);
|
||||
|
||||
void gst_mpd_node_get_list_item (gpointer data, gpointer user_data);
|
||||
void gst_mpd_node_add_child_node (GstMPDNode* data, xmlNodePtr user_data);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GSTMPDNODE_H__ */
|
1569
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstmpdparser.c
Normal file
1569
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstmpdparser.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* DASH MPD parsing library
|
||||
*
|
||||
* gstmpdparser.h
|
||||
*
|
||||
* Copyright (C) 2012 STMicroelectronics
|
||||
*
|
||||
* Authors:
|
||||
* Gianluca Gennari <gennarone@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_MPDPARSER_H__
|
||||
#define __GST_MPDPARSER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
|
||||
#include "gstadaptivedemux.h"
|
||||
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstxmlhelper.h"
|
||||
#include "gstmpdrootnode.h"
|
||||
#include "gstmpdbaseurlnode.h"
|
||||
#include "gstmpdutctimingnode.h"
|
||||
#include "gstmpdmetricsnode.h"
|
||||
#include "gstmpdmetricsrangenode.h"
|
||||
#include "gstmpdsnode.h"
|
||||
#include "gstmpdsegmenttimelinenode.h"
|
||||
#include "gstmpdsegmenttemplatenode.h"
|
||||
#include "gstmpdsegmenturlnode.h"
|
||||
#include "gstmpdsegmentlistnode.h"
|
||||
#include "gstmpdsegmentbasenode.h"
|
||||
#include "gstmpdperiodnode.h"
|
||||
#include "gstmpdrepresentationnode.h"
|
||||
#include "gstmpdsubrepresentationnode.h"
|
||||
#include "gstmpdcontentcomponentnode.h"
|
||||
#include "gstmpdadaptationsetnode.h"
|
||||
#include "gstmpdsubsetnode.h"
|
||||
#include "gstmpdprograminformationnode.h"
|
||||
#include "gstmpdlocationnode.h"
|
||||
#include "gstmpdreportingnode.h"
|
||||
#include "gstmpdurltypenode.h"
|
||||
#include "gstmpddescriptortypenode.h"
|
||||
#include "gstmpdrepresentationbasenode.h"
|
||||
#include "gstmpdmultsegmentbasenode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstActiveStream GstActiveStream;
|
||||
typedef struct _GstStreamPeriod GstStreamPeriod;
|
||||
typedef struct _GstMediaFragmentInfo GstMediaFragmentInfo;
|
||||
typedef struct _GstMediaSegment GstMediaSegment;
|
||||
|
||||
|
||||
#define GST_MPD_DURATION_NONE ((guint64)-1)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_STREAM_UNKNOWN,
|
||||
GST_STREAM_VIDEO, /* video stream (the main one) */
|
||||
GST_STREAM_AUDIO, /* audio stream (optional) */
|
||||
GST_STREAM_APPLICATION /* application stream (optional): for timed text/subtitles */
|
||||
} GstStreamMimeType;
|
||||
|
||||
/**
|
||||
* GstStreamPeriod:
|
||||
*
|
||||
* Stream period data structure
|
||||
*/
|
||||
struct _GstStreamPeriod
|
||||
{
|
||||
GstMPDPeriodNode *period; /* Stream period */
|
||||
guint number; /* Period number */
|
||||
GstClockTime start; /* Period start time */
|
||||
GstClockTime duration; /* Period duration */
|
||||
};
|
||||
|
||||
/**
|
||||
* GstMediaSegment:
|
||||
*
|
||||
* Media segment data structure
|
||||
*/
|
||||
struct _GstMediaSegment
|
||||
{
|
||||
GstMPDSegmentURLNode *SegmentURL; /* this is NULL when using a SegmentTemplate */
|
||||
guint number; /* segment number */
|
||||
gint repeat; /* number of extra repetitions (0 = played only once) */
|
||||
guint64 scale_start; /* start time in timescale units */
|
||||
guint64 scale_duration; /* duration in timescale units */
|
||||
GstClockTime start; /* segment start time */
|
||||
GstClockTime duration; /* segment duration */
|
||||
};
|
||||
|
||||
struct _GstMediaFragmentInfo
|
||||
{
|
||||
gchar *uri;
|
||||
gint64 range_start;
|
||||
gint64 range_end;
|
||||
|
||||
gchar *index_uri;
|
||||
gint64 index_range_start;
|
||||
gint64 index_range_end;
|
||||
|
||||
gboolean discontinuity;
|
||||
GstClockTime timestamp;
|
||||
GstClockTime duration;
|
||||
};
|
||||
|
||||
/**
|
||||
* GstActiveStream:
|
||||
*
|
||||
* Active stream data structure
|
||||
*/
|
||||
struct _GstActiveStream
|
||||
{
|
||||
GstStreamMimeType mimeType; /* video/audio/application */
|
||||
|
||||
guint baseURL_idx; /* index of the baseURL used for last request */
|
||||
gchar *baseURL; /* active baseURL used for last request */
|
||||
gchar *queryURL; /* active queryURL used for last request */
|
||||
guint max_bandwidth; /* max bandwidth allowed for this mimeType */
|
||||
|
||||
GstMPDAdaptationSetNode *cur_adapt_set; /* active adaptation set */
|
||||
gint representation_idx; /* index of current representation */
|
||||
GstMPDRepresentationNode *cur_representation; /* active representation */
|
||||
GstMPDSegmentBaseNode *cur_segment_base; /* active segment base */
|
||||
GstMPDSegmentListNode *cur_segment_list; /* active segment list */
|
||||
GstMPDSegmentTemplateNode *cur_seg_template; /* active segment template */
|
||||
gint segment_index; /* index of next sequence chunk */
|
||||
guint segment_repeat_index; /* index of the repeat count of a segment */
|
||||
GPtrArray *segments; /* array of GstMediaSegment */
|
||||
GstClockTime presentationTimeOffset; /* presentation time offset of the current segment */
|
||||
};
|
||||
|
||||
/* MPD file parsing */
|
||||
gboolean gst_mpdparser_get_mpd_root_node (GstMPDRootNode ** mpd_root_node, const gchar * data, gint size);
|
||||
GstMPDSegmentListNode * gst_mpdparser_get_external_segment_list (const gchar * data, gint size, GstMPDSegmentListNode * parent);
|
||||
GList * gst_mpdparser_get_external_periods (const gchar * data, gint size);
|
||||
GList * gst_mpdparser_get_external_adaptation_sets (const gchar * data, gint size, GstMPDPeriodNode* period);
|
||||
|
||||
/* navigation functions */
|
||||
GstStreamMimeType gst_mpdparser_representation_get_mimetype (GstMPDAdaptationSetNode * adapt_set, GstMPDRepresentationNode * rep);
|
||||
|
||||
/* Memory management */
|
||||
void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
|
||||
void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
|
||||
void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
|
||||
void gst_mpdparser_media_fragment_info_clear (GstMediaFragmentInfo * fragment);
|
||||
/* Active stream methods*/
|
||||
void gst_mpdparser_init_active_stream_segments (GstActiveStream * stream);
|
||||
gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream, GstMPDSegmentURLNode * segmentURL);
|
||||
const gchar *gst_mpdparser_get_initializationURL (GstActiveStream * stream, GstMPDURLTypeNode * InitializationURL);
|
||||
gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template, const gchar * id, guint number, guint bandwidth, guint64 time);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MPDPARSER_H__ */
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdperiodnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDPeriodNode2, gst_mpd_period_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_PERIOD_0,
|
||||
PROP_MPD_PERIOD_ID,
|
||||
PROP_MPD_PERIOD_START,
|
||||
PROP_MPD_PERIOD_DURATION,
|
||||
PROP_MPD_PERIOD_BITSTREAM_SWITCHING,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_period_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDPeriodNode *self = GST_MPD_PERIOD_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_PERIOD_ID:
|
||||
g_free (self->id);
|
||||
self->id = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_PERIOD_START:
|
||||
self->start = g_value_get_uint64 (value);
|
||||
break;
|
||||
case PROP_MPD_PERIOD_DURATION:
|
||||
self->duration = g_value_get_uint64 (value);
|
||||
break;
|
||||
case PROP_MPD_PERIOD_BITSTREAM_SWITCHING:
|
||||
self->bitstreamSwitching = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_period_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDPeriodNode *self = GST_MPD_PERIOD_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_PERIOD_ID:
|
||||
g_value_set_string (value, self->id);
|
||||
break;
|
||||
case PROP_MPD_PERIOD_START:
|
||||
g_value_set_uint64 (value, self->start);
|
||||
break;
|
||||
case PROP_MPD_PERIOD_DURATION:
|
||||
g_value_set_uint64 (value, self->duration);
|
||||
break;
|
||||
case PROP_MPD_PERIOD_BITSTREAM_SWITCHING:
|
||||
g_value_set_boolean (value, self->bitstreamSwitching);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_period_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDPeriodNode *self = GST_MPD_PERIOD_NODE (object);
|
||||
|
||||
if (self->id)
|
||||
xmlFree (self->id);
|
||||
gst_mpd_segment_base_node_free (self->SegmentBase);
|
||||
gst_mpd_segment_list_node_free (self->SegmentList);
|
||||
gst_mpd_segment_template_node_free (self->SegmentTemplate);
|
||||
g_list_free_full (self->AdaptationSets,
|
||||
(GDestroyNotify) gst_mpd_adaptation_set_node_free);
|
||||
g_list_free_full (self->Subsets, (GDestroyNotify) gst_mpd_subset_node_free);
|
||||
g_list_free_full (self->BaseURLs, (GDestroyNotify) gst_mpd_baseurl_node_free);
|
||||
if (self->xlink_href)
|
||||
xmlFree (self->xlink_href);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_period_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_period_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr period_xml_node = NULL;
|
||||
GstMPDPeriodNode *self = GST_MPD_PERIOD_NODE (node);
|
||||
|
||||
period_xml_node = xmlNewNode (NULL, (xmlChar *) "Period");
|
||||
|
||||
if (self->id)
|
||||
gst_xml_helper_set_prop_string (period_xml_node, "id", self->id);
|
||||
|
||||
gst_xml_helper_set_prop_duration (period_xml_node, "start", self->start);
|
||||
gst_xml_helper_set_prop_duration (period_xml_node, "duration",
|
||||
self->duration);
|
||||
gst_xml_helper_set_prop_boolean (period_xml_node, "bitstreamSwitching",
|
||||
self->bitstreamSwitching);
|
||||
|
||||
if (self->SegmentBase)
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->SegmentBase),
|
||||
period_xml_node);
|
||||
|
||||
if (self->SegmentList)
|
||||
gst_mpd_mult_segment_base_node_add_child_node (GST_MPD_NODE
|
||||
(self->SegmentList), period_xml_node);
|
||||
|
||||
if (self->SegmentTemplate)
|
||||
gst_mpd_mult_segment_base_node_add_child_node (GST_MPD_NODE
|
||||
(self->SegmentTemplate), period_xml_node);
|
||||
|
||||
g_list_foreach (self->AdaptationSets,
|
||||
gst_mpd_representation_base_node_get_list_item, period_xml_node);
|
||||
g_list_foreach (self->Subsets, gst_mpd_node_get_list_item, period_xml_node);
|
||||
g_list_foreach (self->BaseURLs, gst_mpd_node_get_list_item, period_xml_node);
|
||||
|
||||
|
||||
return period_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_period_node_class_init (GstMPDPeriodNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_period_node_finalize;
|
||||
object_class->set_property = gst_mpd_period_node_set_property;
|
||||
object_class->get_property = gst_mpd_period_node_get_property;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_period_get_xml_node;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_MPD_PERIOD_ID,
|
||||
g_param_spec_string ("id", "id",
|
||||
"unique id for period", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_MPD_PERIOD_START,
|
||||
g_param_spec_uint64 ("start", "Period start",
|
||||
"Period start",
|
||||
0, G_MAXUINT64, 0,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_MPD_PERIOD_DURATION,
|
||||
g_param_spec_uint64 ("duration", "period duration",
|
||||
"Period duration",
|
||||
0, G_MAXUINT64, 0,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_PERIOD_BITSTREAM_SWITCHING,
|
||||
g_param_spec_boolean ("bitstream-switching", "Bitstream switching",
|
||||
"Bitstream switching", FALSE,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_period_node_init (GstMPDPeriodNode * self)
|
||||
{
|
||||
self->id = NULL;
|
||||
self->start = 0; /* [ms] */
|
||||
self->duration = 0; /* [ms] */
|
||||
self->bitstreamSwitching = 0;
|
||||
self->SegmentBase = NULL;
|
||||
self->SegmentList = NULL;
|
||||
self->SegmentTemplate = NULL;
|
||||
self->AdaptationSets = NULL;
|
||||
self->Subsets = NULL;
|
||||
self->BaseURLs = NULL;
|
||||
self->xlink_href = NULL;
|
||||
self->actuate = 0;
|
||||
}
|
||||
|
||||
GstMPDPeriodNode *
|
||||
gst_mpd_period_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_PERIOD_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_period_node_free (GstMPDPeriodNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDPERIODNODE_H__
|
||||
#define __GSTMPDPERIODNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstmpdsegmentlistnode.h"
|
||||
#include "gstmpdsegmenttemplatenode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GstSegmentTemplateNode;
|
||||
|
||||
#define GST_TYPE_MPD_PERIOD_NODE gst_mpd_period_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDPeriodNode2, gst_mpd_period_node, GST, MPD_PERIOD_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDPeriodNode2 GstMPDPeriodNode;
|
||||
typedef GstMPDPeriodNode2Class GstMPDPeriodNodeClass;
|
||||
|
||||
struct _GstMPDPeriodNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *id;
|
||||
guint64 start; /* [ms] */
|
||||
guint64 duration; /* [ms] */
|
||||
gboolean bitstreamSwitching;
|
||||
/* SegmentBase node */
|
||||
GstMPDSegmentBaseNode *SegmentBase;
|
||||
/* SegmentList node */
|
||||
GstMPDSegmentListNode *SegmentList;
|
||||
/* SegmentTemplate node */
|
||||
GstMPDSegmentTemplateNode *SegmentTemplate;
|
||||
/* list of Adaptation Set nodes */
|
||||
GList *AdaptationSets;
|
||||
/* list of Representation nodes */
|
||||
GList *Subsets;
|
||||
/* list of BaseURL nodes */
|
||||
GList *BaseURLs;
|
||||
|
||||
gchar *xlink_href;
|
||||
int actuate;
|
||||
};
|
||||
|
||||
GstMPDPeriodNode * gst_mpd_period_node_new (void);
|
||||
void gst_mpd_period_node_free (GstMPDPeriodNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDPERIODNODE_H__ */
|
|
@ -0,0 +1,125 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdprograminformationnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDProgramInformationNode2, gst_mpd_program_information_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_program_information_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDProgramInformationNode *self =
|
||||
GST_MPD_PROGRAM_INFORMATION_NODE (object);
|
||||
|
||||
if (self->lang)
|
||||
xmlFree (self->lang);
|
||||
if (self->moreInformationURL)
|
||||
xmlFree (self->moreInformationURL);
|
||||
if (self->Title)
|
||||
xmlFree (self->Title);
|
||||
if (self->Source)
|
||||
xmlFree (self->Source);
|
||||
if (self->Copyright)
|
||||
xmlFree (self->Copyright);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_program_information_node_parent_class)->finalize
|
||||
(object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_program_information_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr program_info_xml_node = NULL;
|
||||
xmlNodePtr child_node = NULL;
|
||||
GstMPDProgramInformationNode *self = GST_MPD_PROGRAM_INFORMATION_NODE (node);
|
||||
|
||||
program_info_xml_node = xmlNewNode (NULL, (xmlChar *) "ProgramInformation");
|
||||
|
||||
if (self->lang)
|
||||
gst_xml_helper_set_prop_string (program_info_xml_node, "lang", self->lang);
|
||||
|
||||
if (self->moreInformationURL)
|
||||
gst_xml_helper_set_prop_string (program_info_xml_node, "moreInformationURL",
|
||||
self->moreInformationURL);
|
||||
|
||||
if (self->Title) {
|
||||
child_node = xmlNewNode (NULL, (xmlChar *) "Title");
|
||||
gst_xml_helper_set_content (child_node, self->Title);
|
||||
xmlAddChild (program_info_xml_node, child_node);
|
||||
}
|
||||
|
||||
if (self->Source) {
|
||||
child_node = xmlNewNode (NULL, (xmlChar *) "Source");
|
||||
gst_xml_helper_set_content (child_node, self->Source);
|
||||
xmlAddChild (program_info_xml_node, child_node);
|
||||
}
|
||||
|
||||
if (self->Copyright) {
|
||||
child_node = xmlNewNode (NULL, (xmlChar *) "Copyright");
|
||||
gst_xml_helper_set_content (child_node, self->Copyright);
|
||||
xmlAddChild (program_info_xml_node, child_node);
|
||||
}
|
||||
|
||||
return program_info_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_program_information_node_class_init (GstMPDProgramInformationNodeClass *
|
||||
klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_program_information_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_program_information_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_program_information_node_init (GstMPDProgramInformationNode * self)
|
||||
{
|
||||
self->lang = NULL;
|
||||
self->moreInformationURL = NULL;
|
||||
self->Title = NULL;
|
||||
self->Source = NULL;
|
||||
self->Copyright = NULL;
|
||||
}
|
||||
|
||||
GstMPDProgramInformationNode *
|
||||
gst_mpd_program_information_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_PROGRAM_INFORMATION_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_program_information_node_free (GstMPDProgramInformationNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDPROGRAMINFORMATIONNODE_H__
|
||||
#define __GSTMPDPROGRAMINFORMATIONNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_PROGRAM_INFORMATION_NODE gst_mpd_program_information_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDProgramInformationNode2, gst_mpd_program_information_node, GST, MPD_PROGRAM_INFORMATION_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDProgramInformationNode2 GstMPDProgramInformationNode;
|
||||
typedef GstMPDProgramInformationNode2Class GstMPDProgramInformationNodeClass;
|
||||
|
||||
struct _GstMPDProgramInformationNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *lang; /* LangVectorType RFC 5646 */
|
||||
gchar *moreInformationURL;
|
||||
/* children nodes */
|
||||
gchar *Title;
|
||||
gchar *Source;
|
||||
gchar *Copyright;
|
||||
};
|
||||
|
||||
GstMPDProgramInformationNode * gst_mpd_program_information_node_new (void);
|
||||
void gst_mpd_program_information_node_free (GstMPDProgramInformationNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDPROGRAMINFORMATIONNODE_H__ */
|
|
@ -0,0 +1,64 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdreportingnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDReportingNode2, gst_mpd_reporting_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_reporting_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr reporting_xml_node = NULL;
|
||||
|
||||
reporting_xml_node = xmlNewNode (NULL, (xmlChar *) "Reporting");
|
||||
|
||||
return reporting_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_reporting_node_class_init (GstMPDReportingNodeClass * klass)
|
||||
{
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_reporting_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_reporting_node_init (GstMPDReportingNode * self)
|
||||
{
|
||||
}
|
||||
|
||||
GstMPDReportingNode *
|
||||
gst_mpd_reporting_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_REPORTING_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_reporting_node_free (GstMPDReportingNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDREPORTINGNODE_H__
|
||||
#define __GSTMPDREPORTINGNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_REPORTING_NODE gst_mpd_reporting_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDReportingNode2, gst_mpd_reporting_node, GST, MPD_REPORTING_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDReportingNode2 GstMPDReportingNode;
|
||||
typedef GstMPDReportingNode2Class GstMPDReportingNodeClass;
|
||||
|
||||
struct _GstMPDReportingNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
};
|
||||
|
||||
GstMPDReportingNode * gst_mpd_reporting_node_new (void);
|
||||
void gst_mpd_reporting_node_free (GstMPDReportingNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDREPORTINGNODE_H__ */
|
|
@ -0,0 +1,351 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdrepresentationbasenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDRepresentationBaseNode2, gst_mpd_representation_base_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_REPRESENTATION_BASE_0 = 100,
|
||||
PROP_MPD_REPRESENTATION_BASE_PROFILES,
|
||||
PROP_MPD_REPRESENTATION_BASE_WIDTH,
|
||||
PROP_MPD_REPRESENTATION_BASE_HEIGHT,
|
||||
PROP_MPD_REPRESENTATION_BASE_SAR,
|
||||
PROP_MPD_REPRESENTATION_BASE_MIN_FRAME_RATE,
|
||||
PROP_MPD_REPRESENTATION_BASE_MAX_FRAME_RATE,
|
||||
PROP_MPD_REPRESENTATION_BASE_FRAME_RATE,
|
||||
PROP_MPD_REPRESENTATION_BASE_AUDIO_SAMPLING_RATE,
|
||||
PROP_MPD_REPRESENTATION_BASE_MIMETYPE,
|
||||
PROP_MPD_REPRESENTATION_BASE_SEGMENT_PROFILES,
|
||||
PROP_MPD_REPRESENTATION_BASE_CODECS,
|
||||
PROP_MPD_REPRESENTATION_BASE_MAX_SAP_PERIOD,
|
||||
PROP_MPD_REPRESENTATION_BASE_START_WITH_SAP,
|
||||
PROP_MPD_REPRESENTATION_BASE_MAX_PLAYOUT_RATE,
|
||||
PROP_MPD_REPRESENTATION_BASE_CODING_DEPENDENCY,
|
||||
PROP_MPD_REPRESENTATION_BASE_SCAN_TYPE,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_representation_base_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDRepresentationBaseNode *self =
|
||||
GST_MPD_REPRESENTATION_BASE_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_REPRESENTATION_BASE_PROFILES:
|
||||
g_free (self->profiles);
|
||||
self->profiles = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_WIDTH:
|
||||
self->width = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_HEIGHT:
|
||||
self->height = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_SAR:
|
||||
g_slice_free (GstXMLRatio, self->sar);
|
||||
self->sar = gst_xml_helper_clone_ratio (g_value_get_pointer (value));
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MIN_FRAME_RATE:
|
||||
g_slice_free (GstXMLFrameRate, self->minFrameRate);
|
||||
self->minFrameRate =
|
||||
gst_xml_helper_clone_frame_rate (g_value_get_pointer (value));
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MAX_FRAME_RATE:
|
||||
g_slice_free (GstXMLFrameRate, self->maxFrameRate);
|
||||
self->maxFrameRate =
|
||||
gst_xml_helper_clone_frame_rate (g_value_get_pointer (value));
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_FRAME_RATE:
|
||||
g_slice_free (GstXMLFrameRate, self->frameRate);
|
||||
self->frameRate =
|
||||
gst_xml_helper_clone_frame_rate (g_value_get_pointer (value));
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_AUDIO_SAMPLING_RATE:
|
||||
g_free (self->audioSamplingRate);
|
||||
self->audioSamplingRate =
|
||||
g_strdup_printf ("%u", g_value_get_uint (value));
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MIMETYPE:
|
||||
g_free (self->mimeType);
|
||||
self->mimeType = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_SEGMENT_PROFILES:
|
||||
g_free (self->segmentProfiles);
|
||||
self->segmentProfiles = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_CODECS:
|
||||
g_free (self->codecs);
|
||||
self->codecs = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MAX_SAP_PERIOD:
|
||||
self->maximumSAPPeriod = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_START_WITH_SAP:
|
||||
self->startWithSAP = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MAX_PLAYOUT_RATE:
|
||||
self->maxPlayoutRate = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_CODING_DEPENDENCY:
|
||||
self->codingDependency = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_SCAN_TYPE:
|
||||
g_free (self->scanType);
|
||||
self->scanType = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_base_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDRepresentationBaseNode *self =
|
||||
GST_MPD_REPRESENTATION_BASE_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_REPRESENTATION_BASE_PROFILES:
|
||||
g_value_set_string (value, self->profiles);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_WIDTH:
|
||||
g_value_set_uint (value, self->width);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_HEIGHT:
|
||||
g_value_set_uint (value, self->height);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_SAR:
|
||||
g_value_set_pointer (value, self->sar);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MIN_FRAME_RATE:
|
||||
g_value_set_pointer (value, self->minFrameRate);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MAX_FRAME_RATE:
|
||||
g_value_set_pointer (value, self->maxFrameRate);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_FRAME_RATE:
|
||||
g_value_set_pointer (value, self->frameRate);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_AUDIO_SAMPLING_RATE:
|
||||
g_value_set_uint (value, atoi (self->audioSamplingRate));
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MIMETYPE:
|
||||
g_value_set_string (value, self->mimeType);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_SEGMENT_PROFILES:
|
||||
g_value_set_string (value, self->segmentProfiles);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_CODECS:
|
||||
g_value_set_string (value, self->codecs);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MAX_SAP_PERIOD:
|
||||
g_value_set_double (value, self->maximumSAPPeriod);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_START_WITH_SAP:
|
||||
g_value_set_int (value, self->startWithSAP);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_MAX_PLAYOUT_RATE:
|
||||
g_value_set_double (value, self->maxPlayoutRate);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_CODING_DEPENDENCY:
|
||||
g_value_set_boolean (value, self->codingDependency);
|
||||
self->codingDependency = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BASE_SCAN_TYPE:
|
||||
g_value_set_string (value, self->scanType);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_base_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDRepresentationBaseNode *self =
|
||||
GST_MPD_REPRESENTATION_BASE_NODE (object);
|
||||
|
||||
if (self->profiles)
|
||||
xmlFree (self->profiles);
|
||||
g_slice_free (GstXMLRatio, self->sar);
|
||||
g_slice_free (GstXMLFrameRate, self->frameRate);
|
||||
g_slice_free (GstXMLFrameRate, self->minFrameRate);
|
||||
g_slice_free (GstXMLFrameRate, self->maxFrameRate);
|
||||
if (self->audioSamplingRate)
|
||||
xmlFree (self->audioSamplingRate);
|
||||
if (self->mimeType)
|
||||
xmlFree (self->mimeType);
|
||||
if (self->segmentProfiles)
|
||||
xmlFree (self->segmentProfiles);
|
||||
if (self->codecs)
|
||||
xmlFree (self->codecs);
|
||||
if (self->scanType)
|
||||
xmlFree (self->scanType);
|
||||
g_list_free_full (self->FramePacking,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->AudioChannelConfiguration,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
g_list_free_full (self->ContentProtection,
|
||||
(GDestroyNotify) gst_mpd_descriptor_type_node_free);
|
||||
|
||||
if (self->caps)
|
||||
gst_caps_unref (self->caps);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_representation_base_node_parent_class)->finalize
|
||||
(object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static void
|
||||
gst_mpd_representation_base_get_xml_node (GstMPDNode * node,
|
||||
xmlNodePtr representation_base_node)
|
||||
{
|
||||
GstMPDRepresentationBaseNode *self = GST_MPD_REPRESENTATION_BASE_NODE (node);
|
||||
|
||||
if (self->profiles)
|
||||
gst_xml_helper_set_prop_string (representation_base_node, "profiles",
|
||||
self->profiles);
|
||||
if (self->width)
|
||||
gst_xml_helper_set_prop_uint (representation_base_node, "width",
|
||||
self->width);
|
||||
if (self->height)
|
||||
gst_xml_helper_set_prop_uint (representation_base_node, "height",
|
||||
self->height);
|
||||
|
||||
gst_xml_helper_set_prop_ratio (representation_base_node, "sar", self->sar);
|
||||
gst_xml_helper_set_prop_framerate (representation_base_node, "minFrameRate",
|
||||
self->minFrameRate);
|
||||
gst_xml_helper_set_prop_framerate (representation_base_node, "maxFrameRate",
|
||||
self->maxFrameRate);
|
||||
gst_xml_helper_set_prop_framerate (representation_base_node, "frameRate",
|
||||
self->frameRate);
|
||||
|
||||
gst_xml_helper_set_prop_string (representation_base_node,
|
||||
"audioSamplingRate", self->audioSamplingRate);
|
||||
gst_xml_helper_set_prop_string (representation_base_node, "mimeType",
|
||||
self->mimeType);
|
||||
gst_xml_helper_set_prop_string (representation_base_node, "segmentProfiles",
|
||||
self->segmentProfiles);
|
||||
gst_xml_helper_set_prop_string (representation_base_node, "codecs",
|
||||
self->codecs);
|
||||
if (self->maximumSAPPeriod)
|
||||
gst_xml_helper_set_prop_double (representation_base_node,
|
||||
"maximumSAPPeriod", self->maximumSAPPeriod);
|
||||
if (self->startWithSAP)
|
||||
gst_xml_helper_set_prop_int (representation_base_node, "startWithSAP",
|
||||
self->startWithSAP);
|
||||
if (self->maxPlayoutRate)
|
||||
gst_xml_helper_set_prop_double (representation_base_node, "maxPlayoutRate",
|
||||
self->maxPlayoutRate);
|
||||
if (self->codingDependency)
|
||||
gst_xml_helper_set_prop_boolean (representation_base_node,
|
||||
"codingDependency", self->codingDependency);
|
||||
|
||||
gst_xml_helper_set_prop_string (representation_base_node, "scanType",
|
||||
self->scanType);
|
||||
|
||||
g_list_foreach (self->FramePacking,
|
||||
gst_mpd_node_get_list_item, representation_base_node);
|
||||
g_list_foreach (self->AudioChannelConfiguration,
|
||||
gst_mpd_node_get_list_item, representation_base_node);
|
||||
g_list_foreach (self->ContentProtection,
|
||||
gst_mpd_node_get_list_item, representation_base_node);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_base_node_class_init (GstMPDRepresentationBaseNodeClass *
|
||||
klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_representation_base_node_finalize;
|
||||
object_class->set_property = gst_mpd_representation_base_node_set_property;
|
||||
object_class->get_property = gst_mpd_representation_base_node_get_property;
|
||||
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_REPRESENTATION_BASE_WIDTH, g_param_spec_uint ("width",
|
||||
"width", "representation width", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_REPRESENTATION_BASE_HEIGHT, g_param_spec_uint ("height",
|
||||
"height", "representation height", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_REPRESENTATION_BASE_MIMETYPE, g_param_spec_string ("mime-type",
|
||||
"mimetype", "representation mimetype", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_REPRESENTATION_BASE_CODECS, g_param_spec_string ("codecs",
|
||||
"codecs", "representation codec", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_REPRESENTATION_BASE_AUDIO_SAMPLING_RATE,
|
||||
g_param_spec_uint ("audio-sampling-rate", "audio sampling rate",
|
||||
"representation audio sampling rate", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_base_node_init (GstMPDRepresentationBaseNode * self)
|
||||
{
|
||||
self->profiles = NULL;
|
||||
self->width = 0;
|
||||
self->height = 0;
|
||||
self->sar = NULL;
|
||||
self->minFrameRate = NULL;
|
||||
self->maxFrameRate = NULL;
|
||||
self->frameRate = NULL;
|
||||
self->audioSamplingRate = NULL;
|
||||
self->mimeType = NULL;
|
||||
self->segmentProfiles = NULL;
|
||||
self->codecs = NULL;
|
||||
self->maximumSAPPeriod = 0;
|
||||
self->startWithSAP = GST_SAP_TYPE_0;
|
||||
self->maxPlayoutRate = 0.0;
|
||||
self->codingDependency = FALSE;
|
||||
self->scanType = NULL;
|
||||
self->FramePacking = NULL;
|
||||
self->AudioChannelConfiguration = NULL;
|
||||
self->ContentProtection = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_representation_base_node_get_list_item (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstMPDNode *node = (GstMPDNode *) data;
|
||||
xmlNodePtr parent_xml_node = (xmlNodePtr) user_data;
|
||||
xmlNodePtr new_xml_node = gst_mpd_node_get_xml_pointer (node);
|
||||
|
||||
gst_mpd_representation_base_get_xml_node (node, new_xml_node);
|
||||
xmlAddChild (parent_xml_node, new_xml_node);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDREPRESENTATIONBASENODE_H__
|
||||
#define __GSTMPDREPRESENTATIONBASENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_REPRESENTATION_BASE_NODE gst_mpd_representation_base_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDRepresentationBaseNode2, gst_mpd_representation_base_node, GST, MPD_REPRESENTATION_BASE_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDRepresentationBaseNode2 GstMPDRepresentationBaseNode;
|
||||
typedef GstMPDRepresentationBaseNode2Class GstMPDRepresentationBaseNodeClass;
|
||||
|
||||
#define glib_autoptr_clear_GstMPDRepresentationBaseNode glib_autoptr_clear_GstMPDRepresentationBaseNode2
|
||||
|
||||
struct _GstMPDRepresentationBaseNode2
|
||||
{
|
||||
GstObject base;
|
||||
gchar *profiles;
|
||||
guint width;
|
||||
guint height;
|
||||
GstXMLRatio *sar;
|
||||
GstXMLFrameRate *minFrameRate;
|
||||
GstXMLFrameRate *maxFrameRate;
|
||||
GstXMLFrameRate *frameRate;
|
||||
gchar *audioSamplingRate;
|
||||
gchar *mimeType;
|
||||
gchar *segmentProfiles;
|
||||
gchar *codecs;
|
||||
GstCaps *caps;
|
||||
gdouble maximumSAPPeriod;
|
||||
GstMPDSAPType startWithSAP;
|
||||
gdouble maxPlayoutRate;
|
||||
gboolean codingDependency;
|
||||
gchar *scanType;
|
||||
/* list of FramePacking DescriptorType nodes */
|
||||
GList *FramePacking;
|
||||
/* list of AudioChannelConfiguration DescriptorType nodes */
|
||||
GList *AudioChannelConfiguration;
|
||||
/* list of ContentProtection DescriptorType nodes */
|
||||
GList *ContentProtection;
|
||||
};
|
||||
|
||||
|
||||
void gst_mpd_representation_base_node_get_list_item (gpointer data, gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GSTMPDREPRESENTATIONBASENODE_H__ */
|
|
@ -0,0 +1,198 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdrepresentationnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDRepresentationNode2, gst_mpd_representation_node,
|
||||
GST_TYPE_MPD_REPRESENTATION_BASE_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_REPRESENTATION_0,
|
||||
PROP_MPD_REPRESENTATION_ID,
|
||||
PROP_MPD_REPRESENTATION_BANDWIDTH,
|
||||
PROP_MPD_REPRESENTATION_QUALITY_RANKING,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_representation_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDRepresentationNode *self = GST_MPD_REPRESENTATION_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_REPRESENTATION_ID:
|
||||
g_free (self->id);
|
||||
self->id = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BANDWIDTH:
|
||||
self->bandwidth = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_QUALITY_RANKING:
|
||||
self->qualityRanking = g_value_get_uint (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDRepresentationNode *self = GST_MPD_REPRESENTATION_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_REPRESENTATION_ID:
|
||||
g_value_set_string (value, self->id);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_BANDWIDTH:
|
||||
g_value_set_uint (value, self->bandwidth);
|
||||
break;
|
||||
case PROP_MPD_REPRESENTATION_QUALITY_RANKING:
|
||||
g_value_set_uint (value, self->qualityRanking);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDRepresentationNode *self = GST_MPD_REPRESENTATION_NODE (object);
|
||||
|
||||
if (self->id)
|
||||
xmlFree (self->id);
|
||||
g_strfreev (self->dependencyId);
|
||||
g_strfreev (self->mediaStreamStructureId);
|
||||
g_list_free_full (self->SubRepresentations,
|
||||
(GDestroyNotify) gst_mpd_sub_representation_node_free);
|
||||
gst_mpd_segment_base_node_free (self->SegmentBase);
|
||||
gst_mpd_segment_template_node_free (self->SegmentTemplate);
|
||||
gst_mpd_segment_list_node_free (self->SegmentList);
|
||||
g_list_free_full (self->BaseURLs, (GDestroyNotify) gst_mpd_baseurl_node_free);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_representation_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_representation_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
gchar *value;
|
||||
xmlNodePtr representation_xml_node = NULL;
|
||||
GstMPDRepresentationNode *self = GST_MPD_REPRESENTATION_NODE (node);
|
||||
|
||||
representation_xml_node = xmlNewNode (NULL, (xmlChar *) "Representation");
|
||||
|
||||
gst_xml_helper_set_prop_string (representation_xml_node, "id", self->id);
|
||||
gst_xml_helper_set_prop_uint (representation_xml_node, "bandwidth",
|
||||
self->bandwidth);
|
||||
if (self->qualityRanking)
|
||||
gst_xml_helper_set_prop_uint (representation_xml_node, "qualityRanking",
|
||||
self->qualityRanking);
|
||||
|
||||
|
||||
if (self->dependencyId) {
|
||||
value = g_strjoinv (" ", self->dependencyId);
|
||||
gst_xml_helper_set_prop_string (representation_xml_node, "dependencyId",
|
||||
value);
|
||||
g_free (value);
|
||||
}
|
||||
if (self->mediaStreamStructureId) {
|
||||
value = g_strjoinv (" ", self->mediaStreamStructureId);
|
||||
gst_xml_helper_set_prop_string (representation_xml_node,
|
||||
"mediaStreamStructureId", value);
|
||||
g_free (value);
|
||||
}
|
||||
|
||||
g_list_foreach (self->BaseURLs, gst_mpd_node_get_list_item,
|
||||
representation_xml_node);
|
||||
g_list_foreach (self->SubRepresentations,
|
||||
gst_mpd_representation_base_node_get_list_item, representation_xml_node);
|
||||
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->SegmentBase),
|
||||
representation_xml_node);
|
||||
gst_mpd_mult_segment_base_node_add_child_node (GST_MPD_NODE
|
||||
(self->SegmentTemplate), representation_xml_node);
|
||||
gst_mpd_mult_segment_base_node_add_child_node (GST_MPD_NODE
|
||||
(self->SegmentList), representation_xml_node);
|
||||
|
||||
return representation_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_node_class_init (GstMPDRepresentationNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_representation_node_finalize;
|
||||
object_class->set_property = gst_mpd_representation_node_set_property;
|
||||
object_class->get_property = gst_mpd_representation_node_get_property;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_representation_get_xml_node;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_REPRESENTATION_BANDWIDTH, g_param_spec_uint ("bandwidth",
|
||||
"bandwidth", "representation bandwidth", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_REPRESENTATION_QUALITY_RANKING,
|
||||
g_param_spec_uint ("quality-ranking", "quality ranking",
|
||||
"representation quality ranking", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_representation_node_init (GstMPDRepresentationNode * self)
|
||||
{
|
||||
self->id = NULL;
|
||||
self->bandwidth = 0;
|
||||
self->qualityRanking = 0;
|
||||
self->dependencyId = NULL;
|
||||
self->mediaStreamStructureId = NULL;
|
||||
self->BaseURLs = NULL;
|
||||
self->SubRepresentations = NULL;
|
||||
self->SegmentBase = NULL;
|
||||
self->SegmentTemplate = NULL;
|
||||
self->SegmentList = NULL;
|
||||
}
|
||||
|
||||
GstMPDRepresentationNode *
|
||||
gst_mpd_representation_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_REPRESENTATION_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_representation_node_free (GstMPDRepresentationNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDREPRESENTATIONNODE_H__
|
||||
#define __GSTMPDREPRESENTATIONNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstmpdrepresentationbasenode.h"
|
||||
#include "gstmpdsegmentlistnode.h"
|
||||
#include "gstmpdsegmenttemplatenode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_REPRESENTATION_NODE gst_mpd_representation_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDRepresentationNode2, gst_mpd_representation_node, GST, MPD_REPRESENTATION_NODE, GstMPDRepresentationBaseNode)
|
||||
|
||||
typedef GstMPDRepresentationNode2 GstMPDRepresentationNode;
|
||||
typedef GstMPDRepresentationNode2Class GstMPDRepresentationNodeClass;
|
||||
|
||||
struct _GstMPDRepresentationNode2
|
||||
{
|
||||
GstMPDRepresentationBaseNode parent_instance;
|
||||
gchar *id;
|
||||
guint bandwidth;
|
||||
guint qualityRanking;
|
||||
gchar **dependencyId; /* StringVectorType */
|
||||
gchar **mediaStreamStructureId; /* StringVectorType */
|
||||
/* list of BaseURL nodes */
|
||||
GList *BaseURLs;
|
||||
/* list of SubRepresentation nodes */
|
||||
GList *SubRepresentations;
|
||||
/* SegmentBase node */
|
||||
GstMPDSegmentBaseNode *SegmentBase;
|
||||
/* SegmentTemplate node */
|
||||
GstMPDSegmentTemplateNode *SegmentTemplate;
|
||||
/* SegmentList node */
|
||||
GstMPDSegmentListNode *SegmentList;
|
||||
};
|
||||
|
||||
|
||||
GstMPDRepresentationNode * gst_mpd_representation_node_new (void);
|
||||
void gst_mpd_representation_node_free (GstMPDRepresentationNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDREPRESENTATIONNODE_H__ */
|
|
@ -0,0 +1,405 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdrootnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDRootNode2, gst_mpd_root_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_ROOT_0,
|
||||
PROP_MPD_ROOT_DEFAULT_NAMESPACE,
|
||||
PROP_MPD_ROOT_NAMESPACE_XSI,
|
||||
PROP_MPD_ROOT_NAMESPACE_EXT,
|
||||
PROP_MPD_ROOT_SCHEMA_LOCATION,
|
||||
PROP_MPD_ROOT_ID,
|
||||
PROP_MPD_ROOT_PROFILES,
|
||||
PROP_MPD_ROOT_TYPE,
|
||||
PROP_MPD_ROOT_PUBLISH_TIME,
|
||||
PROP_MPD_ROOT_AVAILABILTY_START_TIME,
|
||||
PROP_MPD_ROOT_AVAILABILTY_END_TIME,
|
||||
PROP_MPD_ROOT_MEDIA_PRESENTATION_DURATION,
|
||||
PROP_MPD_ROOT_MINIMUM_UPDATE_PERIOD,
|
||||
PROP_MPD_ROOT_MIN_BUFFER_TIME,
|
||||
PROP_MPD_ROOT_TIMESHIFT_BUFFER_DEPTH,
|
||||
PROP_MPD_ROOT_SUGGESTED_PRESENTATION_DELAY,
|
||||
PROP_MPD_ROOT_MAX_SEGMENT_DURATION,
|
||||
PROP_MPD_ROOT_MAX_SUBSEGMENT_DURATION,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_root_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDRootNode *self = GST_MPD_ROOT_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_ROOT_DEFAULT_NAMESPACE:
|
||||
g_free (self->default_namespace);
|
||||
self->default_namespace = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_NAMESPACE_XSI:
|
||||
g_free (self->namespace_xsi);
|
||||
self->namespace_xsi = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_NAMESPACE_EXT:
|
||||
g_free (self->namespace_ext);
|
||||
self->namespace_ext = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_SCHEMA_LOCATION:
|
||||
g_free (self->schemaLocation);
|
||||
self->schemaLocation = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_ID:
|
||||
g_free (self->id);
|
||||
self->id = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_PROFILES:
|
||||
g_free (self->profiles);
|
||||
self->profiles = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_TYPE:
|
||||
self->type = (GstMPDFileType) g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_AVAILABILTY_START_TIME:
|
||||
if (self->availabilityStartTime)
|
||||
gst_date_time_unref (self->availabilityStartTime);
|
||||
self->availabilityStartTime = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_AVAILABILTY_END_TIME:
|
||||
if (self->availabilityEndTime)
|
||||
gst_date_time_unref (self->availabilityEndTime);
|
||||
self->availabilityEndTime = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_PUBLISH_TIME:
|
||||
if (self->publishTime)
|
||||
gst_date_time_unref (self->publishTime);
|
||||
self->publishTime = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_MEDIA_PRESENTATION_DURATION:
|
||||
self->mediaPresentationDuration = g_value_get_uint64 (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_MINIMUM_UPDATE_PERIOD:
|
||||
self->minimumUpdatePeriod = g_value_get_uint64 (value);
|
||||
break;
|
||||
case PROP_MPD_ROOT_MIN_BUFFER_TIME:
|
||||
self->minBufferTime = g_value_get_uint64 (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_root_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDRootNode *self = GST_MPD_ROOT_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_ROOT_DEFAULT_NAMESPACE:
|
||||
g_value_set_string (value, self->default_namespace);
|
||||
break;
|
||||
case PROP_MPD_ROOT_NAMESPACE_XSI:
|
||||
g_value_set_string (value, self->namespace_xsi);
|
||||
break;
|
||||
case PROP_MPD_ROOT_NAMESPACE_EXT:
|
||||
g_value_set_string (value, self->namespace_ext);
|
||||
break;
|
||||
case PROP_MPD_ROOT_SCHEMA_LOCATION:
|
||||
g_value_set_string (value, self->schemaLocation);
|
||||
break;
|
||||
case PROP_MPD_ROOT_ID:
|
||||
g_value_set_string (value, self->id);
|
||||
break;
|
||||
case PROP_MPD_ROOT_PROFILES:
|
||||
g_value_set_string (value, self->profiles);
|
||||
break;
|
||||
case PROP_MPD_ROOT_TYPE:
|
||||
g_value_set_int (value, self->type);
|
||||
break;
|
||||
case PROP_MPD_ROOT_AVAILABILTY_START_TIME:
|
||||
g_value_set_boxed (value, self->availabilityStartTime);
|
||||
break;
|
||||
case PROP_MPD_ROOT_AVAILABILTY_END_TIME:
|
||||
g_value_set_boxed (value, self->availabilityEndTime);
|
||||
break;
|
||||
case PROP_MPD_ROOT_PUBLISH_TIME:
|
||||
g_value_set_boxed (value, self->publishTime);
|
||||
break;
|
||||
case PROP_MPD_ROOT_MEDIA_PRESENTATION_DURATION:
|
||||
g_value_set_uint64 (value, self->mediaPresentationDuration);
|
||||
break;
|
||||
case PROP_MPD_ROOT_MINIMUM_UPDATE_PERIOD:
|
||||
g_value_set_uint64 (value, self->minimumUpdatePeriod);
|
||||
break;
|
||||
case PROP_MPD_ROOT_MIN_BUFFER_TIME:
|
||||
g_value_set_uint64 (value, self->minBufferTime);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_root_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDRootNode *self = GST_MPD_ROOT_NODE (object);
|
||||
|
||||
g_free (self->default_namespace);
|
||||
g_free (self->namespace_xsi);
|
||||
g_free (self->namespace_ext);
|
||||
g_free (self->schemaLocation);
|
||||
g_free (self->id);
|
||||
g_free (self->profiles);
|
||||
|
||||
if (self->availabilityStartTime)
|
||||
gst_date_time_unref (self->availabilityStartTime);
|
||||
if (self->availabilityEndTime)
|
||||
gst_date_time_unref (self->availabilityEndTime);
|
||||
if (self->publishTime)
|
||||
gst_date_time_unref (self->publishTime);
|
||||
|
||||
g_list_free_full (self->ProgramInfos,
|
||||
(GDestroyNotify) gst_mpd_program_information_node_free);
|
||||
g_list_free_full (self->BaseURLs, (GDestroyNotify) gst_mpd_baseurl_node_free);
|
||||
g_list_free_full (self->Locations,
|
||||
(GDestroyNotify) gst_mpd_location_node_free);
|
||||
g_list_free_full (self->Periods, (GDestroyNotify) gst_mpd_period_node_free);
|
||||
g_list_free_full (self->Metrics, (GDestroyNotify) gst_mpd_metrics_node_free);
|
||||
g_list_free_full (self->UTCTimings,
|
||||
(GDestroyNotify) gst_mpd_utctiming_node_free);
|
||||
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_root_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_root_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr root_xml_node;
|
||||
GstMPDRootNode *self = GST_MPD_ROOT_NODE (node);
|
||||
|
||||
root_xml_node = xmlNewNode (NULL, (xmlChar *) "MPD");
|
||||
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "xmlns",
|
||||
self->default_namespace);
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "profiles", self->profiles);
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "schemaLocation",
|
||||
self->schemaLocation);
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "xmlns:xsi",
|
||||
self->namespace_xsi);
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "xmlns:ext",
|
||||
self->namespace_ext);
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "id", self->id);
|
||||
|
||||
if (self->type == GST_MPD_FILE_TYPE_STATIC)
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "type", (gchar *) "static");
|
||||
else
|
||||
gst_xml_helper_set_prop_string (root_xml_node, "type", (gchar *) "dynamic");
|
||||
|
||||
|
||||
gst_xml_helper_set_prop_date_time (root_xml_node, "availabilityStartTime",
|
||||
self->availabilityStartTime);
|
||||
|
||||
gst_xml_helper_set_prop_date_time (root_xml_node, "availabilityEndTime",
|
||||
self->availabilityEndTime);
|
||||
gst_xml_helper_set_prop_date_time (root_xml_node, "publishTime",
|
||||
self->publishTime);
|
||||
|
||||
if (self->mediaPresentationDuration)
|
||||
gst_xml_helper_set_prop_duration (root_xml_node,
|
||||
"mediaPresentationDuration", self->mediaPresentationDuration);
|
||||
if (self->minimumUpdatePeriod)
|
||||
gst_xml_helper_set_prop_duration (root_xml_node, "minimumUpdatePeriod",
|
||||
self->minimumUpdatePeriod);
|
||||
if (self->minBufferTime)
|
||||
gst_xml_helper_set_prop_duration (root_xml_node, "minBufferTime",
|
||||
self->minBufferTime);
|
||||
if (self->timeShiftBufferDepth)
|
||||
gst_xml_helper_set_prop_duration (root_xml_node, "timeShiftBufferDepth",
|
||||
self->timeShiftBufferDepth);
|
||||
if (self->suggestedPresentationDelay)
|
||||
gst_xml_helper_set_prop_duration (root_xml_node,
|
||||
"suggestedPresentationDelay", self->suggestedPresentationDelay);
|
||||
if (self->maxSegmentDuration)
|
||||
gst_xml_helper_set_prop_duration (root_xml_node, "maxSegmentDuration",
|
||||
self->maxSegmentDuration);
|
||||
if (self->maxSubsegmentDuration)
|
||||
gst_xml_helper_set_prop_duration (root_xml_node, "maxSubsegmentDuration",
|
||||
self->maxSubsegmentDuration);
|
||||
|
||||
g_list_foreach (self->BaseURLs, gst_mpd_node_get_list_item, root_xml_node);
|
||||
g_list_foreach (self->Locations, gst_mpd_node_get_list_item, root_xml_node);
|
||||
g_list_foreach (self->ProgramInfos, gst_mpd_node_get_list_item,
|
||||
root_xml_node);
|
||||
g_list_foreach (self->Periods, gst_mpd_node_get_list_item, root_xml_node);
|
||||
g_list_foreach (self->Metrics, gst_mpd_node_get_list_item, root_xml_node);
|
||||
g_list_foreach (self->UTCTimings, gst_mpd_node_get_list_item, root_xml_node);
|
||||
|
||||
return root_xml_node;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mpd_root_get_xml_buffer (GstMPDNode * node, gchar ** doc_content,
|
||||
gint * doc_size)
|
||||
{
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr root_xml_node;
|
||||
xmlChar *xmlbody;
|
||||
|
||||
doc = xmlNewDoc ((xmlChar *) "1.0");
|
||||
root_xml_node = gst_mpd_root_get_xml_node (node);
|
||||
xmlDocSetRootElement (doc, root_xml_node);
|
||||
|
||||
xmlDocDumpMemory (doc, &xmlbody, doc_size);
|
||||
*doc_content = g_strndup ((gchar *) xmlbody, *doc_size);
|
||||
xmlFree (xmlbody);
|
||||
|
||||
xmlFreeDoc (doc);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_root_node_class_init (GstMPDRootNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_root_node_finalize;
|
||||
object_class->set_property = gst_mpd_root_node_set_property;
|
||||
object_class->get_property = gst_mpd_root_node_get_property;
|
||||
|
||||
m_klass->get_xml_buffer = gst_mpd_root_get_xml_buffer;
|
||||
m_klass->get_xml_node = gst_mpd_root_get_xml_node;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ROOT_DEFAULT_NAMESPACE, g_param_spec_string ("default-namespace",
|
||||
"default namespace", "default namespace", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_MPD_ROOT_NAMESPACE_XSI,
|
||||
g_param_spec_string ("namespace-xsi", "namespace xsi", "namespace xsi",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_MPD_ROOT_NAMESPACE_EXT,
|
||||
g_param_spec_string ("namespace-ext", "namespace ext", "namespace ext",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_MPD_ROOT_SCHEMA_LOCATION,
|
||||
g_param_spec_string ("schema-location", "schema location",
|
||||
"schema location for period", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_MPD_ROOT_ID,
|
||||
g_param_spec_string ("id", "id", "unique id for period", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_MPD_ROOT_PROFILES,
|
||||
g_param_spec_string ("profiles", "profiles", "profiles", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_MPD_ROOT_TYPE,
|
||||
g_param_spec_int ("type", "MPD type",
|
||||
"MPD type",
|
||||
GST_MPD_FILE_TYPE_STATIC, GST_MPD_FILE_TYPE_DYNAMIC,
|
||||
GST_MPD_FILE_TYPE_STATIC,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ROOT_AVAILABILTY_START_TIME,
|
||||
g_param_spec_boxed ("availability-start-time", "Availability start time",
|
||||
"MPD availability start time", GST_TYPE_DATE_TIME,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ROOT_AVAILABILTY_END_TIME,
|
||||
g_param_spec_boxed ("availability-end-time", "Availability end time",
|
||||
"MPD availability end time", GST_TYPE_DATE_TIME,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ROOT_PUBLISH_TIME,
|
||||
g_param_spec_boxed ("publish-time", "publish time",
|
||||
"MPD publish time", GST_TYPE_DATE_TIME,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ROOT_MEDIA_PRESENTATION_DURATION,
|
||||
g_param_spec_uint64 ("media-presentation-duration",
|
||||
"media presentation duration", "media presentation duration", 0,
|
||||
G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ROOT_MINIMUM_UPDATE_PERIOD,
|
||||
g_param_spec_uint64 ("minimum-update-period",
|
||||
"minimum update period", "minimum update period", 0,
|
||||
G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_ROOT_MIN_BUFFER_TIME,
|
||||
g_param_spec_uint64 ("min-buffer-time", "mininim buffer time",
|
||||
"mininim buffer time", 0,
|
||||
G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_root_node_init (GstMPDRootNode * self)
|
||||
{
|
||||
self->default_namespace = NULL;
|
||||
self->namespace_xsi = NULL;
|
||||
self->namespace_ext = NULL;
|
||||
self->schemaLocation = NULL;
|
||||
self->id = NULL;
|
||||
self->profiles = NULL;
|
||||
self->type = GST_MPD_FILE_TYPE_STATIC;
|
||||
self->availabilityStartTime = NULL;
|
||||
self->availabilityEndTime = NULL;
|
||||
self->publishTime = NULL;
|
||||
self->mediaPresentationDuration = 0; /* [ms] */
|
||||
self->minimumUpdatePeriod = 0; /* [ms] */
|
||||
self->minBufferTime = 2000; /* [ms] */
|
||||
self->timeShiftBufferDepth = 0; /* [ms] */
|
||||
self->suggestedPresentationDelay = 0; /* [ms] */
|
||||
self->maxSegmentDuration = 0; /* [ms] */
|
||||
self->maxSubsegmentDuration = 0; /* [ms] */
|
||||
/* list of BaseURL nodes */
|
||||
self->BaseURLs = NULL;
|
||||
/* list of Location nodes */
|
||||
self->Locations = NULL;
|
||||
/* List of ProgramInformation nodes */
|
||||
self->ProgramInfos = NULL;
|
||||
/* list of Periods nodes */
|
||||
self->Periods = NULL;
|
||||
/* list of Metrics nodes */
|
||||
self->Metrics = NULL;
|
||||
/* list of GstUTCTimingNode nodes */
|
||||
self->UTCTimings = NULL;
|
||||
}
|
||||
|
||||
GstMPDRootNode *
|
||||
gst_mpd_root_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_ROOT_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_root_node_free (GstMPDRootNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDROOTNODE_H__
|
||||
#define __GSTMPDROOTNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_ROOT_NODE gst_mpd_root_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDRootNode2, gst_mpd_root_node, GST, MPD_ROOT_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDRootNode2 GstMPDRootNode;
|
||||
typedef GstMPDRootNode2Class GstMPDRootNodeClass;
|
||||
|
||||
struct _GstMPDRootNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *default_namespace;
|
||||
gchar *namespace_xsi;
|
||||
gchar *namespace_ext;
|
||||
gchar *schemaLocation;
|
||||
gchar *id;
|
||||
gchar *profiles;
|
||||
GstMPDFileType type;
|
||||
GstDateTime *availabilityStartTime;
|
||||
GstDateTime *availabilityEndTime;
|
||||
GstDateTime *publishTime;
|
||||
guint64 mediaPresentationDuration; /* [ms] */
|
||||
guint64 minimumUpdatePeriod; /* [ms] */
|
||||
guint64 minBufferTime; /* [ms] */
|
||||
guint64 timeShiftBufferDepth; /* [ms] */
|
||||
guint64 suggestedPresentationDelay; /* [ms] */
|
||||
guint64 maxSegmentDuration; /* [ms] */
|
||||
guint64 maxSubsegmentDuration; /* [ms] */
|
||||
/* list of BaseURL nodes */
|
||||
GList *BaseURLs;
|
||||
/* list of Location nodes */
|
||||
GList *Locations;
|
||||
/* List of ProgramInformation nodes */
|
||||
GList *ProgramInfos;
|
||||
/* list of Periods nodes */
|
||||
GList *Periods;
|
||||
/* list of Metrics nodes */
|
||||
GList *Metrics;
|
||||
/* list of GstUTCTimingNode nodes */
|
||||
GList *UTCTimings;
|
||||
};
|
||||
|
||||
GstMPDRootNode * gst_mpd_root_node_new (void);
|
||||
void gst_mpd_root_node_free (GstMPDRootNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GSTMPDROOTNODE_H__ */
|
|
@ -0,0 +1,112 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsegmentbasenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSegmentBaseNode2, gst_mpd_segment_base_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_segment_base_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDSegmentBaseNode *self = GST_MPD_SEGMENT_BASE_NODE (object);
|
||||
|
||||
if (self->indexRange)
|
||||
g_slice_free (GstXMLRange, self->indexRange);
|
||||
gst_mpd_url_type_node_free (self->Initialization);
|
||||
gst_mpd_url_type_node_free (self->RepresentationIndex);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_segment_base_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_segment_base_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr segment_base_xml_node = NULL;
|
||||
GstMPDSegmentBaseNode *self = GST_MPD_SEGMENT_BASE_NODE (node);
|
||||
|
||||
segment_base_xml_node = xmlNewNode (NULL, (xmlChar *) "SegmentBase");
|
||||
|
||||
if (self->timescale)
|
||||
gst_xml_helper_set_prop_uint (segment_base_xml_node, "timescale",
|
||||
self->timescale);
|
||||
if (self->presentationTimeOffset)
|
||||
gst_xml_helper_set_prop_uint64 (segment_base_xml_node,
|
||||
"presentationTimeOffset", self->presentationTimeOffset);
|
||||
if (self->indexRange) {
|
||||
gst_xml_helper_set_prop_range (segment_base_xml_node, "indexRange",
|
||||
self->indexRange);
|
||||
gst_xml_helper_set_prop_boolean (segment_base_xml_node, "indexRangeExact",
|
||||
self->indexRangeExact);
|
||||
}
|
||||
if (self->Initialization)
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->Initialization),
|
||||
segment_base_xml_node);
|
||||
if (self->RepresentationIndex)
|
||||
gst_mpd_node_add_child_node (GST_MPD_NODE (self->RepresentationIndex),
|
||||
segment_base_xml_node);
|
||||
|
||||
return segment_base_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_base_node_class_init (GstMPDSegmentBaseNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_segment_base_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_segment_base_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_base_node_init (GstMPDSegmentBaseNode * self)
|
||||
{
|
||||
self->timescale = 0;
|
||||
self->presentationTimeOffset = 0;
|
||||
self->indexRange = NULL;
|
||||
self->indexRangeExact = FALSE;
|
||||
/* Initialization node */
|
||||
self->Initialization = NULL;
|
||||
/* RepresentationIndex node */
|
||||
self->RepresentationIndex = NULL;
|
||||
}
|
||||
|
||||
GstMPDSegmentBaseNode *
|
||||
gst_mpd_segment_base_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_SEGMENT_BASE_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_segment_base_node_free (GstMPDSegmentBaseNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSEGMENTBASENODE_H__
|
||||
#define __GSTMPDSEGMENTBASENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdnode.h"
|
||||
#include "gstmpdurltypenode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_SEGMENT_BASE_NODE gst_mpd_segment_base_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSegmentBaseNode2, gst_mpd_segment_base_node, GST, MPD_SEGMENT_BASE_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDSegmentBaseNode2 GstMPDSegmentBaseNode;
|
||||
typedef GstMPDSegmentBaseNode2Class GstMPDSegmentBaseNodeClass;
|
||||
|
||||
#define glib_autoptr_clear_GstMPDMultSegmentBaseNode glib_autoptr_clear_GstMPDMultSegmentBaseNode2
|
||||
|
||||
struct _GstMPDSegmentBaseNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
guint timescale;
|
||||
guint64 presentationTimeOffset;
|
||||
GstXMLRange *indexRange;
|
||||
gboolean indexRangeExact;
|
||||
/* Initialization node */
|
||||
GstMPDURLTypeNode *Initialization;
|
||||
/* RepresentationIndex node */
|
||||
GstMPDURLTypeNode *RepresentationIndex;
|
||||
};
|
||||
|
||||
GstMPDSegmentBaseNode * gst_mpd_segment_base_node_new (void);
|
||||
void gst_mpd_segment_base_node_free (GstMPDSegmentBaseNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSEGMENTBASENODE_H__ */
|
|
@ -0,0 +1,104 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsegmentlistnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSegmentListNode2, gst_mpd_segment_list_node,
|
||||
GST_TYPE_MPD_MULT_SEGMENT_BASE_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_segment_list_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDSegmentListNode *self = GST_MPD_SEGMENT_LIST_NODE (object);
|
||||
|
||||
g_list_free_full (self->SegmentURL,
|
||||
(GDestroyNotify) gst_mpd_segment_url_node_free);
|
||||
if (self->xlink_href)
|
||||
xmlFree (self->xlink_href);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_segment_list_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_segment_list_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr segment_list_xml_node = NULL;
|
||||
GstMPDSegmentListNode *self = GST_MPD_SEGMENT_LIST_NODE (node);
|
||||
|
||||
segment_list_xml_node = xmlNewNode (NULL, (xmlChar *) "SegmentList");
|
||||
|
||||
g_list_foreach (self->SegmentURL, gst_mpd_node_get_list_item,
|
||||
segment_list_xml_node);
|
||||
|
||||
if (self->xlink_href)
|
||||
gst_xml_helper_set_prop_string (segment_list_xml_node, "xlink_href",
|
||||
self->xlink_href);
|
||||
|
||||
return segment_list_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_list_node_class_init (GstMPDSegmentListNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_segment_list_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_segment_list_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_list_node_init (GstMPDSegmentListNode * self)
|
||||
{
|
||||
self->SegmentURL = NULL;
|
||||
self->xlink_href = NULL;
|
||||
self->actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST;
|
||||
}
|
||||
|
||||
GstMPDSegmentListNode *
|
||||
gst_mpd_segment_list_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_SEGMENT_LIST_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_segment_list_node_free (GstMPDSegmentListNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_segment_list_node_add_segment (GstMPDSegmentListNode * self,
|
||||
GstMPDSegmentURLNode * segment_url)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->SegmentURL = g_list_append (self->SegmentURL, segment_url);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSEGMENTLISTNODE_H__
|
||||
#define __GSTMPDSEGMENTLISTNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstmpdmultsegmentbasenode.h"
|
||||
#include "gstmpdsegmenturlnode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_SEGMENT_LIST_NODE gst_mpd_segment_list_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSegmentListNode2, gst_mpd_segment_list_node, GST, MPD_SEGMENT_LIST_NODE, GstMPDMultSegmentBaseNode)
|
||||
|
||||
typedef GstMPDSegmentListNode2 GstMPDSegmentListNode;
|
||||
typedef GstMPDSegmentListNode2Class GstMPDSegmentListNodeClass;
|
||||
|
||||
struct _GstMPDSegmentListNode2
|
||||
{
|
||||
GstMPDMultSegmentBaseNode parent_instance;
|
||||
/* extension */
|
||||
/* list of SegmentURL nodes */
|
||||
GList *SegmentURL;
|
||||
|
||||
gchar *xlink_href;
|
||||
GstMPDXLinkActuate actuate;
|
||||
};
|
||||
|
||||
GstMPDSegmentListNode * gst_mpd_segment_list_node_new (void);
|
||||
void gst_mpd_segment_list_node_free (GstMPDSegmentListNode* self);
|
||||
|
||||
void gst_mpd_segment_list_node_add_segment(GstMPDSegmentListNode * self, GstMPDSegmentURLNode * segment_url);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSEGMENTLISTNODE_H__ */
|
|
@ -0,0 +1,186 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsegmenttemplatenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSegmentTemplateNode2, gst_mpd_segment_template_node,
|
||||
GST_TYPE_MPD_MULT_SEGMENT_BASE_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_SEGMENT_TEMPLATE_0,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_MEDIA,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_INDEX,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_INITIALIZATION,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_BITSTREAM_SWITCHING,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_segment_template_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDSegmentTemplateNode *self = GST_MPD_SEGMENT_TEMPLATE_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_MEDIA:
|
||||
self->media = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_INDEX:
|
||||
self->index = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_INITIALIZATION:
|
||||
self->initialization = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_BITSTREAM_SWITCHING:
|
||||
self->bitstreamSwitching = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_template_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDSegmentTemplateNode *self = GST_MPD_SEGMENT_TEMPLATE_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_MEDIA:
|
||||
g_value_set_string (value, self->media);
|
||||
break;
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_INDEX:
|
||||
g_value_set_string (value, self->index);
|
||||
break;
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_INITIALIZATION:
|
||||
g_value_set_string (value, self->initialization);
|
||||
break;
|
||||
case PROP_MPD_SEGMENT_TEMPLATE_BITSTREAM_SWITCHING:
|
||||
g_value_set_string (value, self->bitstreamSwitching);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_template_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDSegmentTemplateNode *self = GST_MPD_SEGMENT_TEMPLATE_NODE (object);
|
||||
|
||||
if (self->media)
|
||||
xmlFree (self->media);
|
||||
if (self->index)
|
||||
xmlFree (self->index);
|
||||
if (self->initialization)
|
||||
xmlFree (self->initialization);
|
||||
if (self->bitstreamSwitching)
|
||||
xmlFree (self->bitstreamSwitching);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_segment_template_node_parent_class)->finalize
|
||||
(object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_segment_template_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr segment_template_xml_node = NULL;
|
||||
GstMPDSegmentTemplateNode *self = GST_MPD_SEGMENT_TEMPLATE_NODE (node);
|
||||
|
||||
segment_template_xml_node = xmlNewNode (NULL, (xmlChar *) "SegmentTemplate");
|
||||
|
||||
if (self->media)
|
||||
gst_xml_helper_set_prop_string (segment_template_xml_node, "media",
|
||||
self->media);
|
||||
|
||||
if (self->index)
|
||||
gst_xml_helper_set_prop_string (segment_template_xml_node, "index",
|
||||
self->index);
|
||||
|
||||
if (self->initialization)
|
||||
gst_xml_helper_set_prop_string (segment_template_xml_node, "initialization",
|
||||
self->initialization);
|
||||
|
||||
if (self->bitstreamSwitching)
|
||||
gst_xml_helper_set_prop_string (segment_template_xml_node,
|
||||
"bitstreamSwitching", self->bitstreamSwitching);
|
||||
|
||||
return segment_template_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_template_node_class_init (GstMPDSegmentTemplateNodeClass *
|
||||
klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_segment_template_node_finalize;
|
||||
object_class->set_property = gst_mpd_segment_template_node_set_property;
|
||||
object_class->get_property = gst_mpd_segment_template_node_get_property;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_segment_template_get_xml_node;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_MEDIA, g_param_spec_string ("media",
|
||||
"media", "media", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_INDEX, g_param_spec_string ("index",
|
||||
"index", "index", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_INITIALIZATION,
|
||||
g_param_spec_string ("initialization", "initialization", "initialization",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_SEGMENT_TEMPLATE_BITSTREAM_SWITCHING,
|
||||
g_param_spec_string ("bitstream-switching", "bitstream switching",
|
||||
"bitstream switching", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_template_node_init (GstMPDSegmentTemplateNode * self)
|
||||
{
|
||||
self->media = NULL;
|
||||
self->index = NULL;
|
||||
self->initialization = NULL;
|
||||
self->bitstreamSwitching = NULL;
|
||||
}
|
||||
|
||||
GstMPDSegmentTemplateNode *
|
||||
gst_mpd_segment_template_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_SEGMENT_TEMPLATE_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_segment_template_node_free (GstMPDSegmentTemplateNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSEGMENTTEMPLATENODE_H__
|
||||
#define __GSTMPDSEGMENTTEMPLATENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstmpdmultsegmentbasenode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_SEGMENT_TEMPLATE_NODE gst_mpd_segment_template_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSegmentTemplateNode2, gst_mpd_segment_template_node, GST, MPD_SEGMENT_TEMPLATE_NODE, GstMPDMultSegmentBaseNode)
|
||||
|
||||
typedef GstMPDSegmentTemplateNode2 GstMPDSegmentTemplateNode;
|
||||
typedef GstMPDSegmentTemplateNode2Class GstMPDSegmentTemplateNodeClass;
|
||||
|
||||
struct _GstMPDSegmentTemplateNode2
|
||||
{
|
||||
GstMPDMultSegmentBaseNode parent_instance;
|
||||
|
||||
gchar *media;
|
||||
gchar *index;
|
||||
gchar *initialization;
|
||||
gchar *bitstreamSwitching;
|
||||
};
|
||||
|
||||
GstMPDSegmentTemplateNode * gst_mpd_segment_template_node_new (void);
|
||||
void gst_mpd_segment_template_node_free (GstMPDSegmentTemplateNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSEGMENTTEMPLATENODE_H__ */
|
|
@ -0,0 +1,111 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsegmenttimelinenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSegmentTimelineNode2, gst_mpd_segment_timeline_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_segment_timeline_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDSegmentTimelineNode *self = GST_MPD_SEGMENT_TIMELINE_NODE (object);
|
||||
|
||||
g_queue_foreach (&self->S, (GFunc) gst_mpd_s_node_free, NULL);
|
||||
g_queue_clear (&self->S);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_segment_timeline_node_parent_class)->finalize
|
||||
(object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_segment_timeline_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr segment_timeline_xml_node = NULL;
|
||||
GstMPDSegmentTimelineNode *self = GST_MPD_SEGMENT_TIMELINE_NODE (node);
|
||||
|
||||
segment_timeline_xml_node = xmlNewNode (NULL, (xmlChar *) "SegmentTimeline");
|
||||
|
||||
g_queue_foreach (&self->S, (GFunc) gst_mpd_node_get_list_item,
|
||||
segment_timeline_xml_node);
|
||||
|
||||
return segment_timeline_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_timeline_node_class_init (GstMPDSegmentTimelineNodeClass *
|
||||
klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_segment_timeline_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_segment_timeline_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_timeline_node_init (GstMPDSegmentTimelineNode * self)
|
||||
{
|
||||
g_queue_init (&self->S);
|
||||
}
|
||||
|
||||
GstMPDSegmentTimelineNode *
|
||||
gst_mpd_segment_timeline_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_SEGMENT_TIMELINE_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_segment_timeline_node_free (GstMPDSegmentTimelineNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
||||
|
||||
GstMPDSegmentTimelineNode *
|
||||
gst_mpd_segment_timeline_node_clone (GstMPDSegmentTimelineNode *
|
||||
segment_timeline)
|
||||
{
|
||||
GstMPDSegmentTimelineNode *clone = NULL;
|
||||
GList *list;
|
||||
|
||||
if (segment_timeline) {
|
||||
clone = gst_mpd_segment_timeline_node_new ();
|
||||
for (list = g_queue_peek_head_link (&segment_timeline->S); list;
|
||||
list = g_list_next (list)) {
|
||||
GstMPDSNode *s_node;
|
||||
s_node = (GstMPDSNode *) list->data;
|
||||
if (s_node) {
|
||||
g_queue_push_tail (&clone->S, gst_mpd_s_node_clone (s_node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSEGMENTTIMELINENODE_H__
|
||||
#define __GSTMPDSEGMENTTIMELINENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdnode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_SEGMENT_TIMELINE_NODE gst_mpd_segment_timeline_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSegmentTimelineNode2, gst_mpd_segment_timeline_node, GST, MPD_SEGMENT_TIMELINE_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDSegmentTimelineNode2 GstMPDSegmentTimelineNode;
|
||||
typedef GstMPDSegmentTimelineNode2Class GstMPDSegmentTimelineNodeClass;
|
||||
|
||||
struct _GstMPDSegmentTimelineNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
/* list of S nodes */
|
||||
GQueue S;
|
||||
};
|
||||
|
||||
GstMPDSegmentTimelineNode * gst_mpd_segment_timeline_node_new (void);
|
||||
void gst_mpd_segment_timeline_node_free (GstMPDSegmentTimelineNode* self);
|
||||
|
||||
GstMPDSegmentTimelineNode *gst_mpd_segment_timeline_node_clone (GstMPDSegmentTimelineNode * pointer);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSEGMENTTIMELINENODE_H__ */
|
|
@ -0,0 +1,166 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsegmenturlnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
#include "gstmpdhelper.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSegmentURLNode2, gst_mpd_segment_url_node,
|
||||
GST_TYPE_MPD_NODE);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MPD_SEGMENT_URL_0,
|
||||
PROP_MPD_SEGMENT_URL_MEDIA,
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_segment_url_node_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDSegmentURLNode *self = GST_MPD_SEGMENT_URL_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_SEGMENT_URL_MEDIA:
|
||||
g_free (self->media);
|
||||
self->media = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_url_node_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMPDSegmentURLNode *self = GST_MPD_SEGMENT_URL_NODE (object);
|
||||
switch (prop_id) {
|
||||
case PROP_MPD_SEGMENT_URL_MEDIA:
|
||||
g_value_set_string (value, self->media);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_url_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDSegmentURLNode *self = GST_MPD_SEGMENT_URL_NODE (object);
|
||||
|
||||
if (self->media)
|
||||
xmlFree (self->media);
|
||||
g_slice_free (GstXMLRange, self->mediaRange);
|
||||
if (self->index)
|
||||
xmlFree (self->index);
|
||||
g_slice_free (GstXMLRange, self->indexRange);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_segment_url_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_segment_url_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr segment_url_xml_node = NULL;
|
||||
GstMPDSegmentURLNode *self = GST_MPD_SEGMENT_URL_NODE (node);
|
||||
|
||||
segment_url_xml_node = xmlNewNode (NULL, (xmlChar *) "SegmentURL");
|
||||
|
||||
if (self->media)
|
||||
gst_xml_helper_set_prop_string (segment_url_xml_node, "media", self->media);
|
||||
|
||||
if (self->mediaRange)
|
||||
gst_xml_helper_set_prop_range (segment_url_xml_node, "mediaRange",
|
||||
self->mediaRange);
|
||||
|
||||
if (self->index)
|
||||
gst_xml_helper_set_prop_string (segment_url_xml_node, "index", self->index);
|
||||
|
||||
if (self->indexRange)
|
||||
gst_xml_helper_set_prop_range (segment_url_xml_node, "indexRange",
|
||||
self->indexRange);
|
||||
|
||||
return segment_url_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_url_node_class_init (GstMPDSegmentURLNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_segment_url_node_finalize;
|
||||
object_class->set_property = gst_mpd_segment_url_node_set_property;
|
||||
object_class->get_property = gst_mpd_segment_url_node_get_property;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_segment_url_get_xml_node;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MPD_SEGMENT_URL_MEDIA, g_param_spec_string ("media",
|
||||
"media", "media description", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_segment_url_node_init (GstMPDSegmentURLNode * self)
|
||||
{
|
||||
self->media = NULL;
|
||||
self->mediaRange = NULL;
|
||||
self->index = NULL;
|
||||
self->indexRange = NULL;
|
||||
}
|
||||
|
||||
GstMPDSegmentURLNode *
|
||||
gst_mpd_segment_url_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_SEGMENT_URL_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_segment_url_node_free (GstMPDSegmentURLNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
||||
|
||||
GstMPDSegmentURLNode *
|
||||
gst_mpd_segment_url_node_clone (GstMPDSegmentURLNode * seg_url)
|
||||
{
|
||||
GstMPDSegmentURLNode *clone = NULL;
|
||||
|
||||
if (seg_url) {
|
||||
clone = gst_mpd_segment_url_node_new ();
|
||||
clone->media = xmlMemStrdup (seg_url->media);
|
||||
clone->mediaRange = gst_xml_helper_clone_range (seg_url->mediaRange);
|
||||
clone->index = xmlMemStrdup (seg_url->index);
|
||||
clone->indexRange = gst_xml_helper_clone_range (seg_url->indexRange);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSEGMENTURLNODE_H__
|
||||
#define __GSTMPDSEGMENTURLNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdnode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_SEGMENT_URL_NODE gst_mpd_segment_url_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSegmentURLNode2, gst_mpd_segment_url_node, GST, MPD_SEGMENT_URL_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDSegmentURLNode2 GstMPDSegmentURLNode;
|
||||
typedef GstMPDSegmentURLNode2Class GstMPDSegmentURLNodeClass;
|
||||
|
||||
struct _GstMPDSegmentURLNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar *media;
|
||||
GstXMLRange *mediaRange;
|
||||
gchar *index;
|
||||
GstXMLRange *indexRange;
|
||||
};
|
||||
|
||||
GstMPDSegmentURLNode * gst_mpd_segment_url_node_new (void);
|
||||
void gst_mpd_segment_url_node_free (GstMPDSegmentURLNode* self);
|
||||
|
||||
GstMPDSegmentURLNode *gst_mpd_segment_url_node_clone (GstMPDSegmentURLNode * seg_url);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSEGMENTURLNODE_H__ */
|
|
@ -0,0 +1,92 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSNode2, gst_mpd_s_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_s_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr s_xml_node = NULL;
|
||||
GstMPDSNode *self = GST_MPD_S_NODE (node);
|
||||
|
||||
s_xml_node = xmlNewNode (NULL, (xmlChar *) "S");
|
||||
|
||||
if (self->t)
|
||||
gst_xml_helper_set_prop_uint64 (s_xml_node, "t", self->t);
|
||||
|
||||
if (self->d)
|
||||
gst_xml_helper_set_prop_uint64 (s_xml_node, "d", self->d);
|
||||
|
||||
if (self->r)
|
||||
gst_xml_helper_set_prop_int (s_xml_node, "r", self->r);
|
||||
|
||||
return s_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_s_node_class_init (GstMPDSNodeClass * klass)
|
||||
{
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_s_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_s_node_init (GstMPDSNode * self)
|
||||
{
|
||||
self->t = 0;
|
||||
self->d = 0;
|
||||
self->r = 0;
|
||||
}
|
||||
|
||||
GstMPDSNode *
|
||||
gst_mpd_s_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_S_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_s_node_free (GstMPDSNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
||||
|
||||
GstMPDSNode *
|
||||
gst_mpd_s_node_clone (GstMPDSNode * s_node)
|
||||
{
|
||||
GstMPDSNode *clone = NULL;
|
||||
|
||||
if (s_node) {
|
||||
clone = gst_mpd_s_node_new ();
|
||||
clone->t = s_node->t;
|
||||
clone->d = s_node->d;
|
||||
clone->r = s_node->r;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSNODE_H__
|
||||
#define __GSTMPDSNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdnode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_S_NODE gst_mpd_s_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSNode2, gst_mpd_s_node, GST, MPD_S_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDSNode2 GstMPDSNode;
|
||||
typedef GstMPDSNode2Class GstMPDSNodeClass;
|
||||
|
||||
struct _GstMPDSNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
guint64 t;
|
||||
guint64 d;
|
||||
gint r;
|
||||
};
|
||||
|
||||
GstMPDSNode * gst_mpd_s_node_new (void);
|
||||
void gst_mpd_s_node_free (GstMPDSNode* self);
|
||||
|
||||
GstMPDSNode *gst_mpd_s_node_clone (GstMPDSNode * pointer);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSNODE_H__ */
|
|
@ -0,0 +1,109 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsubrepresentationnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSubRepresentationNode2, gst_mpd_sub_representation_node,
|
||||
GST_TYPE_MPD_REPRESENTATION_BASE_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_sub_representation_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDSubRepresentationNode *self = GST_MPD_SUB_REPRESENTATION_NODE (object);
|
||||
|
||||
if (self->dependencyLevel)
|
||||
xmlFree (self->dependencyLevel);
|
||||
g_strfreev (self->contentComponent);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_sub_representation_node_parent_class)->finalize
|
||||
(object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_sub_representation_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
gchar *value = NULL;
|
||||
xmlNodePtr sub_representation_xml_node = NULL;
|
||||
GstMPDSubRepresentationNode *self = GST_MPD_SUB_REPRESENTATION_NODE (node);
|
||||
|
||||
sub_representation_xml_node =
|
||||
xmlNewNode (NULL, (xmlChar *) "SubRepresentation");
|
||||
|
||||
gst_xml_helper_set_prop_uint (sub_representation_xml_node, "level",
|
||||
self->level);
|
||||
|
||||
gst_xml_helper_set_prop_uint_vector_type (sub_representation_xml_node,
|
||||
"dependencyLevel", self->dependencyLevel, self->dependencyLevel_size);
|
||||
|
||||
gst_xml_helper_set_prop_uint (sub_representation_xml_node, "bandwidth",
|
||||
self->level);
|
||||
|
||||
if (self->contentComponent) {
|
||||
value = g_strjoinv (" ", self->contentComponent);
|
||||
gst_xml_helper_set_prop_string (sub_representation_xml_node,
|
||||
"contentComponent", value);
|
||||
g_free (value);
|
||||
}
|
||||
|
||||
return sub_representation_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_sub_representation_node_class_init (GstMPDSubRepresentationNodeClass *
|
||||
klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_sub_representation_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_sub_representation_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_sub_representation_node_init (GstMPDSubRepresentationNode * self)
|
||||
{
|
||||
self->level = 0;
|
||||
self->dependencyLevel = NULL;
|
||||
self->dependencyLevel_size = 0;
|
||||
self->bandwidth = 0;
|
||||
self->contentComponent = NULL;
|
||||
}
|
||||
|
||||
GstMPDSubRepresentationNode *
|
||||
gst_mpd_sub_representation_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_SUB_REPRESENTATION_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_sub_representation_node_free (GstMPDSubRepresentationNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSUBREPRESENTATIONNODE_H__
|
||||
#define __GSTMPDSUBREPRESENTATIONNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdhelper.h"
|
||||
#include "gstmpdrepresentationbasenode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_SUB_REPRESENTATION_NODE gst_mpd_sub_representation_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSubRepresentationNode2, gst_mpd_sub_representation_node, GST, MPD_SUB_REPRESENTATION_NODE, GstMPDRepresentationBaseNode)
|
||||
|
||||
typedef GstMPDSubRepresentationNode2 GstMPDSubRepresentationNode;
|
||||
typedef GstMPDSubRepresentationNode2Class GstMPDSubRepresentationNodeClass;
|
||||
|
||||
struct _GstMPDSubRepresentationNode2
|
||||
{
|
||||
GstMPDRepresentationBaseNode parent_instance;
|
||||
/* RepresentationBase extension */
|
||||
guint level;
|
||||
guint *dependencyLevel; /* UIntVectorType */
|
||||
guint dependencyLevel_size; /* size of "dependencyLevel" array */
|
||||
guint bandwidth;
|
||||
gchar **contentComponent; /* StringVectorType */
|
||||
};
|
||||
|
||||
GstMPDSubRepresentationNode * gst_mpd_sub_representation_node_new (void);
|
||||
void gst_mpd_sub_representation_node_free (GstMPDSubRepresentationNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSUBREPRESENTATIONNODE_H__ */
|
|
@ -0,0 +1,88 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdsubsetnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDSubsetNode2, gst_mpd_subset_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_subset_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDSubsetNode *self = GST_MPD_SUBSET_NODE (object);
|
||||
|
||||
if (self->contains)
|
||||
xmlFree (self->contains);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_subset_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_subset_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr subset_xml_node = NULL;
|
||||
GstMPDSubsetNode *self = GST_MPD_SUBSET_NODE (node);
|
||||
|
||||
subset_xml_node = xmlNewNode (NULL, (xmlChar *) "Subset");
|
||||
|
||||
if (self->contains)
|
||||
gst_xml_helper_set_prop_uint_vector_type (subset_xml_node, "contains",
|
||||
self->contains, self->contains_size);
|
||||
|
||||
return subset_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_subset_node_class_init (GstMPDSubsetNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_subset_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_subset_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_subset_node_init (GstMPDSubsetNode * self)
|
||||
{
|
||||
self->contains_size = 0;
|
||||
self->contains = NULL;
|
||||
}
|
||||
|
||||
GstMPDSubsetNode *
|
||||
gst_mpd_subset_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_SUBSET_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_subset_node_free (GstMPDSubsetNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDSUBSETNODE_H__
|
||||
#define __GSTMPDSUBSETNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdnode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_SUBSET_NODE gst_mpd_subset_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDSubsetNode2, gst_mpd_subset_node, GST, MPD_SUBSET_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDSubsetNode2 GstMPDSubsetNode;
|
||||
typedef GstMPDSubsetNode2Class GstMPDSubsetNodeClass;
|
||||
|
||||
struct _GstMPDSubsetNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
guint *contains; /* UIntVectorType */
|
||||
guint contains_size; /* size of the "contains" array */
|
||||
};
|
||||
|
||||
struct _GstMPDSubsetNodeClass {
|
||||
GstMPDNodeClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GstMPDSubsetNode * gst_mpd_subset_node_new (void);
|
||||
void gst_mpd_subset_node_free (GstMPDSubsetNode* self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDSUBSETNODE_H__ */
|
|
@ -0,0 +1,110 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdurltypenode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDURLTypeNode2, gst_mpd_url_type_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_url_type_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDURLTypeNode *self = GST_MPD_URL_TYPE_NODE (object);
|
||||
|
||||
if (self->sourceURL)
|
||||
xmlFree (self->sourceURL);
|
||||
g_slice_free (GstXMLRange, self->range);
|
||||
g_free (self->node_name);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_url_type_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_url_type_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr url_type_xml_node = NULL;
|
||||
GstMPDURLTypeNode *self = GST_MPD_URL_TYPE_NODE (node);
|
||||
|
||||
url_type_xml_node = xmlNewNode (NULL, (xmlChar *) self->node_name);
|
||||
|
||||
gst_xml_helper_set_prop_string (url_type_xml_node, "sourceURL",
|
||||
self->sourceURL);
|
||||
gst_xml_helper_set_prop_range (url_type_xml_node, "range", self->range);
|
||||
|
||||
return url_type_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_url_type_node_class_init (GstMPDURLTypeNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_url_type_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_url_type_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_url_type_node_init (GstMPDURLTypeNode * self)
|
||||
{
|
||||
self->node_name = NULL;
|
||||
self->sourceURL = NULL;
|
||||
self->range = NULL;
|
||||
}
|
||||
|
||||
GstMPDURLTypeNode *
|
||||
gst_mpd_url_type_node_new (const gchar * name)
|
||||
{
|
||||
GstMPDURLTypeNode *self = g_object_new (GST_TYPE_MPD_URL_TYPE_NODE, NULL);
|
||||
self->node_name = g_strdup (name);
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_url_type_node_free (GstMPDURLTypeNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
||||
|
||||
GstMPDURLTypeNode *
|
||||
gst_mpd_url_type_node_clone (GstMPDURLTypeNode * url)
|
||||
{
|
||||
|
||||
GstMPDURLTypeNode *clone = NULL;
|
||||
|
||||
if (url) {
|
||||
clone = gst_mpd_url_type_node_new (url->node_name);
|
||||
if (url->sourceURL) {
|
||||
clone->sourceURL = xmlMemStrdup (url->sourceURL);
|
||||
}
|
||||
clone->range = gst_xml_helper_clone_range (url->range);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDURLTYPENODE_H__
|
||||
#define __GSTMPDURLTYPENODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdnode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_URL_TYPE_NODE gst_mpd_url_type_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDURLTypeNode2, gst_mpd_url_type_node, GST, MPD_URL_TYPE_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDURLTypeNode2 GstMPDURLTypeNode;
|
||||
typedef GstMPDURLTypeNode2Class GstMPDURLTypeNodeClass;
|
||||
|
||||
struct _GstMPDURLTypeNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
gchar* node_name;
|
||||
gchar *sourceURL;
|
||||
GstXMLRange *range;
|
||||
};
|
||||
|
||||
GstMPDURLTypeNode * gst_mpd_url_type_node_new (const gchar* name);
|
||||
void gst_mpd_url_type_node_free (GstMPDURLTypeNode* self);
|
||||
|
||||
GstMPDURLTypeNode *gst_mpd_url_type_node_clone (GstMPDURLTypeNode * url);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDURLTYPENODE_H__ */
|
|
@ -0,0 +1,140 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include "gstmpdutctimingnode.h"
|
||||
#include "gstmpdparser.h"
|
||||
|
||||
G_DEFINE_TYPE (GstMPDUTCTimingNode2, gst_mpd_utctiming_node, GST_TYPE_MPD_NODE);
|
||||
|
||||
static const struct GstMPDUTCTimingMethod gst_mpd_utctiming_methods[] = {
|
||||
{"urn:mpeg:dash:utc:ntp:2014", GST_MPD_UTCTIMING_TYPE_NTP},
|
||||
{"urn:mpeg:dash:utc:sntp:2014", GST_MPD_UTCTIMING_TYPE_SNTP},
|
||||
{"urn:mpeg:dash:utc:http-head:2014", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
|
||||
{"urn:mpeg:dash:utc:http-xsdate:2014", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
|
||||
{"urn:mpeg:dash:utc:http-iso:2014", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
|
||||
{"urn:mpeg:dash:utc:http-ntp:2014", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
|
||||
{"urn:mpeg:dash:utc:direct:2014", GST_MPD_UTCTIMING_TYPE_DIRECT},
|
||||
/*
|
||||
* Early working drafts used the :2012 namespace and this namespace is
|
||||
* used by some DASH packagers. To work-around these packagers, we also
|
||||
* accept the early draft scheme names.
|
||||
*/
|
||||
{"urn:mpeg:dash:utc:ntp:2012", GST_MPD_UTCTIMING_TYPE_NTP},
|
||||
{"urn:mpeg:dash:utc:sntp:2012", GST_MPD_UTCTIMING_TYPE_SNTP},
|
||||
{"urn:mpeg:dash:utc:http-head:2012", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
|
||||
{"urn:mpeg:dash:utc:http-xsdate:2012", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
|
||||
{"urn:mpeg:dash:utc:http-iso:2012", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
|
||||
{"urn:mpeg:dash:utc:http-ntp:2012", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
|
||||
{"urn:mpeg:dash:utc:direct:2012", GST_MPD_UTCTIMING_TYPE_DIRECT},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
/* GObject VMethods */
|
||||
|
||||
static void
|
||||
gst_mpd_utctiming_node_finalize (GObject * object)
|
||||
{
|
||||
GstMPDUTCTimingNode *self = GST_MPD_UTCTIMING_NODE (object);
|
||||
|
||||
g_strfreev (self->urls);
|
||||
|
||||
G_OBJECT_CLASS (gst_mpd_utctiming_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Base class */
|
||||
|
||||
static xmlNodePtr
|
||||
gst_mpd_utc_timing_get_xml_node (GstMPDNode * node)
|
||||
{
|
||||
xmlNodePtr utc_timing_xml_node = NULL;
|
||||
gchar *value = NULL;
|
||||
GstMPDUTCTimingNode *self = GST_MPD_UTCTIMING_NODE (node);
|
||||
|
||||
utc_timing_xml_node = xmlNewNode (NULL, (xmlChar *) "UTCTiming");
|
||||
|
||||
if (self->method) {
|
||||
gst_xml_helper_set_prop_string (utc_timing_xml_node, "schemeiduri",
|
||||
(gchar *) gst_mpd_utctiming_get_scheme_id_uri (self->method));
|
||||
}
|
||||
if (self->urls) {
|
||||
value = g_strjoinv (" ", self->urls);
|
||||
gst_xml_helper_set_prop_string (utc_timing_xml_node, "value", value);
|
||||
g_free (value);
|
||||
}
|
||||
|
||||
return utc_timing_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_utctiming_node_class_init (GstMPDUTCTimingNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GstMPDNodeClass *m_klass;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
m_klass = GST_MPD_NODE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_mpd_utctiming_node_finalize;
|
||||
|
||||
m_klass->get_xml_node = gst_mpd_utc_timing_get_xml_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mpd_utctiming_node_init (GstMPDUTCTimingNode * self)
|
||||
{
|
||||
self->method = 0;
|
||||
self->urls = NULL;
|
||||
}
|
||||
|
||||
GstMPDUTCTimingNode *
|
||||
gst_mpd_utctiming_node_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_MPD_UTCTIMING_NODE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mpd_utctiming_node_free (GstMPDUTCTimingNode * self)
|
||||
{
|
||||
if (self)
|
||||
gst_object_unref (self);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gst_mpd_utctiming_get_scheme_id_uri (GstMPDUTCTimingType type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; gst_mpd_utctiming_methods[i].name; ++i) {
|
||||
if (type == gst_mpd_utctiming_methods[i].method)
|
||||
return gst_mpd_utctiming_methods[i].name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GstMPDUTCTimingType
|
||||
gst_mpd_utctiming_get_method (gchar * schemeIDURI)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; gst_mpd_utctiming_methods[i].name; ++i) {
|
||||
if (g_ascii_strncasecmp (gst_mpd_utctiming_methods[i].name,
|
||||
schemeIDURI, strlen (gst_mpd_utctiming_methods[i].name)) == 0)
|
||||
return gst_mpd_utctiming_methods[i].method;
|
||||
}
|
||||
return GST_MPD_UTCTIMING_TYPE_UNKNOWN;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GSTMPDUTCTIMINGNODE_H__
|
||||
#define __GSTMPDUTCTIMINGNODE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstmpdnode.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MPD_UTCTIMING_NODE gst_mpd_utctiming_node_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (GstMPDUTCTimingNode2, gst_mpd_utctiming_node, GST, MPD_UTCTIMING_NODE, GstMPDNode)
|
||||
|
||||
typedef GstMPDUTCTimingNode2 GstMPDUTCTimingNode;
|
||||
typedef GstMPDUTCTimingNode2Class GstMPDUTCTimingNodeClass;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_MPD_UTCTIMING_TYPE_UNKNOWN = 0x00,
|
||||
GST_MPD_UTCTIMING_TYPE_NTP = 0x01,
|
||||
GST_MPD_UTCTIMING_TYPE_SNTP = 0x02,
|
||||
GST_MPD_UTCTIMING_TYPE_HTTP_HEAD = 0x04,
|
||||
GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE = 0x08,
|
||||
GST_MPD_UTCTIMING_TYPE_HTTP_ISO = 0x10,
|
||||
GST_MPD_UTCTIMING_TYPE_HTTP_NTP = 0x20,
|
||||
GST_MPD_UTCTIMING_TYPE_DIRECT = 0x40
|
||||
} GstMPDUTCTimingType;
|
||||
|
||||
struct GstMPDUTCTimingMethod
|
||||
{
|
||||
const gchar *name;
|
||||
GstMPDUTCTimingType method;
|
||||
};
|
||||
|
||||
struct _GstMPDUTCTimingNode2
|
||||
{
|
||||
GstObject parent_instance;
|
||||
GstMPDUTCTimingType method;
|
||||
/* NULL terminated array of strings */
|
||||
gchar **urls;
|
||||
/* TODO add missing fields such as weight etc.*/
|
||||
};
|
||||
|
||||
|
||||
GstMPDUTCTimingNode * gst_mpd_utctiming_node_new (void);
|
||||
void gst_mpd_utctiming_node_free (GstMPDUTCTimingNode* self);
|
||||
|
||||
const gchar* gst_mpd_utctiming_get_scheme_id_uri (GstMPDUTCTimingType type);
|
||||
GstMPDUTCTimingType gst_mpd_utctiming_get_method (gchar* schemeIDURI);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTMPDUTCTIMINGNODE_H__ */
|
1259
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstxmlhelper.c
Normal file
1259
subprojects/gst-plugins-good/ext/adaptivedemux2/dash/gstxmlhelper.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,133 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
* Author: Stéphane Cerveau <scerveau@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __GST_XMLHELPER_H__
|
||||
#define __GST_XMLHELPER_H__
|
||||
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <gst/gst.h>
|
||||
#include "gstdash_debug.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstXMLRange GstXMLRange;
|
||||
typedef struct _GstXMLRatio GstXMLRatio;
|
||||
typedef struct _GstXMLFrameRate GstXMLFrameRate;
|
||||
typedef struct _GstXMLConditionalUintType GstXMLConditionalUintType;
|
||||
|
||||
struct _GstXMLRange
|
||||
{
|
||||
guint64 first_byte_pos;
|
||||
guint64 last_byte_pos;
|
||||
};
|
||||
|
||||
struct _GstXMLRatio
|
||||
{
|
||||
guint num;
|
||||
guint den;
|
||||
};
|
||||
|
||||
struct _GstXMLFrameRate
|
||||
{
|
||||
guint num;
|
||||
guint den;
|
||||
};
|
||||
|
||||
struct _GstXMLConditionalUintType
|
||||
{
|
||||
gboolean flag;
|
||||
guint value;
|
||||
};
|
||||
|
||||
GstXMLRange *gst_xml_helper_clone_range (GstXMLRange * range);
|
||||
GstXMLRatio *gst_xml_helper_clone_ratio (GstXMLRatio * ratio);
|
||||
GstXMLFrameRate *gst_xml_helper_clone_frame_rate (GstXMLFrameRate * frameRate);
|
||||
|
||||
/* XML property get method */
|
||||
gboolean gst_xml_helper_get_prop_validated_string (xmlNode * a_node,
|
||||
const gchar * property_name, gchar ** property_value,
|
||||
gboolean (*validator) (const char *));
|
||||
gboolean gst_xml_helper_get_prop_string (xmlNode * a_node,
|
||||
const gchar * property_name, gchar ** property_value);
|
||||
gboolean gst_xml_helper_get_prop_string_stripped (xmlNode * a_node,
|
||||
const gchar * property_name, gchar ** property_value);
|
||||
gboolean gst_xml_helper_get_ns_prop_string (xmlNode * a_node,
|
||||
const gchar * ns_name, const gchar * property_name,
|
||||
gchar ** property_value);
|
||||
gboolean gst_xml_helper_get_prop_string_vector_type (xmlNode * a_node,
|
||||
const gchar * property_name, gchar *** property_value);
|
||||
gboolean gst_xml_helper_get_prop_signed_integer (xmlNode * a_node,
|
||||
const gchar * property_name, gint default_val, gint * property_value);
|
||||
gboolean gst_xml_helper_get_prop_unsigned_integer (xmlNode * a_node,
|
||||
const gchar * property_name, guint default_val, guint * property_value);
|
||||
gboolean gst_xml_helper_get_prop_unsigned_integer_64 (xmlNode *
|
||||
a_node, const gchar * property_name, guint64 default_val,
|
||||
guint64 * property_value);
|
||||
gboolean gst_xml_helper_get_prop_uint_vector_type (xmlNode * a_node,
|
||||
const gchar * property_name, guint ** property_value, guint * value_size);
|
||||
gboolean gst_xml_helper_get_prop_double (xmlNode * a_node,
|
||||
const gchar * property_name, gdouble * property_value);
|
||||
gboolean gst_xml_helper_get_prop_boolean (xmlNode * a_node,
|
||||
const gchar * property_name, gboolean default_val,
|
||||
gboolean * property_value);
|
||||
gboolean gst_xml_helper_get_prop_range (xmlNode * a_node,
|
||||
const gchar * property_name, GstXMLRange ** property_value);
|
||||
gboolean gst_xml_helper_get_prop_ratio (xmlNode * a_node,
|
||||
const gchar * property_name, GstXMLRatio ** property_value);
|
||||
gboolean gst_xml_helper_get_prop_framerate (xmlNode * a_node,
|
||||
const gchar * property_name, GstXMLFrameRate ** property_value);
|
||||
gboolean gst_xml_helper_get_prop_cond_uint (xmlNode * a_node,
|
||||
const gchar * property_name, GstXMLConditionalUintType ** property_value);
|
||||
gboolean gst_xml_helper_get_prop_dateTime (xmlNode * a_node,
|
||||
const gchar * property_name, GstDateTime ** property_value);
|
||||
gboolean gst_xml_helper_get_prop_duration (xmlNode * a_node,
|
||||
const gchar * property_name, guint64 default_value,
|
||||
guint64 * property_value);
|
||||
gboolean gst_xml_helper_get_prop_string_no_whitespace (xmlNode * a_node,
|
||||
const gchar * property_name, gchar ** property_value);
|
||||
|
||||
/* XML node get method */
|
||||
gboolean gst_xml_helper_get_node_content (xmlNode * a_node,
|
||||
gchar ** content);
|
||||
gchar *gst_xml_helper_get_node_namespace (xmlNode * a_node,
|
||||
const gchar * prefix);
|
||||
gboolean gst_xml_helper_get_node_as_string (xmlNode * a_node,
|
||||
gchar ** content);
|
||||
|
||||
/* XML property set method */
|
||||
void gst_xml_helper_set_prop_string (xmlNodePtr node, const gchar * name, gchar* value);
|
||||
void gst_xml_helper_set_prop_boolean (xmlNodePtr node, const gchar * name, gboolean value);
|
||||
void gst_xml_helper_set_prop_int (xmlNodePtr root, const gchar * name, gint value);
|
||||
void gst_xml_helper_set_prop_uint (xmlNodePtr root, const gchar * name, guint value);
|
||||
void gst_xml_helper_set_prop_int64 (xmlNodePtr node, const gchar * name, gint64 value);
|
||||
void gst_xml_helper_set_prop_uint64 (xmlNodePtr node, const gchar * name, guint64 value);
|
||||
void gst_xml_helper_set_prop_uint_vector_type (xmlNode * a_node, const gchar * name, guint * value, guint value_size);
|
||||
void gst_xml_helper_set_prop_double (xmlNodePtr node, const gchar * name, gdouble value);
|
||||
void gst_xml_helper_set_prop_date_time (xmlNodePtr node, const gchar * name, GstDateTime* value);
|
||||
void gst_xml_helper_set_prop_duration (xmlNode * node, const gchar * name, guint64 value);
|
||||
void gst_xml_helper_set_prop_ratio (xmlNodePtr node, const gchar * name, GstXMLRatio* value);
|
||||
void gst_xml_helper_set_prop_framerate (xmlNodePtr node, const gchar * name, GstXMLFrameRate* value);
|
||||
void gst_xml_helper_set_prop_range (xmlNodePtr node, const gchar * name, GstXMLRange* value);
|
||||
void gst_xml_helper_set_prop_cond_uint (xmlNode * a_node, const gchar * property_name, GstXMLConditionalUintType * cond);
|
||||
void gst_xml_helper_set_content (xmlNodePtr node, gchar * content);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_XMLHELPER_H__ */
|
1004
subprojects/gst-plugins-good/ext/adaptivedemux2/downloadhelper.c
Normal file
1004
subprojects/gst-plugins-good/ext/adaptivedemux2/downloadhelper.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,67 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021-2022 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* downloadhelper.h:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Youshould 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 <glib.h>
|
||||
|
||||
#include "gstadaptivedemuxutils.h"
|
||||
#include "downloadrequest.h"
|
||||
|
||||
#ifndef __DOWNLOADHELPER_H__
|
||||
#define __DOWNLOADHELPER_H__
|
||||
|
||||
G_BEGIN_DECLS typedef struct DownloadHelper DownloadHelper;
|
||||
typedef enum DownloadFlags DownloadFlags;
|
||||
|
||||
#define HTTP_STATUS_IS_SUCCESSFUL(s) ((s) >= 200 && (s) < 300)
|
||||
|
||||
enum DownloadFlags
|
||||
{
|
||||
DOWNLOAD_FLAG_NONE = 0,
|
||||
DOWNLOAD_FLAG_COMPRESS = (1 << 0),
|
||||
DOWNLOAD_FLAG_FORCE_REFRESH = (1 << 1),
|
||||
DOWNLOAD_FLAG_HEADERS_ONLY = (1 << 2),
|
||||
DOWNLOAD_FLAG_BLOCKING = (1 << 3),
|
||||
};
|
||||
|
||||
DownloadHelper *downloadhelper_new (GstAdaptiveDemuxClock *clock);
|
||||
|
||||
gboolean downloadhelper_start (DownloadHelper * dh);
|
||||
void downloadhelper_stop (DownloadHelper * dh);
|
||||
|
||||
void downloadhelper_free (DownloadHelper * dh);
|
||||
|
||||
void downloadhelper_set_referer (DownloadHelper * dh, const gchar *referer);
|
||||
void downloadhelper_set_user_agent (DownloadHelper * dh, const gchar *user_agent);
|
||||
void downloadhelper_set_cookies (DownloadHelper * dh, gchar **cookies);
|
||||
|
||||
gboolean downloadhelper_submit_request (DownloadHelper * dh,
|
||||
const gchar * referer, DownloadFlags flags, DownloadRequest * request,
|
||||
GError ** err);
|
||||
void downloadhelper_cancel_request (DownloadHelper * dh, DownloadRequest *request);
|
||||
|
||||
DownloadRequest *downloadhelper_fetch_uri (DownloadHelper * dh, const gchar * uri,
|
||||
const gchar * referer, DownloadFlags flags, GError ** err);
|
||||
DownloadRequest *downloadhelper_fetch_uri_range (DownloadHelper * dh,
|
||||
const gchar * uri, const gchar * referer, DownloadFlags flags,
|
||||
gint64 range_start, gint64 range_end, GError ** err);
|
||||
|
||||
G_END_DECLS
|
||||
#endif
|
|
@ -0,0 +1,396 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
|
||||
* Copyright (C) 2021-2022 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* gstrequest.c:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/base/gsttypefindhelper.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include "downloadrequest.h"
|
||||
|
||||
typedef struct _DownloadRequestPrivate DownloadRequestPrivate;
|
||||
|
||||
struct _DownloadRequestPrivate
|
||||
{
|
||||
DownloadRequest request;
|
||||
|
||||
GstBuffer *buffer;
|
||||
GstCaps *caps;
|
||||
GRecMutex lock;
|
||||
|
||||
DownloadRequestEventCallback completion_cb;
|
||||
DownloadRequestEventCallback cancellation_cb;
|
||||
DownloadRequestEventCallback error_cb;
|
||||
DownloadRequestEventCallback progress_cb;
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
#define DOWNLOAD_REQUEST_PRIVATE(frag) ((DownloadRequestPrivate *)(frag))
|
||||
|
||||
static void download_request_free (DownloadRequest * request);
|
||||
|
||||
DownloadRequest *
|
||||
download_request_new (void)
|
||||
{
|
||||
DownloadRequest *request =
|
||||
(DownloadRequest *) g_slice_new0 (DownloadRequestPrivate);
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
|
||||
g_atomic_int_set (&request->ref_count, 1);
|
||||
|
||||
g_rec_mutex_init (&priv->lock);
|
||||
|
||||
priv->buffer = NULL;
|
||||
|
||||
request->state = DOWNLOAD_REQUEST_STATE_UNSENT;
|
||||
request->status_code = 0;
|
||||
|
||||
request->download_request_time = GST_CLOCK_TIME_NONE;
|
||||
request->download_start_time = GST_CLOCK_TIME_NONE;
|
||||
request->download_end_time = GST_CLOCK_TIME_NONE;
|
||||
request->headers = NULL;
|
||||
|
||||
return (DownloadRequest *) (request);
|
||||
}
|
||||
|
||||
DownloadRequest *
|
||||
download_request_new_uri (const gchar * uri)
|
||||
{
|
||||
DownloadRequest *request = download_request_new ();
|
||||
|
||||
request->uri = g_strdup (uri);
|
||||
request->range_start = 0;
|
||||
request->range_end = -1;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
DownloadRequest *
|
||||
download_request_new_uri_range (const gchar * uri, gint64 range_start,
|
||||
gint64 range_end)
|
||||
{
|
||||
DownloadRequest *request = download_request_new ();
|
||||
|
||||
request->uri = g_strdup (uri);
|
||||
request->range_start = range_start;
|
||||
request->range_end = range_end;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
static void
|
||||
download_request_free (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
|
||||
g_free (request->uri);
|
||||
g_free (request->redirect_uri);
|
||||
if (request->headers) {
|
||||
gst_structure_free (request->headers);
|
||||
request->headers = NULL;
|
||||
}
|
||||
|
||||
if (priv->buffer != NULL) {
|
||||
gst_buffer_unref (priv->buffer);
|
||||
priv->buffer = NULL;
|
||||
}
|
||||
|
||||
if (priv->caps != NULL) {
|
||||
gst_caps_unref (priv->caps);
|
||||
priv->caps = NULL;
|
||||
}
|
||||
|
||||
g_rec_mutex_clear (&priv->lock);
|
||||
|
||||
g_slice_free1 (sizeof (DownloadRequestPrivate), priv);
|
||||
}
|
||||
|
||||
void
|
||||
download_request_set_callbacks (DownloadRequest * request,
|
||||
DownloadRequestEventCallback on_completion,
|
||||
DownloadRequestEventCallback on_error,
|
||||
DownloadRequestEventCallback on_cancellation,
|
||||
DownloadRequestEventCallback on_progress, void *cb_data)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
priv->completion_cb = on_completion;
|
||||
priv->error_cb = on_error;
|
||||
priv->cancellation_cb = on_cancellation;
|
||||
priv->progress_cb = on_progress;
|
||||
priv->cb_data = cb_data;
|
||||
|
||||
request->send_progress = (on_progress != NULL);
|
||||
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/* Called with request lock held */
|
||||
void
|
||||
download_request_despatch_progresss (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
if (priv->progress_cb)
|
||||
priv->progress_cb (request, request->state, priv->cb_data);
|
||||
}
|
||||
|
||||
/* Called with request lock held */
|
||||
void
|
||||
download_request_despatch_completion (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
switch (request->state) {
|
||||
case DOWNLOAD_REQUEST_STATE_UNSENT:
|
||||
if (priv->cancellation_cb)
|
||||
priv->cancellation_cb (request, request->state, priv->cb_data);
|
||||
break;
|
||||
case DOWNLOAD_REQUEST_STATE_COMPLETE:
|
||||
if (priv->completion_cb)
|
||||
priv->completion_cb (request, request->state, priv->cb_data);
|
||||
break;
|
||||
case DOWNLOAD_REQUEST_STATE_ERROR:
|
||||
if (priv->error_cb)
|
||||
priv->error_cb (request, request->state, priv->cb_data);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DownloadRequest *
|
||||
download_request_ref (DownloadRequest * request)
|
||||
{
|
||||
g_return_val_if_fail (request != NULL, NULL);
|
||||
g_atomic_int_inc (&request->ref_count);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
void
|
||||
download_request_unref (DownloadRequest * request)
|
||||
{
|
||||
g_return_if_fail (request != NULL);
|
||||
|
||||
if (g_atomic_int_dec_and_test (&request->ref_count)) {
|
||||
download_request_free (request);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
download_request_lock (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
}
|
||||
|
||||
void
|
||||
download_request_unlock (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
GstBuffer *
|
||||
download_request_take_buffer (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
GstBuffer *buffer = NULL;
|
||||
|
||||
g_return_val_if_fail (request != NULL, NULL);
|
||||
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
|
||||
if (request->state != DOWNLOAD_REQUEST_STATE_LOADING
|
||||
&& request->state != DOWNLOAD_REQUEST_STATE_COMPLETE) {
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = priv->buffer;
|
||||
priv->buffer = NULL;
|
||||
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void
|
||||
download_request_set_uri (DownloadRequest * request, const gchar * uri,
|
||||
gint64 range_start, gint64 range_end)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
|
||||
g_assert (request->in_use == FALSE);
|
||||
|
||||
if (request->uri != uri) {
|
||||
g_free (request->uri);
|
||||
request->uri = g_strdup (uri);
|
||||
}
|
||||
|
||||
g_free (request->redirect_uri);
|
||||
request->redirect_uri = NULL;
|
||||
request->redirect_permanent = FALSE;
|
||||
|
||||
request->range_start = range_start;
|
||||
request->range_end = range_end;
|
||||
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
void
|
||||
download_request_reset (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
g_assert (request->in_use == FALSE);
|
||||
request->state = DOWNLOAD_REQUEST_STATE_UNSENT;
|
||||
|
||||
if (request->headers) {
|
||||
gst_structure_free (request->headers);
|
||||
request->headers = NULL;
|
||||
}
|
||||
|
||||
if (priv->buffer != NULL) {
|
||||
gst_buffer_unref (priv->buffer);
|
||||
priv->buffer = NULL;
|
||||
}
|
||||
|
||||
if (priv->caps != NULL) {
|
||||
gst_caps_unref (priv->caps);
|
||||
priv->caps = NULL;
|
||||
}
|
||||
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/* Called when the request is submitted, to clear any settings from a previous
|
||||
* download */
|
||||
void
|
||||
download_request_begin_download (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
|
||||
g_return_if_fail (request != NULL);
|
||||
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
|
||||
if (priv->buffer != NULL) {
|
||||
gst_buffer_unref (priv->buffer);
|
||||
priv->buffer = NULL;
|
||||
}
|
||||
|
||||
if (request->headers) {
|
||||
gst_structure_free (request->headers);
|
||||
request->headers = NULL;
|
||||
}
|
||||
|
||||
if (priv->caps != NULL) {
|
||||
gst_caps_unref (priv->caps);
|
||||
priv->caps = NULL;
|
||||
}
|
||||
|
||||
request->content_length = 0;
|
||||
request->content_received = 0;
|
||||
|
||||
request->download_request_time = GST_CLOCK_TIME_NONE;
|
||||
request->download_start_time = GST_CLOCK_TIME_NONE;
|
||||
request->download_end_time = GST_CLOCK_TIME_NONE;
|
||||
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
void
|
||||
download_request_set_caps (DownloadRequest * request, GstCaps * caps)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
g_return_if_fail (request != NULL);
|
||||
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
gst_caps_replace (&priv->caps, caps);
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
download_request_get_caps (DownloadRequest * request)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
GstCaps *caps;
|
||||
|
||||
g_return_val_if_fail (request != NULL, NULL);
|
||||
|
||||
if (request->state != DOWNLOAD_REQUEST_STATE_LOADING
|
||||
&& request->state != DOWNLOAD_REQUEST_STATE_COMPLETE)
|
||||
return NULL;
|
||||
|
||||
g_rec_mutex_lock (&priv->lock);
|
||||
if (priv->caps == NULL) {
|
||||
guint64 offset, offset_end;
|
||||
|
||||
/* FIXME: This is currently necessary as typefinding only
|
||||
* works with 0 offsets... need to find a better way to
|
||||
* do that */
|
||||
offset = GST_BUFFER_OFFSET (priv->buffer);
|
||||
offset_end = GST_BUFFER_OFFSET_END (priv->buffer);
|
||||
GST_BUFFER_OFFSET (priv->buffer) = GST_BUFFER_OFFSET_NONE;
|
||||
GST_BUFFER_OFFSET_END (priv->buffer) = GST_BUFFER_OFFSET_NONE;
|
||||
priv->caps = gst_type_find_helper_for_buffer (NULL, priv->buffer, NULL);
|
||||
GST_BUFFER_OFFSET (priv->buffer) = offset;
|
||||
GST_BUFFER_OFFSET_END (priv->buffer) = offset_end;
|
||||
}
|
||||
|
||||
caps = gst_caps_ref (priv->caps);
|
||||
g_rec_mutex_unlock (&priv->lock);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
gboolean
|
||||
download_request_add_buffer (DownloadRequest * request, GstBuffer * buffer)
|
||||
{
|
||||
DownloadRequestPrivate *priv = DOWNLOAD_REQUEST_PRIVATE (request);
|
||||
|
||||
g_return_val_if_fail (request != NULL, FALSE);
|
||||
g_return_val_if_fail (buffer != NULL, FALSE);
|
||||
|
||||
if (request->state == DOWNLOAD_REQUEST_STATE_COMPLETE) {
|
||||
GST_WARNING ("Download request is completed, could not add more buffers");
|
||||
gst_buffer_unref (buffer);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Adding new buffer %" GST_PTR_FORMAT " to the request data",
|
||||
buffer);
|
||||
|
||||
request->content_received += gst_buffer_get_size (buffer);
|
||||
|
||||
/* We steal the buffers you pass in */
|
||||
if (priv->buffer == NULL)
|
||||
priv->buffer = buffer;
|
||||
else
|
||||
priv->buffer = gst_buffer_append (priv->buffer, buffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
|
||||
* Copyright (C) 2021-2022 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* downloadrequest.h:
|
||||
*
|
||||
* 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 __DOWNLOAD_REQUEST_H__
|
||||
#define __DOWNLOAD_REQUEST_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define DOWNLOAD_REQUEST(obj) ((DownloadRequest *)(obj))
|
||||
|
||||
typedef struct _DownloadRequest DownloadRequest;
|
||||
typedef enum _DownloadRequestState DownloadRequestState;
|
||||
|
||||
typedef void (*DownloadRequestEventCallback) (DownloadRequest *request, DownloadRequestState state, void *cb_data);
|
||||
|
||||
enum _DownloadRequestState {
|
||||
DOWNLOAD_REQUEST_STATE_UNSENT,
|
||||
DOWNLOAD_REQUEST_STATE_OPEN, /* Request sent, but no response yet */
|
||||
DOWNLOAD_REQUEST_STATE_HEADERS_RECEIVED, /* Response headers received, awaiting body */
|
||||
DOWNLOAD_REQUEST_STATE_LOADING, /* Content loading in progress */
|
||||
DOWNLOAD_REQUEST_STATE_COMPLETE, /* Request processing finished successfully - check status_code for completion 200-399 codes */
|
||||
DOWNLOAD_REQUEST_STATE_ERROR, /* Request generated an http error - check status_code */
|
||||
};
|
||||
|
||||
struct _DownloadRequest
|
||||
{
|
||||
gint ref_count;
|
||||
|
||||
gboolean in_use; /* TRUE if this request is being serviced */
|
||||
gboolean send_progress; /* TRUE if this request wants progress events */
|
||||
|
||||
DownloadRequestState state;
|
||||
guint status_code; /* HTTP status code */
|
||||
|
||||
/* Request parameters */
|
||||
gchar * uri; /* URI of the request */
|
||||
gint64 range_start;
|
||||
gint64 range_end;
|
||||
|
||||
/* possibly populated during a download */
|
||||
gchar * redirect_uri; /* Redirect target if any */
|
||||
gboolean redirect_permanent; /* If the redirect is permanent */
|
||||
|
||||
GstStructure *headers; /* HTTP request/response headers */
|
||||
guint64 content_length; /* Response content length, if known (or 0) */
|
||||
guint64 content_received; /* Response content received so far */
|
||||
|
||||
guint64 download_request_time; /* Epoch time when the download started */
|
||||
guint64 download_start_time; /* Epoch time when the first data for the download arrived */
|
||||
guint64 download_end_time; /* Epoch time when the download finished */
|
||||
};
|
||||
|
||||
void download_request_set_uri (DownloadRequest *request, const gchar *uri,
|
||||
gint64 range_start, gint64 range_end);
|
||||
|
||||
/* Reset the request state back to UNSENT and clear any stored info. The request must not be in use */
|
||||
void download_request_reset (DownloadRequest *request);
|
||||
|
||||
void download_request_begin_download (DownloadRequest *request);
|
||||
|
||||
void download_request_set_caps (DownloadRequest * request, GstCaps * caps);
|
||||
|
||||
GstCaps * download_request_get_caps (DownloadRequest * request);
|
||||
|
||||
gboolean download_request_add_buffer (DownloadRequest *request, GstBuffer *buffer);
|
||||
GstBuffer * download_request_take_buffer (DownloadRequest *request);
|
||||
|
||||
DownloadRequest * download_request_new (void);
|
||||
DownloadRequest * download_request_new_uri (const gchar * uri);
|
||||
DownloadRequest * download_request_new_uri_range (const gchar * uri, gint64 range_start, gint64 range_end);
|
||||
|
||||
void download_request_set_callbacks (DownloadRequest *request,
|
||||
DownloadRequestEventCallback on_completion,
|
||||
DownloadRequestEventCallback on_error,
|
||||
DownloadRequestEventCallback on_cancellation,
|
||||
DownloadRequestEventCallback on_progress,
|
||||
void *cb_data);
|
||||
|
||||
DownloadRequest *download_request_ref (DownloadRequest *request);
|
||||
void download_request_unref (DownloadRequest *request);
|
||||
|
||||
void download_request_lock (DownloadRequest *request);
|
||||
void download_request_unlock (DownloadRequest *request);
|
||||
|
||||
void download_request_despatch_progresss (DownloadRequest *request);
|
||||
void download_request_despatch_completion (DownloadRequest *request);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __DOWNLOAD_REQUEST_H__ */
|
|
@ -0,0 +1,286 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2021-2022 Centricular Ltd
|
||||
* Author: Edward Hervey <edward@centricular.com>
|
||||
* Author: Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstadaptivedemux.h"
|
||||
#include "gstadaptivedemux-private.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
|
||||
#define GST_CAT_DEFAULT adaptivedemux2_debug
|
||||
|
||||
GstAdaptiveDemuxPeriod *
|
||||
gst_adaptive_demux_period_new (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstAdaptiveDemuxPeriod *period;
|
||||
|
||||
period = g_new0 (GstAdaptiveDemuxPeriod, 1);
|
||||
g_atomic_int_set (&period->ref_count, 1);
|
||||
|
||||
period->demux = demux;
|
||||
period->period_num = demux->priv->n_periods++;
|
||||
|
||||
g_queue_push_tail (demux->priv->periods, period);
|
||||
|
||||
return period;
|
||||
}
|
||||
|
||||
static void
|
||||
_demux_period_free (GstAdaptiveDemuxPeriod * period)
|
||||
{
|
||||
/* Disable and remove all streams and tracks. */
|
||||
g_list_free_full (period->streams, (GDestroyNotify) gst_object_unref);
|
||||
|
||||
/* Theoretically all tracks should have gone by now */
|
||||
GST_DEBUG ("Disabling and removing all tracks");
|
||||
g_list_free_full (period->tracks,
|
||||
(GDestroyNotify) gst_adaptive_demux_track_unref);
|
||||
|
||||
g_free (period);
|
||||
}
|
||||
|
||||
GstAdaptiveDemuxPeriod *
|
||||
gst_adaptive_demux_period_ref (GstAdaptiveDemuxPeriod * period)
|
||||
{
|
||||
g_return_val_if_fail (period != NULL, NULL);
|
||||
|
||||
GST_TRACE ("%p %d -> %d", period, period->ref_count, period->ref_count + 1);
|
||||
|
||||
g_atomic_int_inc (&period->ref_count);
|
||||
|
||||
return period;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_period_unref (GstAdaptiveDemuxPeriod * period)
|
||||
{
|
||||
g_return_if_fail (period != NULL);
|
||||
|
||||
GST_TRACE ("%p %d -> %d", period, period->ref_count, period->ref_count - 1);
|
||||
|
||||
if (g_atomic_int_dec_and_test (&period->ref_count)) {
|
||||
_demux_period_free (period);
|
||||
}
|
||||
}
|
||||
|
||||
static GstAdaptiveDemuxTrack *
|
||||
default_track_for_stream_type_locked (GstAdaptiveDemuxPeriod * period,
|
||||
GstStreamType stream_type)
|
||||
{
|
||||
GList *tmp;
|
||||
GstAdaptiveDemuxTrack *res = NULL, *select = NULL;
|
||||
|
||||
for (tmp = period->tracks; tmp; tmp = tmp->next) {
|
||||
GstAdaptiveDemuxTrack *cand = tmp->data;
|
||||
|
||||
if (cand->type == stream_type) {
|
||||
/* If selected, we're done */
|
||||
if (cand->selected)
|
||||
return cand;
|
||||
if (!select && cand->flags & GST_STREAM_FLAG_SELECT)
|
||||
res = select = cand;
|
||||
if (res == NULL)
|
||||
res = cand;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* called with TRACKS_LOCK taken */
|
||||
void
|
||||
gst_adaptive_demux_period_select_default_tracks (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemuxPeriod * period)
|
||||
{
|
||||
GstAdaptiveDemuxTrack *track;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "Picking a default selection");
|
||||
|
||||
/* Do initial selection (pick default for each type) */
|
||||
if ((track =
|
||||
default_track_for_stream_type_locked (period,
|
||||
GST_STREAM_TYPE_VIDEO))) {
|
||||
GST_DEBUG_OBJECT (demux, "Selecting default video track %s",
|
||||
track->stream_id);
|
||||
if (!track->selected) {
|
||||
changed = TRUE;
|
||||
track->selected = TRUE;
|
||||
gst_pad_set_active (track->sinkpad, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if ((track =
|
||||
default_track_for_stream_type_locked (period,
|
||||
GST_STREAM_TYPE_AUDIO))) {
|
||||
GST_DEBUG_OBJECT (demux, "Selecting default audio track %s",
|
||||
track->stream_id);
|
||||
if (!track->selected) {
|
||||
changed = TRUE;
|
||||
track->selected = TRUE;
|
||||
gst_pad_set_active (track->sinkpad, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if ((track =
|
||||
default_track_for_stream_type_locked (period,
|
||||
GST_STREAM_TYPE_TEXT))) {
|
||||
GST_DEBUG_OBJECT (demux, "Selecting default text track %s",
|
||||
track->stream_id);
|
||||
if (!track->selected) {
|
||||
changed = TRUE;
|
||||
track->selected = TRUE;
|
||||
gst_pad_set_active (track->sinkpad, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
g_atomic_int_set (&demux->priv->requested_selection_seqnum,
|
||||
gst_util_seqnum_next ());
|
||||
}
|
||||
|
||||
static GstAdaptiveDemuxTrack *
|
||||
gst_adaptive_demux_period_find_matching_track (GstAdaptiveDemuxPeriod * period,
|
||||
GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
for (iter = period->tracks; iter; iter = iter->next) {
|
||||
GstAdaptiveDemuxTrack *cand = iter->data;
|
||||
|
||||
if (!cand->selected && cand->type == track->type) {
|
||||
/* FIXME : Improve this a *lot* */
|
||||
if (!g_strcmp0 (cand->stream_id, track->stream_id))
|
||||
return cand;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_period_transfer_selection (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemuxPeriod * next_period,
|
||||
GstAdaptiveDemuxPeriod * current_period)
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
for (iter = current_period->tracks; iter; iter = iter->next) {
|
||||
GstAdaptiveDemuxTrack *track = iter->data;
|
||||
if (track->selected) {
|
||||
GstAdaptiveDemuxTrack *new_track =
|
||||
gst_adaptive_demux_period_find_matching_track (next_period, track);
|
||||
if (new_track) {
|
||||
GST_DEBUG_OBJECT (demux, "Selecting replacement track %s",
|
||||
new_track->stream_id);
|
||||
new_track->selected = TRUE;
|
||||
gst_pad_set_active (new_track->sinkpad, TRUE);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (demux, "Could not find replacement track for %s",
|
||||
track->stream_id);
|
||||
/* FIXME : Pick a default for that type ? Just continue as-is ? */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* called with TRACKS_LOCK taken */
|
||||
gboolean
|
||||
gst_adaptive_demux_period_add_track (GstAdaptiveDemuxPeriod * period,
|
||||
GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
GST_LOG ("period %d track:%p", period->period_num, track);
|
||||
|
||||
/* Actually create and add the elements to the demuxer */
|
||||
if (!gst_adaptive_demux_track_add_elements (track, period->period_num)) {
|
||||
GST_ERROR ("Failed to add track");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
period->tracks =
|
||||
g_list_append (period->tracks, gst_adaptive_demux_track_ref (track));
|
||||
period->tracks_changed = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* must be called with manifest_lock taken */
|
||||
GstFlowReturn
|
||||
gst_adaptive_demux_period_combine_stream_flows (GstAdaptiveDemuxPeriod * period)
|
||||
{
|
||||
gboolean all_notlinked = TRUE;
|
||||
gboolean all_eos = TRUE;
|
||||
GList *iter;
|
||||
|
||||
for (iter = period->streams; iter; iter = g_list_next (iter)) {
|
||||
GstAdaptiveDemux2Stream *stream = iter->data;
|
||||
|
||||
if (stream->last_ret != GST_FLOW_NOT_LINKED) {
|
||||
all_notlinked = FALSE;
|
||||
if (stream->last_ret != GST_FLOW_EOS)
|
||||
all_eos = FALSE;
|
||||
}
|
||||
|
||||
if (stream->last_ret <= GST_FLOW_NOT_NEGOTIATED
|
||||
|| stream->last_ret == GST_FLOW_FLUSHING) {
|
||||
return stream->last_ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_notlinked)
|
||||
return GST_FLOW_NOT_LINKED;
|
||||
|
||||
if (all_eos)
|
||||
return GST_FLOW_EOS;
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_period_stop_tasks (GstAdaptiveDemuxPeriod * period)
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
for (iter = period->streams; iter; iter = g_list_next (iter)) {
|
||||
GstAdaptiveDemux2Stream *stream = iter->data;
|
||||
|
||||
gst_adaptive_demux2_stream_stop (stream);
|
||||
|
||||
stream->download_error_count = 0;
|
||||
stream->need_header = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_adaptive_demux_period_has_pending_tracks (GstAdaptiveDemuxPeriod * period)
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
for (iter = period->streams; iter; iter = g_list_next (iter)) {
|
||||
GstAdaptiveDemux2Stream *stream = iter->data;
|
||||
if (stream->pending_tracks)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
|
||||
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||
*
|
||||
* Copyright (C) 2021-2022 Centricular Ltd
|
||||
* Author: Edward Hervey <edward@centricular.com>
|
||||
* Author: Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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_ADAPTIVE_DEMUX_PRIVATE_H_
|
||||
#define _GST_ADAPTIVE_DEMUX_PRIVATE_H_
|
||||
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <gst/base/gstflowcombiner.h>
|
||||
|
||||
#define NUM_LOOKBACK_FRAGMENTS 3
|
||||
#define MAX_DOWNLOAD_ERROR_COUNT 3
|
||||
|
||||
/* Internal, so not using GST_FLOW_CUSTOM_SUCCESS_N */
|
||||
#define GST_ADAPTIVE_DEMUX_FLOW_SWITCH (GST_FLOW_CUSTOM_SUCCESS_2 + 1)
|
||||
|
||||
#define TRACKS_GET_LOCK(d) (&GST_ADAPTIVE_DEMUX_CAST(d)->priv->tracks_lock)
|
||||
#define TRACKS_LOCK(d) g_mutex_lock (TRACKS_GET_LOCK (d))
|
||||
#define TRACKS_UNLOCK(d) g_mutex_unlock (TRACKS_GET_LOCK (d))
|
||||
|
||||
#define BUFFERING_GET_LOCK(d) (&GST_ADAPTIVE_DEMUX_CAST(d)->priv->buffering_lock)
|
||||
#define BUFFERING_LOCK(d) g_mutex_lock (BUFFERING_GET_LOCK (d))
|
||||
#define BUFFERING_UNLOCK(d) g_mutex_unlock (BUFFERING_GET_LOCK (d))
|
||||
|
||||
#define GST_MANIFEST_GET_LOCK(d) (&(GST_ADAPTIVE_DEMUX_CAST(d)->priv->manifest_lock))
|
||||
#define GST_MANIFEST_LOCK(d) G_STMT_START { \
|
||||
GST_TRACE("Locking manifest from thread %p", g_thread_self()); \
|
||||
g_rec_mutex_lock (GST_MANIFEST_GET_LOCK (d)); \
|
||||
GST_TRACE("Locked manifest from thread %p", g_thread_self()); \
|
||||
} G_STMT_END
|
||||
|
||||
#define GST_MANIFEST_UNLOCK(d) G_STMT_START { \
|
||||
GST_TRACE("Unlocking manifest from thread %p", g_thread_self()); \
|
||||
g_rec_mutex_unlock (GST_MANIFEST_GET_LOCK (d)); \
|
||||
} G_STMT_END
|
||||
|
||||
#define GST_ADAPTIVE_DEMUX_GET_SCHEDULER(d) (GST_ADAPTIVE_DEMUX_CAST(d)->priv->scheduler_task)
|
||||
|
||||
#define GST_ADAPTIVE_SCHEDULER_LOCK(d) gst_adaptive_demux_scheduler_lock(demux)
|
||||
#define GST_ADAPTIVE_SCHEDULER_UNLOCK(d) G_STMT_START { \
|
||||
GST_TRACE("Unlocking scheduler from thread %p", g_thread_self()); \
|
||||
gst_adaptive_demux_loop_unlock_and_unpause (GST_ADAPTIVE_DEMUX_GET_SCHEDULER (d)); \
|
||||
} G_STMT_END
|
||||
|
||||
#define GST_ADAPTIVE_DEMUX_SEGMENT_GET_LOCK(d) (&GST_ADAPTIVE_DEMUX_CAST(d)->priv->segment_lock)
|
||||
#define GST_ADAPTIVE_DEMUX_SEGMENT_LOCK(d) g_mutex_lock (GST_ADAPTIVE_DEMUX_SEGMENT_GET_LOCK (d))
|
||||
#define GST_ADAPTIVE_DEMUX_SEGMENT_UNLOCK(d) g_mutex_unlock (GST_ADAPTIVE_DEMUX_SEGMENT_GET_LOCK (d))
|
||||
|
||||
struct _GstAdaptiveDemuxPrivate
|
||||
{
|
||||
GstAdapter *input_adapter; /* protected by manifest_lock */
|
||||
gint have_manifest; /* MT safe */
|
||||
|
||||
/* Adaptive scheduling and parsing task */
|
||||
GstAdaptiveDemuxLoop *scheduler_task;
|
||||
GMutex scheduler_lock;
|
||||
|
||||
/* Callback / timer id for the next manifest update */
|
||||
guint manifest_updates_cb;
|
||||
|
||||
/* Count of failed manifest updates */
|
||||
gint update_failed_count;
|
||||
|
||||
guint32 segment_seqnum; /* protected by manifest_lock */
|
||||
|
||||
/* main lock used to protect adaptive demux and all its streams.
|
||||
* It serializes the adaptive demux public API.
|
||||
*/
|
||||
GRecMutex manifest_lock;
|
||||
|
||||
/* Duration, updated after manifest updates */
|
||||
GstClockTime duration;
|
||||
|
||||
/* Set to TRUE if any stream is waiting on the manifest update */
|
||||
gboolean stream_waiting_for_manifest;
|
||||
|
||||
GMutex api_lock;
|
||||
|
||||
/* Protects demux and stream segment information
|
||||
* Needed because seeks can update segment information
|
||||
* without needing to stop tasks when they just want to
|
||||
* update the segment boundaries */
|
||||
GMutex segment_lock;
|
||||
|
||||
GstClockTime qos_earliest_time;
|
||||
|
||||
/* Protects all tracks and period content */
|
||||
GMutex tracks_lock;
|
||||
/* Used to notify addition to a waiting (i.e. previously empty) track */
|
||||
GCond tracks_add;
|
||||
/* TRUE if we are buffering */
|
||||
gboolean is_buffering;
|
||||
/* TRUE if percent changed and message should be posted */
|
||||
gboolean percent_changed;
|
||||
gint percent;
|
||||
|
||||
/* Serialises buffering message posting to avoid out-of-order
|
||||
* posting */
|
||||
GMutex buffering_lock;
|
||||
|
||||
/* Atomic */
|
||||
guint32 requested_selection_seqnum;
|
||||
|
||||
/* Lock protecting all the following fields */
|
||||
GRecMutex output_lock;
|
||||
/* Output task */
|
||||
GstTask *output_task;
|
||||
/* List of enabled OutputSlot */
|
||||
GList *outputs;
|
||||
/* flow combiner of output slots */
|
||||
GstFlowCombiner *flowcombiner;
|
||||
/* protected by output_lock */
|
||||
gboolean flushing;
|
||||
/* Current output selection seqnum */
|
||||
guint32 current_selection_seqnum;
|
||||
/* Current output position (in running time) */
|
||||
GstClockTimeDiff global_output_position;
|
||||
/* End of fields protected by output_lock */
|
||||
|
||||
gint n_audio_streams, n_video_streams, n_subtitle_streams;
|
||||
|
||||
/* Counter used for uniquely identifying periods */
|
||||
gint n_periods;
|
||||
|
||||
/* Array of periods.
|
||||
*
|
||||
* Head is the period being outputted, or to be outputted first
|
||||
* Tail is where new streams get added */
|
||||
GQueue *periods;
|
||||
};
|
||||
|
||||
static inline gboolean gst_adaptive_demux_scheduler_lock(GstAdaptiveDemux *d)
|
||||
{
|
||||
GST_TRACE("Locking scheduler from thread %p", g_thread_self());
|
||||
if (!gst_adaptive_demux_loop_pause_and_lock (GST_ADAPTIVE_DEMUX_GET_SCHEDULER (d)))
|
||||
return FALSE;
|
||||
|
||||
GST_TRACE("Locked scheduler from thread %p", g_thread_self());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void demux_update_buffering_locked (GstAdaptiveDemux * demux);
|
||||
void demux_post_buffering_locked (GstAdaptiveDemux * demux);
|
||||
|
||||
GstFlowReturn gst_adaptive_demux_update_manifest (GstAdaptiveDemux *demux);
|
||||
|
||||
void gst_adaptive_demux2_stream_wants_manifest_update (GstAdaptiveDemux * demux);
|
||||
|
||||
void gst_adaptive_demux2_stream_parse_error (GstAdaptiveDemux2Stream *stream, GError * err);
|
||||
GstClockTime gst_adaptive_demux2_stream_get_fragment_waiting_time (GstAdaptiveDemux *
|
||||
demux, GstAdaptiveDemux2Stream * stream);
|
||||
GstClockTime gst_adaptive_demux2_stream_get_presentation_offset (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream);
|
||||
GstClockTime gst_adaptive_demux_get_period_start_time (GstAdaptiveDemux * demux);
|
||||
|
||||
gboolean gst_adaptive_demux_is_live (GstAdaptiveDemux * demux);
|
||||
|
||||
void gst_adaptive_demux2_stream_on_manifest_update (GstAdaptiveDemux2Stream * stream);
|
||||
void gst_adaptive_demux2_stream_on_output_space_available (GstAdaptiveDemux2Stream *stream);
|
||||
|
||||
gboolean gst_adaptive_demux2_stream_has_next_fragment (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream);
|
||||
GstFlowReturn gst_adaptive_demux2_stream_update_fragment_info (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream);
|
||||
GstFlowReturn gst_adaptive_demux2_stream_seek (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags,
|
||||
GstClockTimeDiff ts, GstClockTimeDiff * final_ts);
|
||||
gboolean gst_adaptive_demux_get_live_seek_range (GstAdaptiveDemux * demux,
|
||||
gint64 * range_start, gint64 * range_stop);
|
||||
gboolean gst_adaptive_demux2_stream_in_live_seek_range (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream);
|
||||
gboolean gst_adaptive_demux2_stream_is_selected_locked (GstAdaptiveDemux2Stream *stream);
|
||||
|
||||
gboolean gst_adaptive_demux_has_next_period (GstAdaptiveDemux * demux);
|
||||
void gst_adaptive_demux_advance_period (GstAdaptiveDemux * demux);
|
||||
|
||||
void gst_adaptive_demux2_stream_stop (GstAdaptiveDemux2Stream * stream);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstMiniObject *item;
|
||||
gsize size;
|
||||
/* running time of item : GST_CLOCK_STIME_NONE for non-timed data */
|
||||
GstClockTimeDiff runningtime;
|
||||
/* GST_CLOCK_STIME_NONE for non-timed data */
|
||||
GstClockTimeDiff runningtime_end;
|
||||
} TrackQueueItem;
|
||||
|
||||
GstAdaptiveDemux2Stream *find_stream_for_track_locked (GstAdaptiveDemux *
|
||||
demux, GstAdaptiveDemuxTrack * track);
|
||||
|
||||
GstMiniObject * track_dequeue_data_locked (GstAdaptiveDemux * demux, GstAdaptiveDemuxTrack * track, gboolean check_sticky_events);
|
||||
void gst_adaptive_demux_track_flush (GstAdaptiveDemuxTrack * track);
|
||||
void gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track, GstClockTime drain_running_time);
|
||||
void gst_adaptive_demux_track_update_next_position (GstAdaptiveDemuxTrack * track);
|
||||
|
||||
/* Period functions */
|
||||
GstAdaptiveDemuxPeriod * gst_adaptive_demux_period_new (GstAdaptiveDemux * demux);
|
||||
|
||||
GstAdaptiveDemuxPeriod * gst_adaptive_demux_period_ref (GstAdaptiveDemuxPeriod * period);
|
||||
void gst_adaptive_demux_period_unref (GstAdaptiveDemuxPeriod * period);
|
||||
|
||||
gboolean gst_adaptive_demux_period_add_track (GstAdaptiveDemuxPeriod * period,
|
||||
GstAdaptiveDemuxTrack * track);
|
||||
gboolean gst_adaptive_demux_track_add_elements (GstAdaptiveDemuxTrack * track,
|
||||
guint period_num);
|
||||
|
||||
void gst_adaptive_demux_period_select_default_tracks (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemuxPeriod * period);
|
||||
void gst_adaptive_demux_period_transfer_selection (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemuxPeriod * next_period,
|
||||
GstAdaptiveDemuxPeriod * current_period);
|
||||
void gst_adaptive_demux_period_stop_tasks (GstAdaptiveDemuxPeriod * period);
|
||||
GstFlowReturn gst_adaptive_demux_period_combine_stream_flows (GstAdaptiveDemuxPeriod * period);
|
||||
|
||||
gboolean gst_adaptive_demux_period_has_pending_tracks (GstAdaptiveDemuxPeriod * period);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,891 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
|
||||
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||
*
|
||||
* Copyright (C) 2021-2022 Centricular Ltd
|
||||
* Author: Edward Hervey <edward@centricular.com>
|
||||
* Author: Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstadaptivedemux.h"
|
||||
#include "gstadaptivedemux-private.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
|
||||
#define GST_CAT_DEFAULT adaptivedemux2_debug
|
||||
|
||||
/* TRACKS_LOCK held
|
||||
* Flushes all data in the track and resets it */
|
||||
void
|
||||
gst_adaptive_demux_track_flush (GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
GST_DEBUG_OBJECT (track->demux, "Flushing track '%s' with %u queued items",
|
||||
track->stream_id, gst_queue_array_get_length (track->queue));
|
||||
gst_queue_array_clear (track->queue);
|
||||
|
||||
gst_event_store_flush (&track->sticky_events);
|
||||
|
||||
gst_segment_init (&track->input_segment, GST_FORMAT_TIME);
|
||||
track->input_time = 0;
|
||||
track->input_segment_seqnum = GST_SEQNUM_INVALID;
|
||||
|
||||
gst_segment_init (&track->output_segment, GST_FORMAT_TIME);
|
||||
track->gap_position = track->gap_duration = GST_CLOCK_TIME_NONE;
|
||||
|
||||
track->output_time = 0;
|
||||
track->next_position = GST_CLOCK_STIME_NONE;
|
||||
|
||||
track->level_bytes = 0;
|
||||
track->level_time = 0;
|
||||
|
||||
track->eos = FALSE;
|
||||
|
||||
track->update_next_segment = FALSE;
|
||||
|
||||
track->output_discont = FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_track_sink_query_function (GstPad * pad, GstObject * parent, GstQuery * query)
|
||||
{
|
||||
GstAdaptiveDemuxTrack *track = gst_pad_get_element_private (pad);
|
||||
GstAdaptiveDemux *demux = track->demux;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
GST_DEBUG_OBJECT (pad, "query %" GST_PTR_FORMAT, query);
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_ACCEPT_CAPS:
|
||||
/* Should we intersect by track caps as a safety check ? */
|
||||
GST_DEBUG_OBJECT (demux, "We accept any caps on %s:%s",
|
||||
GST_DEBUG_PAD_NAME (pad));
|
||||
gst_query_set_accept_caps_result (query, TRUE);
|
||||
ret = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Dequeue an item from the track queue for processing
|
||||
* TRACKS_LOCK hold */
|
||||
static gboolean
|
||||
track_dequeue_item_locked (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemuxTrack * track, TrackQueueItem * out_item)
|
||||
{
|
||||
TrackQueueItem *item = gst_queue_array_peek_head_struct (track->queue);
|
||||
|
||||
if (item == NULL)
|
||||
return FALSE;
|
||||
|
||||
*out_item = *item;
|
||||
gst_queue_array_pop_head (track->queue);
|
||||
|
||||
GST_LOG_OBJECT (demux,
|
||||
"track %s item running_time %" GST_STIME_FORMAT " end %" GST_STIME_FORMAT,
|
||||
track->stream_id, GST_STIME_ARGS (out_item->runningtime),
|
||||
GST_STIME_ARGS (out_item->runningtime_end));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline GstClockTimeDiff my_segment_to_running_time (GstSegment * segment,
|
||||
GstClockTime val);
|
||||
|
||||
/* Dequeue or generate a buffer/event from the track queue and update the buffering levels
|
||||
* TRACKS_LOCK hold */
|
||||
GstMiniObject *
|
||||
track_dequeue_data_locked (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemuxTrack * track, gboolean check_sticky_events)
|
||||
{
|
||||
GstMiniObject *res = NULL;
|
||||
gboolean is_pending_sticky = FALSE;
|
||||
GstEvent *event;
|
||||
GstClockTimeDiff running_time;
|
||||
GstClockTimeDiff running_time_end;
|
||||
gsize item_size = 0;
|
||||
|
||||
if (check_sticky_events) {
|
||||
/* If there are any sticky events to send, do that before anything else */
|
||||
event = gst_event_store_get_next_pending (&track->sticky_events);
|
||||
if (event != NULL) {
|
||||
res = (GstMiniObject *) event;
|
||||
running_time = running_time_end = GST_CLOCK_STIME_NONE;
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"track %s dequeued pending sticky event %" GST_PTR_FORMAT,
|
||||
track->stream_id, event);
|
||||
is_pending_sticky = TRUE;
|
||||
goto handle_event;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
TrackQueueItem item;
|
||||
|
||||
/* If we're filling a gap, generate a gap event */
|
||||
if (track->gap_position != GST_CLOCK_TIME_NONE) {
|
||||
GstClockTime pos = track->gap_position;
|
||||
GstClockTime duration = track->gap_duration;
|
||||
|
||||
if (duration > 100 * GST_MSECOND) {
|
||||
duration = 100 * GST_MSECOND;
|
||||
track->gap_position += duration;
|
||||
track->gap_duration -= duration;
|
||||
} else {
|
||||
/* Duration dropped below 100 ms, this is the last
|
||||
* gap of the sequence */
|
||||
track->gap_position = GST_CLOCK_TIME_NONE;
|
||||
track->gap_duration = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
res = (GstMiniObject *) gst_event_new_gap (pos, duration);
|
||||
running_time = my_segment_to_running_time (&track->output_segment, pos);
|
||||
running_time_end =
|
||||
my_segment_to_running_time (&track->output_segment, pos + duration);
|
||||
item_size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise, try and pop something from the item queue */
|
||||
if (!track_dequeue_item_locked (demux, track, &item))
|
||||
return NULL;
|
||||
|
||||
res = item.item;
|
||||
running_time = item.runningtime;
|
||||
running_time_end = item.runningtime_end;
|
||||
item_size = item.size;
|
||||
|
||||
/* Special case for a gap event, to drain them out little-by-little.
|
||||
* See if it can be output directly, otherwise set up to fill a gap and loop again */
|
||||
if (GST_IS_EVENT (res) && GST_EVENT_TYPE (res) == GST_EVENT_GAP
|
||||
&& GST_CLOCK_STIME_IS_VALID (running_time)) {
|
||||
GstClockTime pos, duration;
|
||||
GstClockTime cstart, cstop;
|
||||
|
||||
gst_event_parse_gap (GST_EVENT_CAST (res), &pos, &duration);
|
||||
|
||||
/* Handle a track with no duration as 0 duration. This can only
|
||||
* happen if an element in parsebin emits such a gap event */
|
||||
if (duration == GST_CLOCK_TIME_NONE)
|
||||
duration = 0;
|
||||
|
||||
/* We *can* end up with a gap outside of the segment range due to segment
|
||||
* base updating when (re)activating a track. In that case, just let the gap
|
||||
* event flow out normally.
|
||||
* Otherwise, this gap crosses into the segment, clip it to the ends and set up to fill the gap */
|
||||
if (!gst_segment_clip (&track->output_segment, GST_FORMAT_TIME, pos,
|
||||
pos + duration, &cstart, &cstop))
|
||||
break;
|
||||
|
||||
pos = cstart;
|
||||
duration = cstop - cstart;
|
||||
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"track %s Starting gap for runningtime %" GST_STIME_FORMAT
|
||||
" - clipped position %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT,
|
||||
track->stream_id, GST_STIME_ARGS (running_time), GST_TIME_ARGS (pos),
|
||||
GST_TIME_ARGS (duration));
|
||||
|
||||
track->gap_position = pos;
|
||||
track->gap_duration = duration;
|
||||
|
||||
gst_mini_object_unref (res);
|
||||
res = NULL;
|
||||
continue;
|
||||
}
|
||||
} while (res == NULL);
|
||||
|
||||
handle_event:
|
||||
if (GST_IS_EVENT (res)) {
|
||||
event = (GstEvent *) res;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEGMENT:
|
||||
gst_event_copy_segment (event, &track->output_segment);
|
||||
if (track->update_next_segment) {
|
||||
GstClockTimeDiff global_output_position =
|
||||
demux->priv->global_output_position;
|
||||
|
||||
GST_DEBUG ("track %s: Override segment for running time %"
|
||||
GST_STIME_FORMAT " : %" GST_PTR_FORMAT, track->stream_id,
|
||||
GST_STIME_ARGS (global_output_position), event);
|
||||
gst_event_unref (event);
|
||||
gst_segment_set_running_time (&track->output_segment, GST_FORMAT_TIME,
|
||||
global_output_position);
|
||||
|
||||
event = gst_event_new_segment (&track->output_segment);
|
||||
gst_event_set_seqnum (event, track->demux->priv->segment_seqnum);
|
||||
|
||||
res = (GstMiniObject *) event;
|
||||
running_time = global_output_position;
|
||||
|
||||
track->update_next_segment = FALSE;
|
||||
|
||||
/* Replace the stored sticky event with this one */
|
||||
is_pending_sticky = FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Store any sticky event in the cache, unless this is already an event
|
||||
* from the pending sticky_events store */
|
||||
if (!is_pending_sticky && GST_EVENT_IS_STICKY (event)) {
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"track %s Storing sticky event %" GST_PTR_FORMAT,
|
||||
track->stream_id, event);
|
||||
gst_event_store_insert_event (&track->sticky_events, event, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update track buffering levels */
|
||||
if (running_time != GST_CLOCK_STIME_NONE) {
|
||||
GstClockTimeDiff output_time;
|
||||
|
||||
track->output_time = running_time;
|
||||
if (running_time_end != GST_CLOCK_TIME_NONE)
|
||||
track->output_time = running_time_end;
|
||||
|
||||
output_time = MAX (track->output_time, demux->priv->global_output_position);
|
||||
if (track->input_time >= output_time)
|
||||
track->level_time = track->input_time - output_time;
|
||||
else
|
||||
track->level_time = 0;
|
||||
|
||||
GST_LOG_OBJECT (demux,
|
||||
"track %s input_time:%" GST_STIME_FORMAT " output_time:%"
|
||||
GST_STIME_FORMAT " level:%" GST_TIME_FORMAT,
|
||||
track->stream_id, GST_STIME_ARGS (track->input_time),
|
||||
GST_STIME_ARGS (output_time), GST_TIME_ARGS (track->level_time));
|
||||
} else {
|
||||
GST_LOG_OBJECT (demux, "track %s popping untimed item %" GST_PTR_FORMAT,
|
||||
track->stream_id, res);
|
||||
}
|
||||
|
||||
track->level_bytes -= item_size;
|
||||
|
||||
/* FIXME: This logic should be in adaptive demux, not the track */
|
||||
if (track->level_time < track->waiting_del_level) {
|
||||
/* Wake up download loop */
|
||||
GstAdaptiveDemux2Stream *stream =
|
||||
find_stream_for_track_locked (demux, track);
|
||||
|
||||
g_assert (stream != NULL);
|
||||
|
||||
gst_adaptive_demux2_stream_on_output_space_available (stream);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_track_drain_to (GstAdaptiveDemuxTrack * track,
|
||||
GstClockTime drain_running_time)
|
||||
{
|
||||
GstAdaptiveDemux *demux = track->demux;
|
||||
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"Track '%s' draining to running time %" GST_STIME_FORMAT,
|
||||
track->stream_id, GST_STIME_ARGS (drain_running_time));
|
||||
|
||||
while (track->next_position == GST_CLOCK_STIME_NONE ||
|
||||
track->next_position < drain_running_time) {
|
||||
TrackQueueItem *item;
|
||||
GstMiniObject *next_mo = NULL;
|
||||
|
||||
/* If we're in a gap, and the end time is after the target running time,
|
||||
* exit */
|
||||
if (track->gap_position != GST_CLOCK_TIME_NONE) {
|
||||
GstClockTimeDiff running_time_end;
|
||||
GstClockTimeDiff gap_end = track->gap_position;
|
||||
|
||||
/* In reverse playback, the start of the gap is the highest
|
||||
* running time, so only add duration for forward play */
|
||||
if (track->output_segment.rate > 0)
|
||||
gap_end += track->gap_duration;
|
||||
|
||||
running_time_end =
|
||||
my_segment_to_running_time (&track->output_segment, gap_end);
|
||||
|
||||
if (running_time_end >= drain_running_time) {
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"Track '%s' drained to GAP with running time %" GST_STIME_FORMAT,
|
||||
track->stream_id, GST_STIME_ARGS (running_time_end));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise this gap is complete, so skip it */
|
||||
track->gap_position = GST_CLOCK_STIME_NONE;
|
||||
}
|
||||
|
||||
/* Otherwise check what's enqueued */
|
||||
item = gst_queue_array_peek_head_struct (track->queue);
|
||||
/* track is empty, we're done */
|
||||
if (item == NULL) {
|
||||
GST_DEBUG_OBJECT (demux, "Track '%s' completely drained",
|
||||
track->stream_id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the item has a running time, and it's after the drain_running_time
|
||||
* we're done. */
|
||||
if (item->runningtime != GST_CLOCK_STIME_NONE
|
||||
&& item->runningtime >= drain_running_time) {
|
||||
GST_DEBUG_OBJECT (demux, "Track '%s' drained to item %" GST_PTR_FORMAT
|
||||
" with running time %" GST_STIME_FORMAT,
|
||||
track->stream_id, item->item, GST_STIME_ARGS (item->runningtime));
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "Track '%s' discarding %" GST_PTR_FORMAT
|
||||
" with running time %" GST_STIME_FORMAT,
|
||||
track->stream_id, item->item, GST_STIME_ARGS (item->runningtime));
|
||||
|
||||
/* Dequeue the item and discard. Sticky events
|
||||
* will be collected by the dequeue function, gaps will be started.
|
||||
* If it's a buffer, mark the track as discont to get the flag set
|
||||
* on the next output buffer */
|
||||
next_mo = track_dequeue_data_locked (demux, track, FALSE);
|
||||
if (GST_IS_BUFFER (next_mo)) {
|
||||
track->output_discont = TRUE;
|
||||
}
|
||||
gst_mini_object_unref (next_mo);
|
||||
gst_adaptive_demux_track_update_next_position (track);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"Track '%s' drained to running time %" GST_STIME_FORMAT, track->stream_id,
|
||||
GST_STIME_ARGS (track->next_position));
|
||||
}
|
||||
|
||||
static inline GstClockTimeDiff
|
||||
my_segment_to_running_time (GstSegment * segment, GstClockTime val)
|
||||
{
|
||||
GstClockTimeDiff res = GST_CLOCK_STIME_NONE;
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (val)) {
|
||||
gboolean sign =
|
||||
gst_segment_to_running_time_full (segment, GST_FORMAT_TIME, val, &val);
|
||||
if (sign > 0)
|
||||
res = val;
|
||||
else if (sign < 0)
|
||||
res = -val;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Queues an item on a track queue and updates the buffering levels
|
||||
* TRACKS_LOCK hold */
|
||||
static void
|
||||
track_queue_data_locked (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemuxTrack * track, GstMiniObject * object, gsize size,
|
||||
GstClockTime timestamp, GstClockTime duration)
|
||||
{
|
||||
TrackQueueItem item;
|
||||
|
||||
item.item = object;
|
||||
item.size = size;
|
||||
item.runningtime = GST_CLOCK_STIME_NONE;
|
||||
item.runningtime_end = GST_CLOCK_STIME_NONE;
|
||||
|
||||
if (timestamp != GST_CLOCK_TIME_NONE) {
|
||||
GstClockTimeDiff output_time;
|
||||
|
||||
/* Set the running time of the item */
|
||||
item.runningtime =
|
||||
my_segment_to_running_time (&track->input_segment, timestamp);
|
||||
|
||||
/* Update segment position (include duration if valid) */
|
||||
track->input_segment.position = timestamp;
|
||||
if (GST_CLOCK_TIME_IS_VALID (duration)) {
|
||||
track->input_segment.position += duration;
|
||||
item.runningtime_end =
|
||||
my_segment_to_running_time (&track->input_segment,
|
||||
track->input_segment.position);
|
||||
}
|
||||
|
||||
/* Update track input time and level */
|
||||
track->input_time =
|
||||
my_segment_to_running_time (&track->input_segment,
|
||||
track->input_segment.position);
|
||||
|
||||
output_time = MAX (track->output_time, demux->priv->global_output_position);
|
||||
if (track->input_time >= output_time)
|
||||
track->level_time = track->input_time - output_time;
|
||||
else
|
||||
track->level_time = 0;
|
||||
|
||||
GST_LOG_OBJECT (demux,
|
||||
"track %s input_time:%" GST_STIME_FORMAT " output_time:%"
|
||||
GST_STIME_FORMAT " level:%" GST_TIME_FORMAT,
|
||||
track->stream_id, GST_STIME_ARGS (track->input_time),
|
||||
GST_STIME_ARGS (track->output_time), GST_TIME_ARGS (track->level_time));
|
||||
}
|
||||
track->level_bytes += size;
|
||||
gst_queue_array_push_tail_struct (track->queue, &item);
|
||||
|
||||
/* If we were waiting for this track to add something, notify output thread */
|
||||
/* FIXME: This should be in adaptive demux */
|
||||
if (track->waiting_add) {
|
||||
g_cond_signal (&demux->priv->tracks_add);
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
_track_sink_chain_function (GstPad * pad, GstObject * parent,
|
||||
GstBuffer * buffer)
|
||||
{
|
||||
GstAdaptiveDemuxTrack *track = gst_pad_get_element_private (pad);
|
||||
GstAdaptiveDemux *demux = track->demux;
|
||||
GstClockTime ts;
|
||||
|
||||
GST_DEBUG_OBJECT (pad, "buffer %" GST_PTR_FORMAT, buffer);
|
||||
|
||||
TRACKS_LOCK (demux);
|
||||
|
||||
ts = GST_BUFFER_DTS_OR_PTS (buffer);
|
||||
|
||||
/* Buffers coming out of parsebin *should* always be timestamped (it's the
|
||||
* goal of parsebin after all). The tracks will use that (converted to
|
||||
* running-time) in order to track position and buffering levels.
|
||||
*
|
||||
* Unfortunately there are valid cases were the parsers won't be able to
|
||||
* timestamp all frames (due to the underlying formats or muxing). For those
|
||||
* cases, we use the last incoming timestamp (via the track input GstSegment
|
||||
* position):
|
||||
*
|
||||
* * If buffers were previously received, that segment position will
|
||||
* correspond to the last timestamped-buffer PTS/DTS
|
||||
*
|
||||
* * If *no* buffers were previously received, the segment position *should*
|
||||
* correspond to the valid initial position (in buffer timestamps). If not
|
||||
* set, we need to bail out.
|
||||
*/
|
||||
if (!GST_CLOCK_TIME_IS_VALID (ts)) {
|
||||
if (GST_CLOCK_TIME_IS_VALID (track->input_segment.position)) {
|
||||
GST_WARNING_OBJECT (pad,
|
||||
"buffer doesn't have any pts or dts, using segment position (%"
|
||||
GST_TIME_FORMAT ")", GST_TIME_ARGS (track->input_segment.position));
|
||||
ts = track->input_segment.position;
|
||||
} else {
|
||||
GST_ERROR_OBJECT (pad, "initial buffer doesn't have any pts or dts !");
|
||||
gst_buffer_unref (buffer);
|
||||
TRACKS_UNLOCK (demux);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (track->input_segment.position) &&
|
||||
ts > track->input_segment.position &&
|
||||
ts > track->input_segment.start &&
|
||||
ts - track->input_segment.position > 100 * GST_MSECOND) {
|
||||
GstClockTime duration = ts - track->input_segment.position;
|
||||
GstEvent *gap = gst_event_new_gap (track->input_segment.position, duration);
|
||||
/* Insert gap event to ensure coherent interleave */
|
||||
GST_DEBUG_OBJECT (pad,
|
||||
"Inserting gap for %" GST_TIME_FORMAT " vs %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (ts), GST_TIME_ARGS (track->input_segment.position));
|
||||
track_queue_data_locked (demux, track, (GstMiniObject *) gap, 0,
|
||||
track->input_segment.position, duration);
|
||||
}
|
||||
|
||||
track_queue_data_locked (demux, track, (GstMiniObject *) buffer,
|
||||
gst_buffer_get_size (buffer), ts, GST_BUFFER_DURATION (buffer));
|
||||
|
||||
/* Recalculate buffering */
|
||||
demux_update_buffering_locked (demux);
|
||||
demux_post_buffering_locked (demux);
|
||||
/* UNLOCK */
|
||||
TRACKS_UNLOCK (demux);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_track_sink_event_function (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
GstAdaptiveDemuxTrack *track = gst_pad_get_element_private (pad);
|
||||
GstAdaptiveDemux *demux = track->demux;
|
||||
GstClockTime timestamp = GST_CLOCK_TIME_NONE;
|
||||
GstClockTime duration = GST_CLOCK_TIME_NONE;
|
||||
gboolean drop = FALSE;
|
||||
|
||||
GST_DEBUG_OBJECT (pad, "event %" GST_PTR_FORMAT, event);
|
||||
|
||||
TRACKS_LOCK (demux);
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_STREAM_COLLECTION:
|
||||
{
|
||||
/* Replace upstream collection with demux collection */
|
||||
GST_DEBUG_OBJECT (pad, "Dropping stream-collection, we send our own");
|
||||
drop = TRUE;
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_STREAM_START:
|
||||
{
|
||||
GST_DEBUG_OBJECT (pad, "Dropping stream-start, we send our own");
|
||||
if (track->eos) {
|
||||
gint i, len;
|
||||
/* Find and drop latest EOS if present */
|
||||
len = gst_queue_array_get_length (track->queue);
|
||||
for (i = len - 1; i >= 0; i--) {
|
||||
TrackQueueItem *item =
|
||||
gst_queue_array_peek_nth_struct (track->queue, i);
|
||||
if (GST_IS_EVENT (item->item)
|
||||
&& GST_EVENT_TYPE (item->item) == GST_EVENT_EOS) {
|
||||
TrackQueueItem sub;
|
||||
GST_DEBUG_OBJECT (pad, "Removing previously received EOS (pos:%d)",
|
||||
i);
|
||||
if (gst_queue_array_drop_struct (track->queue, i, &sub))
|
||||
gst_mini_object_unref (sub.item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
track->eos = FALSE;
|
||||
}
|
||||
drop = TRUE;
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_EOS:
|
||||
{
|
||||
if (track->pending_srcpad != NULL) {
|
||||
GST_DEBUG_OBJECT (pad,
|
||||
"Dropping EOS because we have a pending pad switch");
|
||||
drop = TRUE;
|
||||
} else {
|
||||
track->eos = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_FLUSH_STOP:
|
||||
case GST_EVENT_FLUSH_START:
|
||||
{
|
||||
/* Drop flush events */
|
||||
drop = TRUE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (drop || !GST_EVENT_IS_SERIALIZED (event)) {
|
||||
GST_DEBUG_OBJECT (pad, "dropping event %s", GST_EVENT_TYPE_NAME (event));
|
||||
gst_event_unref (event);
|
||||
TRACKS_UNLOCK (demux);
|
||||
/* Silently "accept" them */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEGMENT:
|
||||
{
|
||||
guint64 seg_seqnum = gst_event_get_seqnum (event);
|
||||
|
||||
if (track->input_segment_seqnum == seg_seqnum) {
|
||||
GST_DEBUG_OBJECT (pad, "Ignoring duplicate segment");
|
||||
gst_event_unref (event);
|
||||
TRACKS_UNLOCK (demux);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
track->input_segment_seqnum = seg_seqnum;
|
||||
gst_event_copy_segment (event, &track->input_segment);
|
||||
if (track->input_segment.rate >= 0)
|
||||
track->input_segment.position = track->input_segment.start;
|
||||
else
|
||||
track->input_segment.position = track->input_segment.stop;
|
||||
GST_DEBUG_OBJECT (pad, "track %s stored segment %" GST_SEGMENT_FORMAT,
|
||||
track->stream_id, &track->input_segment);
|
||||
timestamp = track->input_segment.position;
|
||||
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_GAP:
|
||||
{
|
||||
gst_event_parse_gap (event, ×tamp, &duration);
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (timestamp)) {
|
||||
GST_DEBUG_OBJECT (pad, "Dropping gap event with invalid timestamp");
|
||||
goto drop_ok;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
track_queue_data_locked (demux, track, (GstMiniObject *) event, 0,
|
||||
timestamp, duration);
|
||||
|
||||
/* Recalculate buffering */
|
||||
demux_update_buffering_locked (demux);
|
||||
demux_post_buffering_locked (demux);
|
||||
|
||||
TRACKS_UNLOCK (demux);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* errors */
|
||||
drop_ok:
|
||||
{
|
||||
gst_event_unref (event);
|
||||
TRACKS_UNLOCK (demux);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
track_sinkpad_unlinked_cb (GstPad * sinkpad, GstPad * parsebin_srcpad,
|
||||
GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
GST_DEBUG_OBJECT (sinkpad, "Got unlinked from %s:%s",
|
||||
GST_DEBUG_PAD_NAME (parsebin_srcpad));
|
||||
|
||||
if (track->pending_srcpad) {
|
||||
GST_DEBUG_OBJECT (sinkpad, "linking to pending pad %s:%s",
|
||||
GST_DEBUG_PAD_NAME (track->pending_srcpad));
|
||||
|
||||
if (gst_pad_link (track->pending_srcpad, sinkpad) != GST_PAD_LINK_OK) {
|
||||
GST_ERROR_OBJECT (sinkpad, "could not link pending pad !");
|
||||
}
|
||||
gst_object_unref (track->pending_srcpad);
|
||||
track->pending_srcpad = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* TRACKS_LOCK held
|
||||
* Call this to update the track next_position with timed data */
|
||||
void
|
||||
gst_adaptive_demux_track_update_next_position (GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
guint i, len;
|
||||
|
||||
/* If filling a gap, the next position is the gap position */
|
||||
if (track->gap_position != GST_CLOCK_TIME_NONE) {
|
||||
track->next_position =
|
||||
my_segment_to_running_time (&track->output_segment,
|
||||
track->gap_position);
|
||||
return;
|
||||
}
|
||||
|
||||
len = gst_queue_array_get_length (track->queue);
|
||||
for (i = 0; i < len; i++) {
|
||||
TrackQueueItem *item = gst_queue_array_peek_nth_struct (track->queue, i);
|
||||
|
||||
if (item->runningtime != GST_CLOCK_STIME_NONE) {
|
||||
GST_DEBUG_OBJECT (track->demux,
|
||||
"Track '%s' next position %" GST_STIME_FORMAT, track->stream_id,
|
||||
GST_STIME_ARGS (item->runningtime));
|
||||
track->next_position = item->runningtime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
track->next_position = GST_CLOCK_STIME_NONE;
|
||||
|
||||
GST_DEBUG_OBJECT (track->demux,
|
||||
"Track '%s' doesn't have any pending timed data", track->stream_id);
|
||||
}
|
||||
|
||||
static void
|
||||
_demux_track_free (GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
GST_DEBUG_OBJECT (track->demux, "freeing track %p '%s'", track,
|
||||
track->stream_id);
|
||||
|
||||
g_free (track->stream_id);
|
||||
g_free (track->upstream_stream_id);
|
||||
|
||||
if (track->pending_srcpad)
|
||||
gst_object_unref (track->pending_srcpad);
|
||||
|
||||
if (track->generic_caps)
|
||||
gst_caps_unref (track->generic_caps);
|
||||
gst_object_unref (track->stream_object);
|
||||
if (track->tags)
|
||||
gst_tag_list_unref (track->tags);
|
||||
gst_queue_array_free (track->queue);
|
||||
|
||||
gst_event_store_deinit (&track->sticky_events);
|
||||
|
||||
if (track->element != NULL) {
|
||||
gst_element_set_state (track->element, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN_CAST (track->demux), track->element);
|
||||
}
|
||||
|
||||
g_free (track);
|
||||
}
|
||||
|
||||
GstAdaptiveDemuxTrack *
|
||||
gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
g_return_val_if_fail (track != NULL, NULL);
|
||||
GST_TRACE ("%p %d -> %d", track, track->ref_count, track->ref_count + 1);
|
||||
g_atomic_int_inc (&track->ref_count);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack * track)
|
||||
{
|
||||
g_return_if_fail (track != NULL);
|
||||
|
||||
GST_TRACE ("%p %d -> %d", track, track->ref_count, track->ref_count - 1);
|
||||
if (g_atomic_int_dec_and_test (&track->ref_count)) {
|
||||
_demux_track_free (track);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_track_queue_item_clear (TrackQueueItem * item)
|
||||
{
|
||||
if (item->item) {
|
||||
gst_mini_object_unref ((GstMiniObject *) item->item);
|
||||
item->item = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Internal function which actually adds the elements to the demuxer */
|
||||
gboolean
|
||||
gst_adaptive_demux_track_add_elements (GstAdaptiveDemuxTrack * track,
|
||||
guint period_num)
|
||||
{
|
||||
GstAdaptiveDemux *demux = track->demux;
|
||||
gchar *internal_name;
|
||||
guint i, len;
|
||||
|
||||
internal_name =
|
||||
g_strdup_printf ("track-period%d-%s", period_num, track->stream_id);
|
||||
len = strlen (internal_name);
|
||||
for (i = 0; i < len; i++)
|
||||
if (internal_name[i] == ' ')
|
||||
internal_name[i] = '_';
|
||||
track->element = gst_bin_new (internal_name);
|
||||
g_free (internal_name);
|
||||
|
||||
internal_name =
|
||||
g_strdup_printf ("track-period%d-sink-%s", period_num, track->stream_id);
|
||||
len = strlen (internal_name);
|
||||
for (i = 0; i < len; i++)
|
||||
if (internal_name[i] == ' ')
|
||||
internal_name[i] = '_';
|
||||
track->sinkpad = gst_pad_new (internal_name, GST_PAD_SINK);
|
||||
g_signal_connect (track->sinkpad, "unlinked",
|
||||
(GCallback) track_sinkpad_unlinked_cb, track);
|
||||
g_free (internal_name);
|
||||
gst_element_add_pad (GST_ELEMENT_CAST (track->element), track->sinkpad);
|
||||
gst_pad_set_element_private (track->sinkpad, track);
|
||||
gst_pad_set_chain_function (track->sinkpad, _track_sink_chain_function);
|
||||
gst_pad_set_event_function (track->sinkpad, _track_sink_event_function);
|
||||
gst_pad_set_query_function (track->sinkpad, _track_sink_query_function);
|
||||
|
||||
if (!gst_bin_add (GST_BIN_CAST (demux), track->element)) {
|
||||
track->element = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_element_sync_state_with_parent (track->element);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_adaptive_demux_track_new:
|
||||
* @demux: a #GstAdaptiveDemux
|
||||
* @type: a #GstStreamType
|
||||
* @flags: a #GstStreamFlags
|
||||
* @stream_id: (transfer none): The stream id for the new track
|
||||
* @caps: (transfer full): The caps for the track
|
||||
* @tags: (allow-none) (transfer full): The tags for the track
|
||||
*
|
||||
* Create and register a new #GstAdaptiveDemuxTrack
|
||||
*
|
||||
* Returns: (transfer none) The new track
|
||||
*/
|
||||
GstAdaptiveDemuxTrack *
|
||||
gst_adaptive_demux_track_new (GstAdaptiveDemux * demux,
|
||||
GstStreamType type,
|
||||
GstStreamFlags flags, gchar * stream_id, GstCaps * caps, GstTagList * tags)
|
||||
{
|
||||
GstAdaptiveDemuxTrack *track;
|
||||
|
||||
g_return_val_if_fail (stream_id != NULL, NULL);
|
||||
g_return_val_if_fail (type && type != GST_STREAM_TYPE_UNKNOWN, NULL);
|
||||
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "type:%s stream_id:%s caps:%" GST_PTR_FORMAT,
|
||||
gst_stream_type_get_name (type), stream_id, caps);
|
||||
|
||||
track = g_new0 (GstAdaptiveDemuxTrack, 1);
|
||||
g_atomic_int_set (&track->ref_count, 1);
|
||||
track->demux = demux;
|
||||
track->type = type;
|
||||
track->flags = flags;
|
||||
track->stream_id = g_strdup (stream_id);
|
||||
track->generic_caps = caps;
|
||||
track->stream_object = gst_stream_new (stream_id, caps, type, flags);
|
||||
if (tags) {
|
||||
track->tags = gst_tag_list_ref (tags);
|
||||
gst_stream_set_tags (track->stream_object, tags);
|
||||
}
|
||||
|
||||
track->selected = FALSE;
|
||||
track->active = FALSE;
|
||||
track->draining = FALSE;
|
||||
|
||||
track->queue = gst_queue_array_new_for_struct (sizeof (TrackQueueItem), 50);
|
||||
gst_queue_array_set_clear_func (track->queue,
|
||||
(GDestroyNotify) _track_queue_item_clear);
|
||||
|
||||
gst_event_store_init (&track->sticky_events);
|
||||
|
||||
track->waiting_add = TRUE;
|
||||
track->waiting_del_level = 0;
|
||||
|
||||
/* We have no fragment duration yet, so the buffering threshold is just the
|
||||
* low watermark in time for now */
|
||||
GST_OBJECT_LOCK (demux);
|
||||
track->buffering_threshold = demux->buffering_low_watermark_time;
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
|
||||
gst_segment_init (&track->input_segment, GST_FORMAT_TIME);
|
||||
track->input_time = 0;
|
||||
track->input_segment_seqnum = GST_SEQNUM_INVALID;
|
||||
|
||||
gst_segment_init (&track->output_segment, GST_FORMAT_TIME);
|
||||
track->gap_position = track->gap_duration = GST_CLOCK_TIME_NONE;
|
||||
|
||||
track->output_time = 0;
|
||||
track->next_position = GST_CLOCK_STIME_NONE;
|
||||
|
||||
track->update_next_segment = FALSE;
|
||||
|
||||
track->level_bytes = 0;
|
||||
track->level_time = 0;
|
||||
|
||||
return track;
|
||||
}
|
4217
subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c
Normal file
4217
subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,786 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
|
||||
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||
*
|
||||
* Copyright (C) 2021-2022 Centricular Ltd
|
||||
* Author: Edward Hervey <edward@centricular.com>
|
||||
* Author: Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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_ADAPTIVE_DEMUX_H_
|
||||
#define _GST_ADAPTIVE_DEMUX_H_
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstqueuearray.h>
|
||||
#include <gst/app/gstappsrc.h>
|
||||
#include "downloadhelper.h"
|
||||
#include "downloadrequest.h"
|
||||
|
||||
#include "gstadaptivedemuxutils.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_ADAPTIVE_DEMUX \
|
||||
(gst_adaptive_demux_ng_get_type())
|
||||
#define GST_ADAPTIVE_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemux))
|
||||
#define GST_ADAPTIVE_DEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
|
||||
#define GST_ADAPTIVE_DEMUX_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass))
|
||||
#define GST_IS_ADAPTIVE_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX))
|
||||
#define GST_IS_ADAPTIVE_DEMUX_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX))
|
||||
#define GST_ADAPTIVE_DEMUX_CAST(obj) ((GstAdaptiveDemux *)obj)
|
||||
|
||||
#define GST_TYPE_ADAPTIVE_DEMUX2_STREAM \
|
||||
(gst_adaptive_demux2_stream_get_type())
|
||||
#define GST_ADAPTIVE_DEMUX2_STREAM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2Stream))
|
||||
#define GST_ADAPTIVE_DEMUX2_STREAM_CAST(obj) ((GstAdaptiveDemux2Stream *)obj)
|
||||
|
||||
typedef struct _GstAdaptiveDemux2Stream GstAdaptiveDemux2Stream;
|
||||
typedef GstObjectClass GstAdaptiveDemux2StreamClass;
|
||||
|
||||
|
||||
/**
|
||||
* GST_ADAPTIVE_DEMUX_SINK_NAME:
|
||||
*
|
||||
* The name of the templates for the sink pad.
|
||||
*/
|
||||
#define GST_ADAPTIVE_DEMUX_SINK_NAME "sink"
|
||||
|
||||
/**
|
||||
* GST_ADAPTIVE_DEMUX_SINK_PAD:
|
||||
* @obj: a #GstAdaptiveDemux
|
||||
*
|
||||
* Gives the pointer to the sink #GstPad object of the element.
|
||||
*/
|
||||
#define GST_ADAPTIVE_DEMUX_SINK_PAD(obj) (((GstAdaptiveDemux *) (obj))->sinkpad)
|
||||
|
||||
#define GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS(obj) ((((GstAdaptiveDemux*)(obj))->segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) == GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)
|
||||
|
||||
#define GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER(obj) (((GstAdaptiveDemux2Stream *) (obj))->need_header)
|
||||
|
||||
/**
|
||||
* GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME:
|
||||
*
|
||||
* Name of the ELEMENT type messages posted by dashdemux with statistics.
|
||||
*
|
||||
* Since: 1.6
|
||||
*/
|
||||
#define GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME "adaptive-streaming-statistics"
|
||||
|
||||
#define GST_ELEMENT_ERROR_FROM_ERROR(el, msg, err) G_STMT_START { \
|
||||
gchar *__dbg = g_strdup_printf ("%s: %s", msg, err->message); \
|
||||
GST_WARNING_OBJECT (el, "error: %s", __dbg); \
|
||||
gst_element_message_full (GST_ELEMENT(el), GST_MESSAGE_ERROR, \
|
||||
err->domain, err->code, \
|
||||
NULL, __dbg, __FILE__, GST_FUNCTION, __LINE__); \
|
||||
g_clear_error (&err); \
|
||||
} G_STMT_END
|
||||
|
||||
/* DEPRECATED */
|
||||
#define GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_1
|
||||
|
||||
/* Current fragment download should be aborted and restarted. The parent class
|
||||
* will call ::update_fragment_info() again to get the updated information.
|
||||
*/
|
||||
#define GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_2
|
||||
|
||||
typedef enum _GstAdaptiveDemux2StreamState GstAdaptiveDemux2StreamState;
|
||||
|
||||
typedef struct _GstAdaptiveDemux2StreamFragment GstAdaptiveDemux2StreamFragment;
|
||||
typedef struct _GstAdaptiveDemuxTrack GstAdaptiveDemuxTrack;
|
||||
typedef struct _GstAdaptiveDemuxPeriod GstAdaptiveDemuxPeriod;
|
||||
typedef struct _GstAdaptiveDemux GstAdaptiveDemux;
|
||||
typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass;
|
||||
typedef struct _GstAdaptiveDemuxPrivate GstAdaptiveDemuxPrivate;
|
||||
|
||||
struct _GstAdaptiveDemux2StreamFragment
|
||||
{
|
||||
/* The period-local stream time for the given fragment. */
|
||||
GstClockTimeDiff stream_time;
|
||||
GstClockTime duration;
|
||||
|
||||
gchar *uri;
|
||||
gint64 range_start;
|
||||
gint64 range_end;
|
||||
|
||||
/* when chunked downloading is used, may be be updated need_another_chunk() */
|
||||
gint chunk_size;
|
||||
|
||||
/* when headers are needed */
|
||||
gchar *header_uri;
|
||||
gint64 header_range_start;
|
||||
gint64 header_range_end;
|
||||
|
||||
/* when index is needed */
|
||||
gchar *index_uri;
|
||||
gint64 index_range_start;
|
||||
gint64 index_range_end;
|
||||
|
||||
gboolean finished;
|
||||
};
|
||||
|
||||
struct _GstAdaptiveDemuxTrack
|
||||
{
|
||||
gint ref_count;
|
||||
|
||||
/* Demux */
|
||||
GstAdaptiveDemux *demux;
|
||||
|
||||
/* Stream type */
|
||||
GstStreamType type;
|
||||
|
||||
/* Stream flags */
|
||||
GstStreamFlags flags;
|
||||
|
||||
/* Unique identifier */
|
||||
gchar *stream_id;
|
||||
|
||||
/* Unique identifier of the internal stream produced
|
||||
* by parsebin for the Stream this track comes from */
|
||||
gchar *upstream_stream_id;
|
||||
|
||||
/* Generic *elementary stream* caps */
|
||||
GstCaps *generic_caps;
|
||||
|
||||
/* Generic metadata */
|
||||
GstTagList *tags;
|
||||
|
||||
/* The stream object */
|
||||
GstStream *stream_object;
|
||||
|
||||
/* If TRUE, this track should be filled */
|
||||
gboolean selected;
|
||||
|
||||
/* If TRUE, this track is currently being outputted */
|
||||
gboolean active;
|
||||
|
||||
/* If TRUE, it is no longer selected but still being outputted. */
|
||||
gboolean draining;
|
||||
|
||||
/* FIXME : Replace by actual track element */
|
||||
GstElement *element;
|
||||
|
||||
/* The level at which 100% buffering is achieved */
|
||||
GstClockTime buffering_threshold;
|
||||
|
||||
/* The sinkpad receives parsed elementary stream */
|
||||
GstPad *sinkpad;
|
||||
|
||||
/* The pending parsebin source pad (used in case streams from parsebin get updated) (ref taken) */
|
||||
GstPad *pending_srcpad;
|
||||
|
||||
/* Data storage */
|
||||
GstQueueArray *queue;
|
||||
|
||||
/* Sticky event storage for this track */
|
||||
GstEventStore sticky_events;
|
||||
|
||||
/* ============== */
|
||||
/* Input tracking */
|
||||
|
||||
/* The track received EOS */
|
||||
gboolean eos;
|
||||
|
||||
/* Level to wait until download can commence */
|
||||
GstClockTime waiting_del_level;
|
||||
|
||||
/* Input segment and time (in running time) */
|
||||
GstSegment input_segment;
|
||||
GstClockTimeDiff input_time;
|
||||
guint64 input_segment_seqnum;
|
||||
|
||||
/* ================= */
|
||||
/* Contents tracking */
|
||||
|
||||
/* Current level of queue in bytes and time */
|
||||
guint64 level_bytes;
|
||||
GstClockTime level_time;
|
||||
|
||||
/* =============== */
|
||||
/* Output tracking */
|
||||
|
||||
/* Is the output thread waiting for data on this track ? */
|
||||
gboolean waiting_add;
|
||||
|
||||
/* If TRUE, the next pending GstSegment running time should be updated to the
|
||||
* time stored in update_next_segment_run_ts */
|
||||
gboolean update_next_segment;
|
||||
|
||||
/* Output segment and time (in running time) */
|
||||
GstSegment output_segment;
|
||||
GstClockTimeDiff output_time;
|
||||
|
||||
/* Track position and duration for emitting gap
|
||||
* events */
|
||||
GstClockTime gap_position;
|
||||
GstClockTime gap_duration;
|
||||
|
||||
/* Next running time position pending in queue */
|
||||
GstClockTimeDiff next_position;
|
||||
|
||||
/* If the next output buffer should be marked discont */
|
||||
gboolean output_discont;
|
||||
};
|
||||
|
||||
enum _GstAdaptiveDemux2StreamState {
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT,
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE,
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE,
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE,
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING,
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS,
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED
|
||||
};
|
||||
|
||||
struct _GstAdaptiveDemux2Stream
|
||||
{
|
||||
GstObject object;
|
||||
|
||||
/* FIXME : transition to gstobject->parent */
|
||||
GstAdaptiveDemux *demux;
|
||||
|
||||
/* The period to which the stream belongs, set when adding the stream to the
|
||||
* demuxer */
|
||||
GstAdaptiveDemuxPeriod *period;
|
||||
|
||||
/* The tracks this stream targets */
|
||||
GList *tracks;
|
||||
|
||||
/* The internal parsebin, forward data to track */
|
||||
GstElement *parsebin;
|
||||
GstPad *parsebin_sink;
|
||||
|
||||
gulong pad_added_id, pad_removed_id;
|
||||
|
||||
GstSegment parse_segment;
|
||||
|
||||
/* TRUE if the current stream GstSegment should be sent downstream */
|
||||
gboolean send_segment;
|
||||
/* TRUE if the stream GstSegment requires recalculation (from demuxer
|
||||
segment) */
|
||||
gboolean compute_segment;
|
||||
/* first_and_live applies to compute_segment */
|
||||
gboolean first_and_live;
|
||||
|
||||
/* When restarting, what is the target position (in demux segment) to
|
||||
* begin at */
|
||||
GstClockTime start_position;
|
||||
|
||||
/* Track the current position (in demux segment) of the current fragment */
|
||||
GstClockTime current_position;
|
||||
|
||||
GstCaps *pending_caps;
|
||||
GstTagList *pending_tags;
|
||||
|
||||
GList *pending_events;
|
||||
|
||||
GstFlowReturn last_ret;
|
||||
GError *last_error;
|
||||
|
||||
gboolean discont;
|
||||
|
||||
/* download tooling */
|
||||
gboolean need_header;
|
||||
gboolean need_index;
|
||||
|
||||
gboolean downloading_header;
|
||||
gboolean downloading_index;
|
||||
|
||||
/* persistent, reused download request for fragment data */
|
||||
DownloadRequest *download_request;
|
||||
|
||||
GstAdaptiveDemux2StreamState state;
|
||||
guint pending_cb_id;
|
||||
gboolean download_active;
|
||||
|
||||
guint last_status_code;
|
||||
|
||||
gboolean pending_tracks; /* if we need to discover tracks dynamically for this stream */
|
||||
gboolean download_finished;
|
||||
gboolean cancelled;
|
||||
gboolean replaced; /* replaced in a bitrate switch (used with cancelled) */
|
||||
|
||||
gboolean starting_fragment;
|
||||
gboolean first_fragment_buffer;
|
||||
gint64 download_start_time;
|
||||
gint64 download_total_bytes;
|
||||
gint64 download_end_offset;
|
||||
guint64 current_download_rate;
|
||||
|
||||
/* bitrate of the previous fragment (pre-queue2) */
|
||||
guint64 last_bitrate;
|
||||
|
||||
/* Total last download time, from request to completion */
|
||||
GstClockTime last_download_time;
|
||||
|
||||
/* Average for the last fragments */
|
||||
guint64 moving_bitrate;
|
||||
guint moving_index;
|
||||
guint64 *fragment_bitrates;
|
||||
|
||||
GstAdaptiveDemux2StreamFragment fragment;
|
||||
|
||||
guint download_error_count;
|
||||
|
||||
/* Last collection provided by parsebin */
|
||||
GstStreamCollection *stream_collection;
|
||||
|
||||
/* OR'd set of stream types in this stream */
|
||||
GstStreamType stream_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* GstAdaptiveDemuxPeriod:
|
||||
*
|
||||
* The opaque #GstAdaptiveDemuxPeriod data structure. */
|
||||
struct _GstAdaptiveDemuxPeriod
|
||||
{
|
||||
gint ref_count;
|
||||
|
||||
GstAdaptiveDemux *demux;
|
||||
|
||||
/* TRUE if the streams of this period were prepared and can be started */
|
||||
gboolean prepared;
|
||||
|
||||
|
||||
/* TRUE if the period no longer receives any data (i.e. it is closed) */
|
||||
gboolean closed;
|
||||
|
||||
/* An increasing unique identifier for the period.
|
||||
*
|
||||
* Note: unrelated to dash period id (which can be identical across
|
||||
* periods) */
|
||||
guint period_num;
|
||||
|
||||
/* The list of GstAdaptiveDemux2Stream (ref hold) */
|
||||
GList *streams;
|
||||
|
||||
/* Current collection */
|
||||
GstStreamCollection *collection;
|
||||
|
||||
/* List of available GstAdaptiveDemuxTrack (ref hold) */
|
||||
GList *tracks;
|
||||
|
||||
/* Whether tracks were changed and need re-matching against outputs */
|
||||
gboolean tracks_changed;
|
||||
};
|
||||
|
||||
/**
|
||||
* GstAdaptiveDemux:
|
||||
*
|
||||
* The opaque #GstAdaptiveDemux data structure.
|
||||
*/
|
||||
struct _GstAdaptiveDemux
|
||||
{
|
||||
/*< private >*/
|
||||
GstBin bin;
|
||||
|
||||
gint running;
|
||||
|
||||
/*< protected >*/
|
||||
GstPad *sinkpad;
|
||||
|
||||
DownloadHelper *download_helper;
|
||||
|
||||
/* Protected by TRACKS_LOCK */
|
||||
/* Period used for output */
|
||||
GstAdaptiveDemuxPeriod *output_period;
|
||||
|
||||
/* Period used for input */
|
||||
GstAdaptiveDemuxPeriod *input_period;
|
||||
|
||||
GstSegment segment;
|
||||
gdouble instant_rate_multiplier; /* 1.0 by default, or from instant-rate seek */
|
||||
|
||||
gchar *manifest_uri;
|
||||
gchar *manifest_base_uri;
|
||||
|
||||
/* Properties */
|
||||
gfloat bandwidth_target_ratio; /* ratio of the available bitrate to use */
|
||||
guint connection_speed; /* Available / bandwidth to use set by the application */
|
||||
guint min_bitrate; /* Minimum bitrate to choose */
|
||||
guint max_bitrate; /* Maximum bitrate to choose */
|
||||
|
||||
guint current_download_rate; /* Current estimate of download bitrate */
|
||||
|
||||
/* Buffering levels */
|
||||
GstClockTime max_buffering_time;
|
||||
GstClockTime buffering_high_watermark_time;
|
||||
GstClockTime buffering_low_watermark_time;
|
||||
gdouble buffering_high_watermark_fragments;
|
||||
gdouble buffering_low_watermark_fragments;
|
||||
|
||||
/* video/audio buffer level as minimum of the appropriate streams */
|
||||
GstClockTime current_level_time_video;
|
||||
GstClockTime current_level_time_audio;
|
||||
|
||||
gboolean have_group_id;
|
||||
guint group_id;
|
||||
|
||||
guint next_stream_id;
|
||||
|
||||
/* Realtime clock */
|
||||
GstAdaptiveDemuxClock *realtime_clock;
|
||||
|
||||
/* < private > */
|
||||
GstAdaptiveDemuxPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* GstAdaptiveDemuxClass:
|
||||
*
|
||||
*/
|
||||
struct _GstAdaptiveDemuxClass
|
||||
{
|
||||
/*< private >*/
|
||||
GstBinClass bin_class;
|
||||
|
||||
/*< public >*/
|
||||
|
||||
/**
|
||||
* process_manifest: Parse the manifest
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @manifest: the manifest to be parsed
|
||||
*
|
||||
* Parse the manifest and add the created streams using
|
||||
* gst_adaptive_demux2_stream_new()
|
||||
*
|
||||
* Returns: %TRUE if successful
|
||||
*/
|
||||
gboolean (*process_manifest) (GstAdaptiveDemux * demux, GstBuffer * manifest);
|
||||
|
||||
/**
|
||||
* get_manifest_update_interval:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
*
|
||||
* Used during live streaming, the subclass should return the interval
|
||||
* between successive manifest updates
|
||||
*
|
||||
* Returns: the update interval in microseconds
|
||||
*/
|
||||
gint64 (*get_manifest_update_interval) (GstAdaptiveDemux * demux);
|
||||
|
||||
/**
|
||||
* update_manifest:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
*
|
||||
* During live streaming, this will be called for the subclass to update its
|
||||
* manifest with the new version. By default it fetches the manifest URI
|
||||
* and passes it to GstAdaptiveDemux::update_manifest_data().
|
||||
*
|
||||
* Returns: #GST_FLOW_OK is all succeeded, #GST_FLOW_EOS if the stream ended
|
||||
* or #GST_FLOW_ERROR if an error happened
|
||||
*/
|
||||
GstFlowReturn (*update_manifest) (GstAdaptiveDemux * demux);
|
||||
|
||||
/**
|
||||
* update_manifest_data:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @buf: Downloaded manifest data
|
||||
*
|
||||
* During live streaming, this will be called for the subclass to update its
|
||||
* manifest with the new version
|
||||
*
|
||||
* Returns: #GST_FLOW_OK is all succeeded, #GST_FLOW_EOS if the stream ended
|
||||
* or #GST_FLOW_ERROR if an error happened
|
||||
*/
|
||||
GstFlowReturn (*update_manifest_data) (GstAdaptiveDemux * demux, GstBuffer * buf);
|
||||
|
||||
gboolean (*is_live) (GstAdaptiveDemux * demux);
|
||||
GstClockTime (*get_duration) (GstAdaptiveDemux * demux);
|
||||
|
||||
/**
|
||||
* reset:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
*
|
||||
* Reset the internal state of the subclass, getting ready to restart with
|
||||
* a new stream afterwards
|
||||
*/
|
||||
void (*reset) (GstAdaptiveDemux * demux);
|
||||
|
||||
/**
|
||||
* seek:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @seek: a seek #GstEvent
|
||||
*
|
||||
* The demuxer should seek on all its streams to the specified position
|
||||
* in the seek event
|
||||
*
|
||||
* Returns: %TRUE if successful
|
||||
*/
|
||||
gboolean (*seek) (GstAdaptiveDemux * demux, GstEvent * seek);
|
||||
|
||||
/**
|
||||
* has_next_period:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
*
|
||||
* Checks if there is a next period following the current one.
|
||||
* DASH can have multiple medias chained in its manifest, when one finishes
|
||||
* this function is called to verify if there is a new period to be played
|
||||
* in sequence.
|
||||
*
|
||||
* Returns: %TRUE if there is another period
|
||||
*/
|
||||
gboolean (*has_next_period) (GstAdaptiveDemux * demux);
|
||||
/**
|
||||
* advance_period:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
*
|
||||
* Advances the manifest to the next period. New streams should be created
|
||||
* using gst_adaptive_demux2_stream_new().
|
||||
*/
|
||||
void (*advance_period) (GstAdaptiveDemux * demux);
|
||||
|
||||
GstFlowReturn (*stream_seek) (GstAdaptiveDemux2Stream * stream,
|
||||
gboolean forward,
|
||||
GstSeekFlags flags,
|
||||
GstClockTimeDiff target_ts,
|
||||
GstClockTimeDiff * final_ts);
|
||||
gboolean (*stream_has_next_fragment) (GstAdaptiveDemux2Stream * stream);
|
||||
GstFlowReturn (*stream_advance_fragment) (GstAdaptiveDemux2Stream * stream);
|
||||
|
||||
/**
|
||||
* stream_can_start:
|
||||
* @demux: The #GstAdaptiveDemux
|
||||
* @stream: a #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* Called before starting a @stream. sub-classes can return %FALSE if more
|
||||
* information is required before it can be started. Sub-classes will have to
|
||||
* call gst_adaptive_demux2_stream_start() when the stream should be started.
|
||||
*/
|
||||
gboolean (*stream_can_start) (GstAdaptiveDemux *demux,
|
||||
GstAdaptiveDemux2Stream *stream);
|
||||
|
||||
/**
|
||||
* stream_update_tracks:
|
||||
* @demux: The #GstAdaptiveDemux
|
||||
* @stream: A #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* Called whenever the base class collected a @collection on a @stream which has
|
||||
* pending tracks to be created. Subclasses should override this if they
|
||||
* create streams without tracks.
|
||||
*
|
||||
* * create the various tracks by analyzing the @stream stream_collection
|
||||
* * Set the track upstream_stream_id to the corresponding stream_id from the collection
|
||||
*/
|
||||
void (*stream_update_tracks) (GstAdaptiveDemux *demux,
|
||||
GstAdaptiveDemux2Stream *stream);
|
||||
/**
|
||||
* need_another_chunk:
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* If chunked downloading is used (chunk_size != 0) this is called once a
|
||||
* chunk is finished to decide whether more has to be downloaded or not.
|
||||
* May update chunk_size to a different value
|
||||
*/
|
||||
gboolean (*need_another_chunk) (GstAdaptiveDemux2Stream * stream);
|
||||
|
||||
/**
|
||||
* stream_update_fragment_info:
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* Requests the stream to set the information about the current fragment to its
|
||||
* current fragment struct
|
||||
*
|
||||
* Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS
|
||||
* if there is no fragment.
|
||||
*/
|
||||
GstFlowReturn (*stream_update_fragment_info) (GstAdaptiveDemux2Stream * stream);
|
||||
/**
|
||||
* stream_select_bitrate:
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
* @bitrate: the bitrate to select (in bytes per second)
|
||||
*
|
||||
* The stream should try to select the bitrate that is the greater, but not
|
||||
* greater than the requested bitrate. If it needs a codec change it should
|
||||
* create the new stream using gst_adaptive_demux2_stream_new(). If it only
|
||||
* needs a caps change it should set the new caps using
|
||||
* gst_adaptive_demux2_stream_set_caps().
|
||||
*
|
||||
* Returns: %TRUE if the stream changed bitrate, %FALSE otherwise
|
||||
*/
|
||||
gboolean (*stream_select_bitrate) (GstAdaptiveDemux2Stream * stream, guint64 bitrate);
|
||||
/**
|
||||
* stream_get_fragment_waiting_time:
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* For live streams, requests how much time should be waited before starting
|
||||
* to download the fragment. This is useful to avoid downloading a fragment that
|
||||
* isn't available yet.
|
||||
*
|
||||
* Returns: The waiting time in as a #GstClockTime
|
||||
*/
|
||||
GstClockTime (*stream_get_fragment_waiting_time) (GstAdaptiveDemux2Stream * stream);
|
||||
|
||||
/**
|
||||
* start_fragment:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* Notifies the subclass that the given stream is starting the download
|
||||
* of a new fragment. Can be used to reset/init internal state that is
|
||||
* needed before each fragment, like decryption engines.
|
||||
*
|
||||
* Returns: %TRUE if successful.
|
||||
*/
|
||||
gboolean (*start_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream);
|
||||
/**
|
||||
* finish_fragment:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* Notifies the subclass that a fragment download was finished.
|
||||
* It can be used to cleanup internal state after a fragment and
|
||||
* also push any pending data before moving to the next fragment.
|
||||
*/
|
||||
GstFlowReturn (*finish_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream);
|
||||
/**
|
||||
* data_received:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
* @buffer: #GstBuffer
|
||||
*
|
||||
* Notifies the subclass that a fragment chunk was downloaded. The subclass
|
||||
* can look at the data and modify/push data as desired.
|
||||
*
|
||||
* Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error.
|
||||
*/
|
||||
GstFlowReturn (*data_received) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
|
||||
|
||||
/**
|
||||
* get_live_seek_range:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @start: pointer to put the start position allowed to seek to
|
||||
* @stop: pointer to put the stop position allowed to seek to
|
||||
*
|
||||
* Gets the allowed seek start and stop positions for the current live stream
|
||||
*
|
||||
* Return: %TRUE if successful
|
||||
*/
|
||||
gboolean (*get_live_seek_range) (GstAdaptiveDemux * demux, gint64 * start, gint64 * stop);
|
||||
|
||||
/**
|
||||
* get_presentation_offset:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
* @stream: #GstAdaptiveDemux2Stream
|
||||
*
|
||||
* Gets the delay to apply to @stream.
|
||||
*
|
||||
* Return: a #GstClockTime representing the (positive) time offset to apply to
|
||||
* @stream.
|
||||
*/
|
||||
GstClockTime (*get_presentation_offset) (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream);
|
||||
|
||||
/**
|
||||
* get_period_start_time:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
*
|
||||
* Gets the start time of the current period. Timestamps are resetting to 0
|
||||
* after each period but we have to maintain a continuous stream and running
|
||||
* time so need to know the start time of the current period.
|
||||
*
|
||||
* Return: a #GstClockTime representing the start time of the currently
|
||||
* selected period.
|
||||
*/
|
||||
GstClockTime (*get_period_start_time) (GstAdaptiveDemux *demux);
|
||||
|
||||
/**
|
||||
* requires_periodical_playlist_update:
|
||||
* @demux: #GstAdaptiveDemux
|
||||
*
|
||||
* Some adaptive streaming protocols allow the client to download
|
||||
* the playlist once and build up the fragment list based on the
|
||||
* current fragment metadata. For those protocols the demuxer
|
||||
* doesn't need to periodically refresh the playlist. This vfunc
|
||||
* is relevant only for live playback scenarios.
|
||||
*
|
||||
* Return: %TRUE if the playlist needs to be refreshed periodically by the demuxer.
|
||||
*/
|
||||
gboolean (*requires_periodical_playlist_update) (GstAdaptiveDemux * demux);
|
||||
};
|
||||
|
||||
GType gst_adaptive_demux_ng_get_type (void);
|
||||
|
||||
GType gst_adaptive_demux2_stream_get_type (void);
|
||||
|
||||
gboolean gst_adaptive_demux2_add_stream (GstAdaptiveDemux *demux,
|
||||
GstAdaptiveDemux2Stream *stream);
|
||||
|
||||
gboolean gst_adaptive_demux2_stream_add_track (GstAdaptiveDemux2Stream *stream,
|
||||
GstAdaptiveDemuxTrack *track);
|
||||
|
||||
GstAdaptiveDemuxTrack *gst_adaptive_demux_track_new (GstAdaptiveDemux *demux,
|
||||
GstStreamType type,
|
||||
GstStreamFlags flags,
|
||||
gchar *stream_id,
|
||||
GstCaps *caps,
|
||||
GstTagList *tags);
|
||||
GstAdaptiveDemuxTrack *gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack *track);
|
||||
void gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack *track);
|
||||
|
||||
|
||||
void gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream,
|
||||
GstCaps * caps);
|
||||
|
||||
void gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream,
|
||||
GstTagList * tags);
|
||||
|
||||
void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f);
|
||||
|
||||
GstFlowReturn gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream,
|
||||
GstBuffer * buffer);
|
||||
|
||||
GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream,
|
||||
GstClockTime duration);
|
||||
|
||||
gboolean gst_adaptive_demux_start_new_period (GstAdaptiveDemux * demux);
|
||||
|
||||
void
|
||||
gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream);
|
||||
|
||||
void gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream,
|
||||
GstEvent * event);
|
||||
|
||||
gboolean gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream *stream);
|
||||
gboolean gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream);
|
||||
|
||||
GstClockTime gst_adaptive_demux2_get_monotonic_time (GstAdaptiveDemux * demux);
|
||||
|
||||
GDateTime *gst_adaptive_demux2_get_client_now_utc (GstAdaptiveDemux * demux);
|
||||
|
||||
gboolean gst_adaptive_demux2_is_running (GstAdaptiveDemux * demux);
|
||||
|
||||
GstClockTime gst_adaptive_demux2_get_qos_earliest_time (GstAdaptiveDemux *demux);
|
||||
|
||||
GstCaps * gst_codec_utils_caps_from_iso_rfc6831 (gchar * codec);
|
||||
|
||||
gdouble gst_adaptive_demux_play_rate (GstAdaptiveDemux *demux);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,679 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
|
||||
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||
* Copyright (C) 2021-2022 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include "gstadaptivedemuxutils.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
|
||||
#define GST_CAT_DEFAULT adaptivedemux2_debug
|
||||
|
||||
struct _GstAdaptiveDemuxClock
|
||||
{
|
||||
gint ref_count;
|
||||
|
||||
GstClock *gst_clock;
|
||||
GstClockTimeDiff clock_offset; /* offset between realtime_clock and UTC */
|
||||
};
|
||||
|
||||
struct _GstAdaptiveDemuxLoop
|
||||
{
|
||||
gint ref_count;
|
||||
GCond cond;
|
||||
GMutex lock;
|
||||
|
||||
GRecMutex context_lock;
|
||||
|
||||
GThread *thread;
|
||||
GMainLoop *loop;
|
||||
GMainContext *context;
|
||||
|
||||
gboolean stopped;
|
||||
gboolean paused;
|
||||
};
|
||||
|
||||
GstAdaptiveDemuxClock *
|
||||
gst_adaptive_demux_clock_new (void)
|
||||
{
|
||||
GstAdaptiveDemuxClock *clock = g_slice_new (GstAdaptiveDemuxClock);
|
||||
GstClockType clock_type = GST_CLOCK_TYPE_OTHER;
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
g_atomic_int_set (&clock->ref_count, 1);
|
||||
|
||||
clock->gst_clock = gst_system_clock_obtain ();
|
||||
g_assert (clock->gst_clock != NULL);
|
||||
|
||||
gobject_class = G_OBJECT_GET_CLASS (clock->gst_clock);
|
||||
if (g_object_class_find_property (gobject_class, "clock-type")) {
|
||||
g_object_get (clock->gst_clock, "clock-type", &clock_type, NULL);
|
||||
} else {
|
||||
GST_WARNING ("System clock does not have clock-type property");
|
||||
}
|
||||
|
||||
if (clock_type == GST_CLOCK_TYPE_REALTIME) {
|
||||
clock->clock_offset = 0;
|
||||
} else {
|
||||
GDateTime *utc_now;
|
||||
|
||||
utc_now = g_date_time_new_now_utc ();
|
||||
gst_adaptive_demux_clock_set_utc_time (clock, utc_now);
|
||||
g_date_time_unref (utc_now);
|
||||
}
|
||||
|
||||
return clock;
|
||||
}
|
||||
|
||||
GstAdaptiveDemuxClock *
|
||||
gst_adaptive_demux_clock_ref (GstAdaptiveDemuxClock * clock)
|
||||
{
|
||||
g_return_val_if_fail (clock != NULL, NULL);
|
||||
g_atomic_int_inc (&clock->ref_count);
|
||||
return clock;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_clock_unref (GstAdaptiveDemuxClock * clock)
|
||||
{
|
||||
g_return_if_fail (clock != NULL);
|
||||
if (g_atomic_int_dec_and_test (&clock->ref_count)) {
|
||||
gst_object_unref (clock->gst_clock);
|
||||
g_slice_free (GstAdaptiveDemuxClock, clock);
|
||||
}
|
||||
}
|
||||
|
||||
GstClockTime
|
||||
gst_adaptive_demux_clock_get_time (GstAdaptiveDemuxClock * clock)
|
||||
{
|
||||
g_return_val_if_fail (clock != NULL, GST_CLOCK_TIME_NONE);
|
||||
return gst_clock_get_time (clock->gst_clock);
|
||||
}
|
||||
|
||||
GDateTime *
|
||||
gst_adaptive_demux_clock_get_now_utc (GstAdaptiveDemuxClock * clock)
|
||||
{
|
||||
GstClockTime rtc_now;
|
||||
GDateTime *unix_datetime;
|
||||
GDateTime *result_datetime;
|
||||
gint64 utc_now_in_us;
|
||||
|
||||
rtc_now = gst_clock_get_time (clock->gst_clock);
|
||||
utc_now_in_us = clock->clock_offset + GST_TIME_AS_USECONDS (rtc_now);
|
||||
unix_datetime =
|
||||
g_date_time_new_from_unix_utc (utc_now_in_us / G_TIME_SPAN_SECOND);
|
||||
result_datetime =
|
||||
g_date_time_add (unix_datetime, utc_now_in_us % G_TIME_SPAN_SECOND);
|
||||
g_date_time_unref (unix_datetime);
|
||||
return result_datetime;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_clock_set_utc_time (GstAdaptiveDemuxClock * clock,
|
||||
GDateTime * utc_now)
|
||||
{
|
||||
GstClockTime rtc_now = gst_clock_get_time (clock->gst_clock);
|
||||
GstClockTimeDiff clock_offset;
|
||||
|
||||
clock_offset =
|
||||
g_date_time_to_unix (utc_now) * G_TIME_SPAN_SECOND +
|
||||
g_date_time_get_microsecond (utc_now) - GST_TIME_AS_USECONDS (rtc_now);
|
||||
|
||||
GST_INFO ("Changing UTC clock offset to %" GST_STIME_FORMAT
|
||||
" was %" GST_STIME_FORMAT, GST_STIME_ARGS (clock_offset),
|
||||
GST_STIME_ARGS (clock->clock_offset));
|
||||
|
||||
clock->clock_offset = clock_offset;
|
||||
}
|
||||
|
||||
GstAdaptiveDemuxLoop *
|
||||
gst_adaptive_demux_loop_new (void)
|
||||
{
|
||||
GstAdaptiveDemuxLoop *loop = g_slice_new0 (GstAdaptiveDemuxLoop);
|
||||
g_atomic_int_set (&loop->ref_count, 1);
|
||||
|
||||
g_mutex_init (&loop->lock);
|
||||
g_rec_mutex_init (&loop->context_lock);
|
||||
g_cond_init (&loop->cond);
|
||||
|
||||
loop->stopped = TRUE;
|
||||
loop->paused = FALSE;
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
GstAdaptiveDemuxLoop *
|
||||
gst_adaptive_demux_loop_ref (GstAdaptiveDemuxLoop * loop)
|
||||
{
|
||||
g_return_val_if_fail (loop != NULL, NULL);
|
||||
g_atomic_int_inc (&loop->ref_count);
|
||||
return loop;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_loop_unref (GstAdaptiveDemuxLoop * loop)
|
||||
{
|
||||
g_return_if_fail (loop != NULL);
|
||||
if (g_atomic_int_dec_and_test (&loop->ref_count)) {
|
||||
gst_adaptive_demux_loop_stop (loop, TRUE);
|
||||
|
||||
g_mutex_clear (&loop->lock);
|
||||
g_rec_mutex_clear (&loop->context_lock);
|
||||
g_cond_clear (&loop->cond);
|
||||
|
||||
g_slice_free (GstAdaptiveDemuxLoop, loop);
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
_gst_adaptive_demux_loop_thread (GstAdaptiveDemuxLoop * loop)
|
||||
{
|
||||
g_mutex_lock (&loop->lock);
|
||||
|
||||
loop->loop = g_main_loop_new (loop->context, FALSE);
|
||||
|
||||
while (!loop->stopped) {
|
||||
g_mutex_unlock (&loop->lock);
|
||||
|
||||
g_rec_mutex_lock (&loop->context_lock);
|
||||
|
||||
g_main_context_push_thread_default (loop->context);
|
||||
g_main_loop_run (loop->loop);
|
||||
g_main_context_pop_thread_default (loop->context);
|
||||
|
||||
g_rec_mutex_unlock (&loop->context_lock);
|
||||
|
||||
g_mutex_lock (&loop->lock);
|
||||
while (loop->paused)
|
||||
g_cond_wait (&loop->cond, &loop->lock);
|
||||
}
|
||||
|
||||
g_main_loop_unref (loop->loop);
|
||||
loop->loop = NULL;
|
||||
|
||||
g_cond_broadcast (&loop->cond);
|
||||
g_mutex_unlock (&loop->lock);
|
||||
|
||||
g_main_context_unref (loop->context);
|
||||
loop->context = NULL;
|
||||
|
||||
gst_adaptive_demux_loop_unref (loop);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_loop_start (GstAdaptiveDemuxLoop * loop)
|
||||
{
|
||||
g_mutex_lock (&loop->lock);
|
||||
if (loop->thread != NULL && !loop->stopped)
|
||||
goto done; /* Already running */
|
||||
|
||||
loop->stopped = FALSE;
|
||||
loop->context = g_main_context_new ();
|
||||
|
||||
loop->thread =
|
||||
g_thread_new ("AdaptiveDemux",
|
||||
(GThreadFunc) _gst_adaptive_demux_loop_thread,
|
||||
gst_adaptive_demux_loop_ref (loop));
|
||||
|
||||
done:
|
||||
g_mutex_unlock (&loop->lock);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_quit_cb (GstAdaptiveDemuxLoop * loop)
|
||||
{
|
||||
g_main_loop_quit (loop->loop);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_loop_stop (GstAdaptiveDemuxLoop * loop, gboolean wait)
|
||||
{
|
||||
g_mutex_lock (&loop->lock);
|
||||
loop->stopped = TRUE;
|
||||
|
||||
if (loop->loop != NULL) {
|
||||
GSource *s = g_idle_source_new ();
|
||||
g_source_set_callback (s, (GSourceFunc) do_quit_cb,
|
||||
gst_adaptive_demux_loop_ref (loop),
|
||||
(GDestroyNotify) gst_adaptive_demux_loop_unref);
|
||||
g_source_attach (s, loop->context);
|
||||
g_source_unref (s);
|
||||
|
||||
if (wait) {
|
||||
while (loop->loop != NULL)
|
||||
g_cond_wait (&loop->cond, &loop->lock);
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock (&loop->lock);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_adaptive_demux_loop_pause_and_lock (GstAdaptiveDemuxLoop * loop)
|
||||
{
|
||||
/* Try and acquire the context lock directly. This will succeed
|
||||
* if called when the loop is not running, and we can avoid
|
||||
* adding an unnecessary extra idle source to quit the loop. */
|
||||
if (!g_rec_mutex_trylock (&loop->context_lock)) {
|
||||
g_mutex_lock (&loop->lock);
|
||||
|
||||
if (loop->stopped) {
|
||||
g_mutex_unlock (&loop->lock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
loop->paused = TRUE;
|
||||
|
||||
{
|
||||
GSource *s = g_idle_source_new ();
|
||||
g_source_set_callback (s, (GSourceFunc) do_quit_cb,
|
||||
gst_adaptive_demux_loop_ref (loop),
|
||||
(GDestroyNotify) gst_adaptive_demux_loop_unref);
|
||||
g_source_attach (s, loop->context);
|
||||
g_source_unref (s);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&loop->lock);
|
||||
|
||||
g_rec_mutex_lock (&loop->context_lock);
|
||||
}
|
||||
g_main_context_push_thread_default (loop->context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_adaptive_demux_loop_unlock_and_unpause (GstAdaptiveDemuxLoop * loop)
|
||||
{
|
||||
g_main_context_pop_thread_default (loop->context);
|
||||
g_rec_mutex_unlock (&loop->context_lock);
|
||||
|
||||
g_mutex_lock (&loop->lock);
|
||||
loop->paused = FALSE;
|
||||
|
||||
if (loop->stopped) {
|
||||
g_mutex_unlock (&loop->lock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Wake up the loop to run again */
|
||||
g_cond_broadcast (&loop->cond);
|
||||
g_mutex_unlock (&loop->lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
guint
|
||||
gst_adaptive_demux_loop_call (GstAdaptiveDemuxLoop * loop, GSourceFunc func,
|
||||
gpointer data, GDestroyNotify notify)
|
||||
{
|
||||
guint ret = 0;
|
||||
|
||||
g_mutex_lock (&loop->lock);
|
||||
if (loop->context) {
|
||||
GSource *s = g_idle_source_new ();
|
||||
g_source_set_callback (s, func, data, notify);
|
||||
ret = g_source_attach (s, loop->context);
|
||||
g_source_unref (s);
|
||||
} else if (notify != NULL) {
|
||||
notify (data);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&loop->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
guint
|
||||
gst_adaptive_demux_loop_call_delayed (GstAdaptiveDemuxLoop * loop,
|
||||
GstClockTime delay, GSourceFunc func, gpointer data, GDestroyNotify notify)
|
||||
{
|
||||
guint ret = 0;
|
||||
|
||||
g_mutex_lock (&loop->lock);
|
||||
if (loop->context) {
|
||||
GSource *s = g_timeout_source_new (GST_TIME_AS_MSECONDS (delay));
|
||||
g_source_set_callback (s, func, data, notify);
|
||||
ret = g_source_attach (s, loop->context);
|
||||
g_source_unref (s);
|
||||
} else if (notify != NULL) {
|
||||
notify (data);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&loop->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
gst_adaptive_demux_loop_cancel_call (GstAdaptiveDemuxLoop * loop, guint cb_id)
|
||||
{
|
||||
GSource *s;
|
||||
|
||||
g_mutex_lock (&loop->lock);
|
||||
s = g_main_context_find_source_by_id (loop->context, cb_id);
|
||||
if (s)
|
||||
g_source_destroy (s);
|
||||
g_mutex_unlock (&loop->lock);
|
||||
}
|
||||
|
||||
struct Rfc5322TimeZone
|
||||
{
|
||||
const gchar *name;
|
||||
gfloat tzoffset;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse an RFC5322 (section 3.3) date-time from the Date: field in the
|
||||
HTTP response.
|
||||
See https://tools.ietf.org/html/rfc5322#section-3.3
|
||||
*/
|
||||
GstDateTime *
|
||||
gst_adaptive_demux_util_parse_http_head_date (const gchar * http_date)
|
||||
{
|
||||
static const gchar *months[] = { NULL, "Jan", "Feb", "Mar", "Apr",
|
||||
"May", "Jun", "Jul", "Aug",
|
||||
"Sep", "Oct", "Nov", "Dec", NULL
|
||||
};
|
||||
static const struct Rfc5322TimeZone timezones[] = {
|
||||
{"Z", 0},
|
||||
{"UT", 0},
|
||||
{"GMT", 0},
|
||||
{"BST", 1},
|
||||
{"EST", -5},
|
||||
{"EDT", -4},
|
||||
{"CST", -6},
|
||||
{"CDT", -5},
|
||||
{"MST", -7},
|
||||
{"MDT", -6},
|
||||
{"PST", -8},
|
||||
{"PDT", -7},
|
||||
{NULL, 0}
|
||||
};
|
||||
gint ret;
|
||||
const gchar *pos;
|
||||
gint year = -1, month = -1, day = -1, hour = -1, minute = -1, second = -1;
|
||||
gchar zone[6];
|
||||
gchar monthstr[4];
|
||||
gfloat tzoffset = 0;
|
||||
gboolean parsed_tz = FALSE;
|
||||
|
||||
g_return_val_if_fail (http_date != NULL, NULL);
|
||||
|
||||
/* skip optional text version of day of the week */
|
||||
pos = strchr (http_date, ',');
|
||||
if (pos)
|
||||
pos++;
|
||||
else
|
||||
pos = http_date;
|
||||
|
||||
ret =
|
||||
sscanf (pos, "%02d %3s %04d %02d:%02d:%02d %5s", &day, monthstr, &year,
|
||||
&hour, &minute, &second, zone);
|
||||
|
||||
if (ret == 7) {
|
||||
gchar *z = zone;
|
||||
gint i;
|
||||
|
||||
for (i = 1; months[i]; ++i) {
|
||||
if (g_ascii_strncasecmp (months[i], monthstr, strlen (months[i])) == 0) {
|
||||
month = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; timezones[i].name && !parsed_tz; ++i) {
|
||||
if (g_ascii_strncasecmp (timezones[i].name, z,
|
||||
strlen (timezones[i].name)) == 0) {
|
||||
tzoffset = timezones[i].tzoffset;
|
||||
parsed_tz = TRUE;
|
||||
}
|
||||
}
|
||||
if (!parsed_tz) {
|
||||
gint hh, mm;
|
||||
gboolean neg = FALSE;
|
||||
/* check if it is in the form +-HHMM */
|
||||
if (*z == '+' || *z == '-') {
|
||||
if (*z == '+')
|
||||
++z;
|
||||
else if (*z == '-') {
|
||||
++z;
|
||||
neg = TRUE;
|
||||
}
|
||||
ret = sscanf (z, "%02d%02d", &hh, &mm);
|
||||
if (ret == 2) {
|
||||
tzoffset = hh;
|
||||
tzoffset += mm / 60.0;
|
||||
if (neg)
|
||||
tzoffset = -tzoffset;
|
||||
parsed_tz = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Accept year in both 2 digit or 4 digit format */
|
||||
if (year < 100)
|
||||
year += 2000;
|
||||
}
|
||||
|
||||
if (month < 1 || !parsed_tz)
|
||||
return NULL;
|
||||
|
||||
return gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean delivered;
|
||||
GstEvent *event;
|
||||
} PadEvent;
|
||||
|
||||
void
|
||||
gst_event_store_init (GstEventStore * store)
|
||||
{
|
||||
store->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
|
||||
store->events_pending = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_event_store_flush (GstEventStore * store)
|
||||
{
|
||||
guint i, len;
|
||||
GArray *events = store->events;
|
||||
|
||||
len = events->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
PadEvent *ev = &g_array_index (events, PadEvent, i);
|
||||
GstEvent *event = ev->event;
|
||||
|
||||
ev->event = NULL;
|
||||
|
||||
gst_event_unref (event);
|
||||
}
|
||||
g_array_set_size (events, 0);
|
||||
|
||||
store->events_pending = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_event_store_deinit (GstEventStore * store)
|
||||
{
|
||||
gst_event_store_flush (store);
|
||||
g_array_free (store->events, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
gst_event_store_insert_event (GstEventStore * store, GstEvent * event,
|
||||
gboolean delivered)
|
||||
{
|
||||
guint i, len;
|
||||
GstEventType type;
|
||||
GArray *events;
|
||||
GQuark name_id = 0;
|
||||
gboolean insert = TRUE;
|
||||
|
||||
type = GST_EVENT_TYPE (event);
|
||||
|
||||
if (type & GST_EVENT_TYPE_STICKY_MULTI)
|
||||
name_id = gst_structure_get_name_id (gst_event_get_structure (event));
|
||||
|
||||
events = store->events;
|
||||
|
||||
len = events->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
PadEvent *ev = &g_array_index (events, PadEvent, i);
|
||||
|
||||
if (ev->event == NULL)
|
||||
continue;
|
||||
|
||||
if (type == GST_EVENT_TYPE (ev->event)) {
|
||||
/* matching types, check matching name if needed */
|
||||
if (name_id && !gst_event_has_name_id (ev->event, name_id))
|
||||
continue;
|
||||
|
||||
/* overwrite if different */
|
||||
if (gst_event_replace (&ev->event, event)) {
|
||||
ev->delivered = delivered;
|
||||
/* If the event was not delivered, mark that we have a pending
|
||||
* undelivered event */
|
||||
if (!delivered)
|
||||
store->events_pending = TRUE;
|
||||
}
|
||||
|
||||
insert = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (type < GST_EVENT_TYPE (ev->event) || (type != GST_EVENT_TYPE (ev->event)
|
||||
&& GST_EVENT_TYPE (ev->event) == GST_EVENT_EOS)) {
|
||||
/* STREAM_START, CAPS and SEGMENT must be delivered in this order. By
|
||||
* storing the sticky ordered we can check that this is respected. */
|
||||
if (G_UNLIKELY (GST_EVENT_TYPE (ev->event) <= GST_EVENT_SEGMENT
|
||||
|| GST_EVENT_TYPE (ev->event) == GST_EVENT_EOS))
|
||||
g_warning (G_STRLOC
|
||||
":%s:<store %p> Sticky event misordering, got '%s' before '%s'",
|
||||
G_STRFUNC, store,
|
||||
gst_event_type_get_name (GST_EVENT_TYPE (ev->event)),
|
||||
gst_event_type_get_name (type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (insert) {
|
||||
PadEvent ev;
|
||||
ev.event = gst_event_ref (event);
|
||||
ev.delivered = delivered;
|
||||
g_array_insert_val (events, i, ev);
|
||||
|
||||
/* If the event was not delivered, mark that we have a pending
|
||||
* undelivered event */
|
||||
if (!delivered)
|
||||
store->events_pending = TRUE;
|
||||
GST_LOG ("store %p stored sticky event %s", store,
|
||||
GST_EVENT_TYPE_NAME (event));
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the first non-pending event and return a ref to it, owned by the caller */
|
||||
GstEvent *
|
||||
gst_event_store_get_next_pending (GstEventStore * store)
|
||||
{
|
||||
GArray *events;
|
||||
guint i, len;
|
||||
|
||||
if (!store->events_pending)
|
||||
return NULL;
|
||||
|
||||
events = store->events;
|
||||
len = events->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
PadEvent *ev = &g_array_index (events, PadEvent, i);
|
||||
|
||||
if (ev->event == NULL || ev->delivered)
|
||||
continue;
|
||||
|
||||
/* Found an undelivered event, return it. The caller will mark it
|
||||
* as delivered once it has done so successfully by calling
|
||||
* gst_event_store_mark_delivered() */
|
||||
return gst_event_ref (ev->event);
|
||||
}
|
||||
|
||||
store->events_pending = FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_event_store_mark_delivered (GstEventStore * store, GstEvent * event)
|
||||
{
|
||||
gboolean events_pending = FALSE;
|
||||
GArray *events;
|
||||
guint i, len;
|
||||
|
||||
events = store->events;
|
||||
len = events->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
PadEvent *ev = &g_array_index (events, PadEvent, i);
|
||||
|
||||
if (ev->event == NULL)
|
||||
continue;
|
||||
|
||||
/* Check if there are any pending events other than
|
||||
* the passed one, so we can update the events_pending
|
||||
* flag at the end */
|
||||
if (ev->event != event && !ev->delivered) {
|
||||
events_pending = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
ev->delivered = TRUE;
|
||||
}
|
||||
|
||||
store->events_pending = events_pending;
|
||||
}
|
||||
|
||||
void
|
||||
gst_event_store_mark_all_undelivered (GstEventStore * store)
|
||||
{
|
||||
gboolean events_pending = FALSE;
|
||||
GArray *events;
|
||||
guint i, len;
|
||||
|
||||
events = store->events;
|
||||
len = events->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
PadEvent *ev = &g_array_index (events, PadEvent, i);
|
||||
|
||||
if (ev->event == NULL)
|
||||
continue;
|
||||
|
||||
ev->delivered = FALSE;
|
||||
events_pending = TRUE;
|
||||
}
|
||||
|
||||
/* Only set the flag if there was at least
|
||||
* one sticky event in the store */
|
||||
store->events_pending = events_pending;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
|
||||
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||
* Copyright (C) 2021-2022 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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_ADAPTIVE_DEMUX_UTILS_H_
|
||||
#define _GST_ADAPTIVE_DEMUX_UTILS_H_
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
typedef struct _GstAdaptiveDemuxClock GstAdaptiveDemuxClock;
|
||||
|
||||
typedef struct _GstAdaptiveDemuxLoop GstAdaptiveDemuxLoop;
|
||||
|
||||
GstAdaptiveDemuxClock *gst_adaptive_demux_clock_new (void);
|
||||
GstAdaptiveDemuxClock *gst_adaptive_demux_clock_ref (GstAdaptiveDemuxClock *
|
||||
clock);
|
||||
void gst_adaptive_demux_clock_unref (GstAdaptiveDemuxClock * clock);
|
||||
|
||||
GstClockTime gst_adaptive_demux_clock_get_time (GstAdaptiveDemuxClock * clock);
|
||||
GDateTime *gst_adaptive_demux_clock_get_now_utc (GstAdaptiveDemuxClock * clock);
|
||||
void gst_adaptive_demux_clock_set_utc_time (GstAdaptiveDemuxClock * clock, GDateTime *utc_now);
|
||||
|
||||
GstAdaptiveDemuxLoop *gst_adaptive_demux_loop_new (void);
|
||||
GstAdaptiveDemuxLoop *gst_adaptive_demux_loop_ref (GstAdaptiveDemuxLoop * loop);
|
||||
void gst_adaptive_demux_loop_unref (GstAdaptiveDemuxLoop * loop);
|
||||
|
||||
void gst_adaptive_demux_loop_start (GstAdaptiveDemuxLoop *loop);
|
||||
void gst_adaptive_demux_loop_stop (GstAdaptiveDemuxLoop * loop, gboolean wait);
|
||||
|
||||
guint gst_adaptive_demux_loop_call (GstAdaptiveDemuxLoop *loop, GSourceFunc func,
|
||||
gpointer data, GDestroyNotify notify);
|
||||
guint gst_adaptive_demux_loop_call_delayed (GstAdaptiveDemuxLoop *loop, GstClockTime delay,
|
||||
GSourceFunc func, gpointer data, GDestroyNotify notify);
|
||||
void gst_adaptive_demux_loop_cancel_call (GstAdaptiveDemuxLoop *loop, guint cb_id);
|
||||
|
||||
gboolean gst_adaptive_demux_loop_pause_and_lock (GstAdaptiveDemuxLoop * loop);
|
||||
gboolean gst_adaptive_demux_loop_unlock_and_unpause (GstAdaptiveDemuxLoop * loop);
|
||||
|
||||
GstDateTime *gst_adaptive_demux_util_parse_http_head_date (const gchar *http_date);
|
||||
|
||||
typedef struct _GstEventStore GstEventStore;
|
||||
|
||||
struct _GstEventStore {
|
||||
GArray *events;
|
||||
gboolean events_pending;
|
||||
};
|
||||
|
||||
void gst_event_store_init(GstEventStore *store);
|
||||
void gst_event_store_deinit(GstEventStore *store);
|
||||
|
||||
void gst_event_store_flush(GstEventStore *store);
|
||||
|
||||
void gst_event_store_insert_event (GstEventStore *store, GstEvent * event, gboolean delivered);
|
||||
GstEvent *gst_event_store_get_next_pending (GstEventStore *store);
|
||||
void gst_event_store_mark_delivered (GstEventStore *store, GstEvent *event);
|
||||
void gst_event_store_mark_all_undelivered (GstEventStore *store);
|
||||
#endif
|
979
subprojects/gst-plugins-good/ext/adaptivedemux2/gstisoff.c
Normal file
979
subprojects/gst-plugins-good/ext/adaptivedemux2/gstisoff.c
Normal file
|
@ -0,0 +1,979 @@
|
|||
/*
|
||||
* ISO File Format parsing library
|
||||
*
|
||||
* gstisoff.h
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics. All rights reserved.
|
||||
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstisoff.h"
|
||||
#include <gst/base/gstbytereader.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_isoff_debug);
|
||||
#define GST_CAT_DEFAULT gst_isoff_debug
|
||||
|
||||
static gboolean initialized = FALSE;
|
||||
|
||||
#define INITIALIZE_DEBUG_CATEGORY \
|
||||
if (!initialized) { \
|
||||
GST_DEBUG_CATEGORY_INIT (gst_isoff_debug, "isoff", 0, \
|
||||
"ISO File Format parsing library"); \
|
||||
initialized = TRUE; \
|
||||
}
|
||||
|
||||
static const guint8 tfrf_uuid[] = {
|
||||
0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
|
||||
0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
|
||||
};
|
||||
|
||||
static const guint8 tfxd_uuid[] = {
|
||||
0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
|
||||
0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
|
||||
};
|
||||
|
||||
/* gst_isoff_parse_box_header:
|
||||
* @reader:
|
||||
* @type: type that was found at the current position
|
||||
* @extended_type: (allow-none): extended type if type=='uuid'
|
||||
* @header_size: (allow-none): size of the box header (type, extended type and size)
|
||||
* @size: size of the complete box including type, extended type and size
|
||||
*
|
||||
* Advances the byte reader to the start of the box content. To skip
|
||||
* over the complete box, skip size - header_size bytes.
|
||||
*
|
||||
* Returns: TRUE if a box header could be parsed, FALSE if more data is needed
|
||||
*/
|
||||
gboolean
|
||||
gst_isoff_parse_box_header (GstByteReader * reader, guint32 * type,
|
||||
guint8 extended_type[16], guint * header_size, guint64 * size)
|
||||
{
|
||||
guint header_start_offset;
|
||||
guint32 size_field;
|
||||
|
||||
INITIALIZE_DEBUG_CATEGORY;
|
||||
header_start_offset = gst_byte_reader_get_pos (reader);
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 8)
|
||||
goto not_enough_data;
|
||||
|
||||
size_field = gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
*type = gst_byte_reader_get_uint32_le_unchecked (reader);
|
||||
|
||||
if (size_field == 1) {
|
||||
if (gst_byte_reader_get_remaining (reader) < 8)
|
||||
goto not_enough_data;
|
||||
*size = gst_byte_reader_get_uint64_be_unchecked (reader);
|
||||
} else {
|
||||
*size = size_field;
|
||||
}
|
||||
|
||||
if (*type == GST_ISOFF_FOURCC_UUID) {
|
||||
if (gst_byte_reader_get_remaining (reader) < 16)
|
||||
goto not_enough_data;
|
||||
|
||||
if (extended_type)
|
||||
memcpy (extended_type, gst_byte_reader_get_data_unchecked (reader, 16),
|
||||
16);
|
||||
}
|
||||
|
||||
if (header_size)
|
||||
*header_size = gst_byte_reader_get_pos (reader) - header_start_offset;
|
||||
|
||||
return TRUE;
|
||||
|
||||
not_enough_data:
|
||||
gst_byte_reader_set_pos (reader, header_start_offset);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_isoff_trun_box_clear (GstTrunBox * trun)
|
||||
{
|
||||
if (trun->samples)
|
||||
g_array_free (trun->samples, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_isoff_tfrf_box_free (GstTfrfBox * tfrf)
|
||||
{
|
||||
if (tfrf->entries)
|
||||
g_array_free (tfrf->entries, TRUE);
|
||||
|
||||
g_free (tfrf);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_isoff_traf_box_clear (GstTrafBox * traf)
|
||||
{
|
||||
if (traf->trun)
|
||||
g_array_free (traf->trun, TRUE);
|
||||
|
||||
if (traf->tfrf)
|
||||
gst_isoff_tfrf_box_free (traf->tfrf);
|
||||
|
||||
g_free (traf->tfxd);
|
||||
traf->trun = NULL;
|
||||
traf->tfrf = NULL;
|
||||
traf->tfxd = NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_mfhd_box_parse (GstMfhdBox * mfhd, GstByteReader * reader)
|
||||
{
|
||||
guint8 version;
|
||||
guint32 flags;
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) != 8)
|
||||
return FALSE;
|
||||
|
||||
version = gst_byte_reader_get_uint8_unchecked (reader);
|
||||
if (version != 0)
|
||||
return FALSE;
|
||||
|
||||
flags = gst_byte_reader_get_uint24_be_unchecked (reader);
|
||||
if (flags != 0)
|
||||
return FALSE;
|
||||
|
||||
mfhd->sequence_number = gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_tfhd_box_parse (GstTfhdBox * tfhd, GstByteReader * reader)
|
||||
{
|
||||
memset (tfhd, 0, sizeof (*tfhd));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
tfhd->version = gst_byte_reader_get_uint8_unchecked (reader);
|
||||
if (tfhd->version != 0)
|
||||
return FALSE;
|
||||
|
||||
tfhd->flags = gst_byte_reader_get_uint24_be_unchecked (reader);
|
||||
|
||||
if (!gst_byte_reader_get_uint32_be (reader, &tfhd->track_id))
|
||||
return FALSE;
|
||||
|
||||
if ((tfhd->flags & GST_TFHD_FLAGS_BASE_DATA_OFFSET_PRESENT) &&
|
||||
!gst_byte_reader_get_uint64_be (reader, &tfhd->base_data_offset))
|
||||
return FALSE;
|
||||
|
||||
if ((tfhd->flags & GST_TFHD_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &tfhd->sample_description_index))
|
||||
return FALSE;
|
||||
|
||||
if ((tfhd->flags & GST_TFHD_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &tfhd->default_sample_duration))
|
||||
return FALSE;
|
||||
|
||||
if ((tfhd->flags & GST_TFHD_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &tfhd->default_sample_size))
|
||||
return FALSE;
|
||||
|
||||
if ((tfhd->flags & GST_TFHD_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &tfhd->default_sample_flags))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_trun_box_parse (GstTrunBox * trun, GstByteReader * reader)
|
||||
{
|
||||
gint i;
|
||||
|
||||
memset (trun, 0, sizeof (*trun));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
trun->version = gst_byte_reader_get_uint8_unchecked (reader);
|
||||
if (trun->version != 0 && trun->version != 1)
|
||||
return FALSE;
|
||||
|
||||
trun->flags = gst_byte_reader_get_uint24_be_unchecked (reader);
|
||||
|
||||
if (!gst_byte_reader_get_uint32_be (reader, &trun->sample_count))
|
||||
return FALSE;
|
||||
|
||||
trun->samples =
|
||||
g_array_sized_new (FALSE, FALSE, sizeof (GstTrunSample),
|
||||
trun->sample_count);
|
||||
|
||||
if ((trun->flags & GST_TRUN_FLAGS_DATA_OFFSET_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, (guint32 *) & trun->data_offset))
|
||||
return FALSE;
|
||||
|
||||
if ((trun->flags & GST_TRUN_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &trun->first_sample_flags))
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < trun->sample_count; i++) {
|
||||
GstTrunSample sample = { 0, };
|
||||
|
||||
if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_DURATION_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &sample.sample_duration))
|
||||
goto error;
|
||||
|
||||
if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_SIZE_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &sample.sample_size))
|
||||
goto error;
|
||||
|
||||
if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_FLAGS_PRESENT) &&
|
||||
!gst_byte_reader_get_uint32_be (reader, &sample.sample_flags))
|
||||
goto error;
|
||||
|
||||
if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSETS_PRESENT)
|
||||
&& !gst_byte_reader_get_uint32_be (reader,
|
||||
&sample.sample_composition_time_offset.u))
|
||||
goto error;
|
||||
|
||||
g_array_append_val (trun->samples, sample);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
gst_isoff_trun_box_clear (trun);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_tfdt_box_parse (GstTfdtBox * tfdt, GstByteReader * reader)
|
||||
{
|
||||
gint8 version;
|
||||
|
||||
memset (tfdt, 0, sizeof (*tfdt));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
version = gst_byte_reader_get_uint8_unchecked (reader);
|
||||
|
||||
if (!gst_byte_reader_skip (reader, 3))
|
||||
return FALSE;
|
||||
|
||||
if (version == 1) {
|
||||
if (!gst_byte_reader_get_uint64_be (reader, &tfdt->decode_time))
|
||||
return FALSE;
|
||||
} else {
|
||||
guint32 dec_time = 0;
|
||||
if (!gst_byte_reader_get_uint32_be (reader, &dec_time))
|
||||
return FALSE;
|
||||
tfdt->decode_time = dec_time;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_tfxd_box_parse (GstTfxdBox * tfxd, GstByteReader * reader)
|
||||
{
|
||||
guint8 version;
|
||||
guint32 flags = 0;
|
||||
guint64 absolute_time = 0;
|
||||
guint64 absolute_duration = 0;
|
||||
|
||||
memset (tfxd, 0, sizeof (*tfxd));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (reader, &version)) {
|
||||
GST_ERROR ("Error getting box's version field");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
|
||||
GST_ERROR ("Error getting box's flags field");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tfxd->version = version;
|
||||
tfxd->flags = flags;
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < ((version & 0x01) ? 16 : 8))
|
||||
return FALSE;
|
||||
|
||||
if (version & 0x01) {
|
||||
gst_byte_reader_get_uint64_be (reader, &absolute_time);
|
||||
gst_byte_reader_get_uint64_be (reader, &absolute_duration);
|
||||
} else {
|
||||
guint32 time = 0;
|
||||
guint32 duration = 0;
|
||||
gst_byte_reader_get_uint32_be (reader, &time);
|
||||
gst_byte_reader_get_uint32_be (reader, &duration);
|
||||
absolute_time = time;
|
||||
absolute_duration = duration;
|
||||
}
|
||||
|
||||
tfxd->time = absolute_time;
|
||||
tfxd->duration = absolute_duration;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_tfrf_box_parse (GstTfrfBox * tfrf, GstByteReader * reader)
|
||||
{
|
||||
guint8 version;
|
||||
guint32 flags = 0;
|
||||
guint8 fragment_count = 0;
|
||||
guint8 index = 0;
|
||||
|
||||
memset (tfrf, 0, sizeof (*tfrf));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (reader, &version)) {
|
||||
GST_ERROR ("Error getting box's version field");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
|
||||
GST_ERROR ("Error getting box's flags field");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tfrf->version = version;
|
||||
tfrf->flags = flags;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (reader, &fragment_count))
|
||||
return FALSE;
|
||||
|
||||
tfrf->entries_count = fragment_count;
|
||||
tfrf->entries =
|
||||
g_array_sized_new (FALSE, FALSE, sizeof (GstTfrfBoxEntry),
|
||||
tfrf->entries_count);
|
||||
|
||||
for (index = 0; index < fragment_count; index++) {
|
||||
GstTfrfBoxEntry entry = { 0, };
|
||||
guint64 absolute_time = 0;
|
||||
guint64 absolute_duration = 0;
|
||||
if (gst_byte_reader_get_remaining (reader) < ((version & 0x01) ? 16 : 8))
|
||||
return FALSE;
|
||||
|
||||
if (version & 0x01) {
|
||||
if (!gst_byte_reader_get_uint64_be (reader, &absolute_time) ||
|
||||
!gst_byte_reader_get_uint64_be (reader, &absolute_duration)) {
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
guint32 time = 0;
|
||||
guint32 duration = 0;
|
||||
if (!gst_byte_reader_get_uint32_be (reader, &time) ||
|
||||
!gst_byte_reader_get_uint32_be (reader, &duration)) {
|
||||
return FALSE;
|
||||
}
|
||||
absolute_time = time;
|
||||
absolute_duration = duration;
|
||||
}
|
||||
entry.time = absolute_time;
|
||||
entry.duration = absolute_duration;
|
||||
|
||||
g_array_append_val (tfrf->entries, entry);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_traf_box_parse (GstTrafBox * traf, GstByteReader * reader)
|
||||
{
|
||||
gboolean had_tfhd = FALSE;
|
||||
|
||||
memset (traf, 0, sizeof (*traf));
|
||||
traf->trun = g_array_new (FALSE, FALSE, sizeof (GstTrunBox));
|
||||
g_array_set_clear_func (traf->trun,
|
||||
(GDestroyNotify) gst_isoff_trun_box_clear);
|
||||
|
||||
traf->tfdt.decode_time = GST_CLOCK_TIME_NONE;
|
||||
|
||||
while (gst_byte_reader_get_remaining (reader) > 0) {
|
||||
guint32 fourcc;
|
||||
guint header_size;
|
||||
guint64 size;
|
||||
GstByteReader sub_reader;
|
||||
guint8 extended_type[16] = { 0, };
|
||||
|
||||
if (!gst_isoff_parse_box_header (reader, &fourcc, extended_type,
|
||||
&header_size, &size))
|
||||
goto error;
|
||||
if (gst_byte_reader_get_remaining (reader) < size - header_size)
|
||||
goto error;
|
||||
|
||||
switch (fourcc) {
|
||||
case GST_ISOFF_FOURCC_TFHD:{
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_tfhd_box_parse (&traf->tfhd, &sub_reader))
|
||||
goto error;
|
||||
had_tfhd = TRUE;
|
||||
break;
|
||||
}
|
||||
case GST_ISOFF_FOURCC_TFDT:{
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_tfdt_box_parse (&traf->tfdt, &sub_reader))
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
case GST_ISOFF_FOURCC_TRUN:{
|
||||
GstTrunBox trun;
|
||||
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_trun_box_parse (&trun, &sub_reader))
|
||||
goto error;
|
||||
|
||||
g_array_append_val (traf->trun, trun);
|
||||
break;
|
||||
}
|
||||
case GST_ISOFF_FOURCC_UUID:{
|
||||
/* smooth-streaming specific */
|
||||
if (memcmp (extended_type, tfrf_uuid, 16) == 0) {
|
||||
if (traf->tfrf)
|
||||
gst_isoff_tfrf_box_free (traf->tfrf);
|
||||
traf->tfrf = g_new0 (GstTfrfBox, 1);
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
|
||||
if (!gst_isoff_tfrf_box_parse (traf->tfrf, &sub_reader))
|
||||
goto error;
|
||||
} else if (memcmp (extended_type, tfxd_uuid, 16) == 0) {
|
||||
if (traf->tfxd)
|
||||
g_free (traf->tfxd);
|
||||
traf->tfxd = g_new0 (GstTfxdBox, 1);
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
|
||||
if (!gst_isoff_tfxd_box_parse (traf->tfxd, &sub_reader))
|
||||
goto error;
|
||||
} else {
|
||||
gst_byte_reader_skip (reader, size - header_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gst_byte_reader_skip (reader, size - header_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!had_tfhd)
|
||||
goto error;
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
gst_isoff_traf_box_clear (traf);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GstMoofBox *
|
||||
gst_isoff_moof_box_parse (GstByteReader * reader)
|
||||
{
|
||||
GstMoofBox *moof;
|
||||
gboolean had_mfhd = FALSE;
|
||||
GstByteReader sub_reader;
|
||||
|
||||
|
||||
INITIALIZE_DEBUG_CATEGORY;
|
||||
moof = g_new0 (GstMoofBox, 1);
|
||||
moof->traf = g_array_new (FALSE, FALSE, sizeof (GstTrafBox));
|
||||
g_array_set_clear_func (moof->traf,
|
||||
(GDestroyNotify) gst_isoff_traf_box_clear);
|
||||
|
||||
while (gst_byte_reader_get_remaining (reader) > 0) {
|
||||
guint32 fourcc;
|
||||
guint header_size;
|
||||
guint64 size;
|
||||
|
||||
if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
|
||||
&size))
|
||||
goto error;
|
||||
if (gst_byte_reader_get_remaining (reader) < size - header_size)
|
||||
goto error;
|
||||
|
||||
switch (fourcc) {
|
||||
case GST_ISOFF_FOURCC_MFHD:{
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_mfhd_box_parse (&moof->mfhd, &sub_reader))
|
||||
goto error;
|
||||
had_mfhd = TRUE;
|
||||
break;
|
||||
}
|
||||
case GST_ISOFF_FOURCC_TRAF:{
|
||||
GstTrafBox traf;
|
||||
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_traf_box_parse (&traf, &sub_reader))
|
||||
goto error;
|
||||
|
||||
g_array_append_val (moof->traf, traf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gst_byte_reader_skip (reader, size - header_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!had_mfhd)
|
||||
goto error;
|
||||
|
||||
return moof;
|
||||
|
||||
error:
|
||||
gst_isoff_moof_box_free (moof);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_isoff_moof_box_free (GstMoofBox * moof)
|
||||
{
|
||||
g_array_free (moof->traf, TRUE);
|
||||
g_free (moof);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_mdhd_box_parse (GstMdhdBox * mdhd, GstByteReader * reader)
|
||||
{
|
||||
guint8 version;
|
||||
|
||||
memset (mdhd, 0, sizeof (*mdhd));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
version = gst_byte_reader_get_uint8_unchecked (reader);
|
||||
|
||||
if (!gst_byte_reader_skip (reader, 3))
|
||||
return FALSE;
|
||||
|
||||
/* skip {creation, modification}_time, we don't have interest */
|
||||
if (version == 1) {
|
||||
if (!gst_byte_reader_skip (reader, 16))
|
||||
return FALSE;
|
||||
} else {
|
||||
if (!gst_byte_reader_skip (reader, 8))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_byte_reader_get_uint32_be (reader, &mdhd->timescale))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_hdlr_box_parse (GstHdlrBox * hdlr, GstByteReader * reader)
|
||||
{
|
||||
memset (hdlr, 0, sizeof (*hdlr));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
/* version & flag */
|
||||
if (!gst_byte_reader_skip (reader, 4))
|
||||
return FALSE;
|
||||
|
||||
/* pre_defined = 0 */
|
||||
if (!gst_byte_reader_skip (reader, 4))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_byte_reader_get_uint32_le (reader, &hdlr->handler_type))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_mdia_box_parse (GstMdiaBox * mdia, GstByteReader * reader)
|
||||
{
|
||||
gboolean had_mdhd = FALSE, had_hdlr = FALSE;
|
||||
while (gst_byte_reader_get_remaining (reader) > 0) {
|
||||
guint32 fourcc;
|
||||
guint header_size;
|
||||
guint64 size;
|
||||
GstByteReader sub_reader;
|
||||
|
||||
if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
|
||||
&size))
|
||||
return FALSE;
|
||||
if (gst_byte_reader_get_remaining (reader) < size - header_size)
|
||||
return FALSE;
|
||||
|
||||
switch (fourcc) {
|
||||
case GST_ISOFF_FOURCC_MDHD:{
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_mdhd_box_parse (&mdia->mdhd, &sub_reader))
|
||||
return FALSE;
|
||||
|
||||
had_mdhd = TRUE;
|
||||
break;
|
||||
}
|
||||
case GST_ISOFF_FOURCC_HDLR:{
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_hdlr_box_parse (&mdia->hdlr, &sub_reader))
|
||||
return FALSE;
|
||||
|
||||
had_hdlr = TRUE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gst_byte_reader_skip (reader, size - header_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!had_mdhd || !had_hdlr)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_tkhd_box_parse (GstTkhdBox * tkhd, GstByteReader * reader)
|
||||
{
|
||||
guint8 version;
|
||||
|
||||
memset (tkhd, 0, sizeof (*tkhd));
|
||||
|
||||
if (gst_byte_reader_get_remaining (reader) < 4)
|
||||
return FALSE;
|
||||
|
||||
if (!gst_byte_reader_get_uint8 (reader, &version))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_byte_reader_skip (reader, 3))
|
||||
return FALSE;
|
||||
|
||||
/* skip {creation, modification}_time, we don't have interest */
|
||||
if (version == 1) {
|
||||
if (!gst_byte_reader_skip (reader, 16))
|
||||
return FALSE;
|
||||
} else {
|
||||
if (!gst_byte_reader_skip (reader, 8))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_byte_reader_get_uint32_be (reader, &tkhd->track_id))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_isoff_trak_box_parse (GstTrakBox * trak, GstByteReader * reader)
|
||||
{
|
||||
gboolean had_mdia = FALSE, had_tkhd = FALSE;
|
||||
while (gst_byte_reader_get_remaining (reader) > 0) {
|
||||
guint32 fourcc;
|
||||
guint header_size;
|
||||
guint64 size;
|
||||
GstByteReader sub_reader;
|
||||
|
||||
if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
|
||||
&size))
|
||||
return FALSE;
|
||||
if (gst_byte_reader_get_remaining (reader) < size - header_size)
|
||||
return FALSE;
|
||||
|
||||
switch (fourcc) {
|
||||
case GST_ISOFF_FOURCC_MDIA:{
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_mdia_box_parse (&trak->mdia, &sub_reader))
|
||||
return FALSE;
|
||||
|
||||
had_mdia = TRUE;
|
||||
break;
|
||||
}
|
||||
case GST_ISOFF_FOURCC_TKHD:{
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_tkhd_box_parse (&trak->tkhd, &sub_reader))
|
||||
return FALSE;
|
||||
|
||||
had_tkhd = TRUE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gst_byte_reader_skip (reader, size - header_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!had_tkhd || !had_mdia)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GstMoovBox *
|
||||
gst_isoff_moov_box_parse (GstByteReader * reader)
|
||||
{
|
||||
GstMoovBox *moov;
|
||||
gboolean had_trak = FALSE;
|
||||
moov = g_new0 (GstMoovBox, 1);
|
||||
moov->trak = g_array_new (FALSE, FALSE, sizeof (GstTrakBox));
|
||||
|
||||
while (gst_byte_reader_get_remaining (reader) > 0) {
|
||||
guint32 fourcc;
|
||||
guint header_size;
|
||||
guint64 size;
|
||||
|
||||
if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
|
||||
&size))
|
||||
goto error;
|
||||
if (gst_byte_reader_get_remaining (reader) < size - header_size)
|
||||
goto error;
|
||||
|
||||
switch (fourcc) {
|
||||
case GST_ISOFF_FOURCC_TRAK:{
|
||||
GstByteReader sub_reader;
|
||||
GstTrakBox trak;
|
||||
|
||||
gst_byte_reader_get_sub_reader (reader, &sub_reader,
|
||||
size - header_size);
|
||||
if (!gst_isoff_trak_box_parse (&trak, &sub_reader))
|
||||
goto error;
|
||||
|
||||
had_trak = TRUE;
|
||||
g_array_append_val (moov->trak, trak);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gst_byte_reader_skip (reader, size - header_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!had_trak)
|
||||
goto error;
|
||||
|
||||
return moov;
|
||||
|
||||
error:
|
||||
gst_isoff_moov_box_free (moov);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gst_isoff_moov_box_free (GstMoovBox * moov)
|
||||
{
|
||||
g_array_free (moov->trak, TRUE);
|
||||
g_free (moov);
|
||||
}
|
||||
|
||||
void
|
||||
gst_isoff_sidx_parser_init (GstSidxParser * parser)
|
||||
{
|
||||
parser->status = GST_ISOFF_SIDX_PARSER_INIT;
|
||||
parser->cumulative_entry_size = 0;
|
||||
parser->sidx.entries = NULL;
|
||||
parser->sidx.entries_count = 0;
|
||||
}
|
||||
|
||||
void
|
||||
gst_isoff_sidx_parser_clear (GstSidxParser * parser)
|
||||
{
|
||||
g_free (parser->sidx.entries);
|
||||
memset (parser, 0, sizeof (*parser));
|
||||
|
||||
gst_isoff_sidx_parser_init (parser);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_isoff_parse_sidx_entry (GstSidxBoxEntry * entry, GstByteReader * reader)
|
||||
{
|
||||
guint32 aux;
|
||||
|
||||
aux = gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
entry->ref_type = aux >> 31;
|
||||
entry->size = aux & 0x7FFFFFFF;
|
||||
entry->duration = gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
aux = gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
entry->starts_with_sap = aux >> 31;
|
||||
entry->sap_type = ((aux >> 28) & 0x7);
|
||||
entry->sap_delta_time = aux & 0xFFFFFFF;
|
||||
}
|
||||
|
||||
GstIsoffParserResult
|
||||
gst_isoff_sidx_parser_parse (GstSidxParser * parser,
|
||||
GstByteReader * reader, guint * consumed)
|
||||
{
|
||||
GstIsoffParserResult res = GST_ISOFF_PARSER_OK;
|
||||
gsize remaining;
|
||||
|
||||
INITIALIZE_DEBUG_CATEGORY;
|
||||
switch (parser->status) {
|
||||
case GST_ISOFF_SIDX_PARSER_INIT:
|
||||
/* Try again once we have enough data for the FullBox header */
|
||||
if (gst_byte_reader_get_remaining (reader) < 4) {
|
||||
gst_byte_reader_set_pos (reader, 0);
|
||||
break;
|
||||
}
|
||||
parser->sidx.version = gst_byte_reader_get_uint8_unchecked (reader);
|
||||
parser->sidx.flags = gst_byte_reader_get_uint24_le_unchecked (reader);
|
||||
|
||||
parser->status = GST_ISOFF_SIDX_PARSER_HEADER;
|
||||
|
||||
case GST_ISOFF_SIDX_PARSER_HEADER:
|
||||
remaining = gst_byte_reader_get_remaining (reader);
|
||||
if (remaining < 12 + (parser->sidx.version == 0 ? 8 : 16)) {
|
||||
break;
|
||||
}
|
||||
|
||||
parser->sidx.ref_id = gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
parser->sidx.timescale = gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
if (parser->sidx.version == 0) {
|
||||
parser->sidx.earliest_pts =
|
||||
gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
parser->sidx.first_offset =
|
||||
gst_byte_reader_get_uint32_be_unchecked (reader);
|
||||
} else {
|
||||
parser->sidx.earliest_pts =
|
||||
gst_byte_reader_get_uint64_be_unchecked (reader);
|
||||
parser->sidx.first_offset =
|
||||
gst_byte_reader_get_uint64_be_unchecked (reader);
|
||||
}
|
||||
/* skip 2 reserved bytes */
|
||||
gst_byte_reader_skip_unchecked (reader, 2);
|
||||
parser->sidx.entries_count =
|
||||
gst_byte_reader_get_uint16_be_unchecked (reader);
|
||||
|
||||
GST_LOG ("Timescale: %" G_GUINT32_FORMAT, parser->sidx.timescale);
|
||||
GST_LOG ("Earliest pts: %" G_GUINT64_FORMAT, parser->sidx.earliest_pts);
|
||||
GST_LOG ("First offset: %" G_GUINT64_FORMAT, parser->sidx.first_offset);
|
||||
|
||||
parser->cumulative_pts =
|
||||
gst_util_uint64_scale_int_round (parser->sidx.earliest_pts,
|
||||
GST_SECOND, parser->sidx.timescale);
|
||||
|
||||
if (parser->sidx.entries_count) {
|
||||
parser->sidx.entries =
|
||||
g_malloc (sizeof (GstSidxBoxEntry) * parser->sidx.entries_count);
|
||||
}
|
||||
parser->sidx.entry_index = 0;
|
||||
|
||||
parser->status = GST_ISOFF_SIDX_PARSER_DATA;
|
||||
|
||||
case GST_ISOFF_SIDX_PARSER_DATA:
|
||||
while (parser->sidx.entry_index < parser->sidx.entries_count) {
|
||||
GstSidxBoxEntry *entry =
|
||||
&parser->sidx.entries[parser->sidx.entry_index];
|
||||
|
||||
remaining = gst_byte_reader_get_remaining (reader);
|
||||
if (remaining < 12)
|
||||
break;
|
||||
|
||||
entry->offset = parser->cumulative_entry_size;
|
||||
entry->pts = parser->cumulative_pts;
|
||||
gst_isoff_parse_sidx_entry (entry, reader);
|
||||
entry->duration = gst_util_uint64_scale_int_round (entry->duration,
|
||||
GST_SECOND, parser->sidx.timescale);
|
||||
parser->cumulative_entry_size += entry->size;
|
||||
parser->cumulative_pts += entry->duration;
|
||||
|
||||
GST_LOG ("Sidx entry %d) offset: %" G_GUINT64_FORMAT ", pts: %"
|
||||
GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT " - size %"
|
||||
G_GUINT32_FORMAT, parser->sidx.entry_index, entry->offset,
|
||||
GST_TIME_ARGS (entry->pts), GST_TIME_ARGS (entry->duration),
|
||||
entry->size);
|
||||
|
||||
parser->sidx.entry_index++;
|
||||
}
|
||||
|
||||
if (parser->sidx.entry_index == parser->sidx.entries_count)
|
||||
parser->status = GST_ISOFF_SIDX_PARSER_FINISHED;
|
||||
else
|
||||
break;
|
||||
case GST_ISOFF_SIDX_PARSER_FINISHED:
|
||||
parser->sidx.entry_index = 0;
|
||||
res = GST_ISOFF_PARSER_DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
*consumed = gst_byte_reader_get_pos (reader);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GstIsoffParserResult
|
||||
gst_isoff_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buffer,
|
||||
guint * consumed)
|
||||
{
|
||||
GstIsoffParserResult res = GST_ISOFF_PARSER_OK;
|
||||
GstByteReader reader;
|
||||
GstMapInfo info;
|
||||
guint32 fourcc;
|
||||
|
||||
INITIALIZE_DEBUG_CATEGORY;
|
||||
if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
|
||||
*consumed = 0;
|
||||
return GST_ISOFF_PARSER_ERROR;
|
||||
}
|
||||
|
||||
gst_byte_reader_init (&reader, info.data, info.size);
|
||||
|
||||
if (parser->status == GST_ISOFF_SIDX_PARSER_INIT) {
|
||||
if (!gst_isoff_parse_box_header (&reader, &fourcc, NULL, NULL,
|
||||
&parser->size))
|
||||
goto done;
|
||||
|
||||
if (fourcc != GST_ISOFF_FOURCC_SIDX) {
|
||||
res = GST_ISOFF_PARSER_UNEXPECTED;
|
||||
gst_byte_reader_set_pos (&reader, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (parser->size == 0) {
|
||||
res = GST_ISOFF_PARSER_ERROR;
|
||||
gst_byte_reader_set_pos (&reader, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Try again once we have enough data for the FullBox header */
|
||||
if (gst_byte_reader_get_remaining (&reader) < 4) {
|
||||
gst_byte_reader_set_pos (&reader, 0);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
res = gst_isoff_sidx_parser_parse (parser, &reader, consumed);
|
||||
|
||||
done:
|
||||
gst_buffer_unmap (buffer, &info);
|
||||
return res;
|
||||
}
|
299
subprojects/gst-plugins-good/ext/adaptivedemux2/gstisoff.h
Normal file
299
subprojects/gst-plugins-good/ext/adaptivedemux2/gstisoff.h
Normal file
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* ISO File Format parsing library
|
||||
*
|
||||
* gstisoff.h
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics. All rights reserved.
|
||||
* Author: Thiago Santos <thiagoss@osg.samsung.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ISOFF_H__
|
||||
#define __GST_ISOFF_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/base.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#ifndef GST_ISOFF_API
|
||||
# ifdef BUILDING_GST_ISOFF
|
||||
# define GST_ISOFF_API GST_API_EXPORT /* from config.h */
|
||||
# else
|
||||
# define GST_ISOFF_API GST_API_IMPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
GST_ISOFF_PARSER_OK,
|
||||
GST_ISOFF_PARSER_DONE,
|
||||
GST_ISOFF_PARSER_UNEXPECTED,
|
||||
GST_ISOFF_PARSER_ERROR
|
||||
} GstIsoffParserResult;
|
||||
|
||||
GST_ISOFF_API
|
||||
gboolean gst_isoff_parse_box_header (GstByteReader * reader, guint32 * type, guint8 extended_type[16], guint * header_size, guint64 * size);
|
||||
|
||||
#define GST_ISOFF_FOURCC_UUID GST_MAKE_FOURCC('u','u','i','d')
|
||||
#define GST_ISOFF_FOURCC_MOOF GST_MAKE_FOURCC('m','o','o','f')
|
||||
#define GST_ISOFF_FOURCC_MFHD GST_MAKE_FOURCC('m','f','h','d')
|
||||
#define GST_ISOFF_FOURCC_TFHD GST_MAKE_FOURCC('t','f','h','d')
|
||||
#define GST_ISOFF_FOURCC_TRUN GST_MAKE_FOURCC('t','r','u','n')
|
||||
#define GST_ISOFF_FOURCC_TRAF GST_MAKE_FOURCC('t','r','a','f')
|
||||
#define GST_ISOFF_FOURCC_TFDT GST_MAKE_FOURCC('t','f','d','t')
|
||||
#define GST_ISOFF_FOURCC_MDAT GST_MAKE_FOURCC('m','d','a','t')
|
||||
#define GST_ISOFF_FOURCC_MOOV GST_MAKE_FOURCC('m','o','o','v')
|
||||
#define GST_ISOFF_FOURCC_TRAK GST_MAKE_FOURCC('t','r','a','k')
|
||||
#define GST_ISOFF_FOURCC_TKHD GST_MAKE_FOURCC('t','k','h','d')
|
||||
#define GST_ISOFF_FOURCC_MDIA GST_MAKE_FOURCC('m','d','i','a')
|
||||
#define GST_ISOFF_FOURCC_MDHD GST_MAKE_FOURCC('m','d','h','d')
|
||||
#define GST_ISOFF_FOURCC_HDLR GST_MAKE_FOURCC('h','d','l','r')
|
||||
#define GST_ISOFF_FOURCC_SIDX GST_MAKE_FOURCC('s','i','d','x')
|
||||
|
||||
/* handler type */
|
||||
#define GST_ISOFF_FOURCC_SOUN GST_MAKE_FOURCC('s','o','u','n')
|
||||
#define GST_ISOFF_FOURCC_VIDE GST_MAKE_FOURCC('v','i','d','e')
|
||||
|
||||
#define GST_ISOFF_SAMPLE_FLAGS_IS_LEADING(flags) (((flags) >> 26) & 0x03)
|
||||
#define GST_ISOFF_SAMPLE_FLAGS_SAMPLE_DEPENDS_ON(flags) (((flags) >> 24) & 0x03)
|
||||
#define GST_ISOFF_SAMPLE_FLAGS_SAMPLE_IS_DEPENDED_ON(flags) (((flags) >> 22) & 0x03)
|
||||
#define GST_ISOFF_SAMPLE_FLAGS_SAMPLE_HAS_REDUNDANCY(flags) (((flags) >> 20) & 0x03)
|
||||
#define GST_ISOFF_SAMPLE_FLAGS_SAMPLE_PADDING_VALUE(flags) (((flags) >> 17) & 0x07)
|
||||
#define GST_ISOFF_SAMPLE_FLAGS_SAMPLE_IS_NON_SYNC_SAMPLE(flags) (((flags) >> 16) & 0x01)
|
||||
#define GST_ISOFF_SAMPLE_FLAGS_SAMPLE_DEGRADATION_PRIORITY(flags) (((flags) >> 0) & 0x0f)
|
||||
|
||||
/* Smooth-Streaming specific boxes */
|
||||
typedef struct _GstTfxdBox
|
||||
{
|
||||
guint8 version;
|
||||
guint32 flags;
|
||||
|
||||
guint64 time;
|
||||
guint64 duration;
|
||||
} GstTfxdBox;
|
||||
|
||||
typedef struct _GstTfrfBoxEntry
|
||||
{
|
||||
guint64 time;
|
||||
guint64 duration;
|
||||
} GstTfrfBoxEntry;
|
||||
|
||||
typedef struct _GstTfrfBox
|
||||
{
|
||||
guint8 version;
|
||||
guint32 flags;
|
||||
|
||||
gint entries_count;
|
||||
GArray *entries;
|
||||
} GstTfrfBox;
|
||||
|
||||
/* Common boxes */
|
||||
typedef struct _GstMfhdBox
|
||||
{
|
||||
guint32 sequence_number;
|
||||
} GstMfhdBox;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_TFHD_FLAGS_BASE_DATA_OFFSET_PRESENT = 0x000001,
|
||||
GST_TFHD_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT = 0x000002,
|
||||
GST_TFHD_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT = 0x000008,
|
||||
GST_TFHD_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT = 0x000010,
|
||||
GST_TFHD_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT = 0x000020,
|
||||
GST_TFHD_FLAGS_DURATION_IS_EMPTY = 0x010000,
|
||||
GST_TFHD_FLAGS_DEFAULT_BASE_IS_MOOF = 0x020000
|
||||
} GstTfhdFlags;
|
||||
|
||||
typedef struct _GstTfhdBox
|
||||
{
|
||||
guint8 version;
|
||||
GstTfhdFlags flags;
|
||||
|
||||
guint32 track_id;
|
||||
|
||||
/* optional */
|
||||
guint64 base_data_offset;
|
||||
guint32 sample_description_index;
|
||||
guint32 default_sample_duration;
|
||||
guint32 default_sample_size;
|
||||
guint32 default_sample_flags;
|
||||
} GstTfhdBox;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_TRUN_FLAGS_DATA_OFFSET_PRESENT = 0x000001,
|
||||
GST_TRUN_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT = 0x000004,
|
||||
GST_TRUN_FLAGS_SAMPLE_DURATION_PRESENT = 0x000100,
|
||||
GST_TRUN_FLAGS_SAMPLE_SIZE_PRESENT = 0x000200,
|
||||
GST_TRUN_FLAGS_SAMPLE_FLAGS_PRESENT = 0x000400,
|
||||
GST_TRUN_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSETS_PRESENT = 0x000800
|
||||
} GstTrunFlags;
|
||||
|
||||
typedef struct _GstTrunBox
|
||||
{
|
||||
guint8 version;
|
||||
GstTrunFlags flags;
|
||||
|
||||
guint32 sample_count;
|
||||
|
||||
/* optional */
|
||||
gint32 data_offset;
|
||||
guint32 first_sample_flags;
|
||||
GArray *samples;
|
||||
} GstTrunBox;
|
||||
|
||||
typedef struct _GstTrunSample
|
||||
{
|
||||
guint32 sample_duration;
|
||||
guint32 sample_size;
|
||||
guint32 sample_flags;
|
||||
|
||||
union {
|
||||
guint32 u; /* version 0 */
|
||||
gint32 s; /* others */
|
||||
} sample_composition_time_offset;
|
||||
} GstTrunSample;
|
||||
|
||||
typedef struct _GstTdftBox
|
||||
{
|
||||
guint64 decode_time;
|
||||
} GstTfdtBox;
|
||||
|
||||
typedef struct _GstTrafBox
|
||||
{
|
||||
GstTfhdBox tfhd;
|
||||
GstTfdtBox tfdt;
|
||||
GArray *trun;
|
||||
|
||||
/* smooth-streaming specific */
|
||||
GstTfrfBox *tfrf;
|
||||
GstTfxdBox *tfxd;
|
||||
} GstTrafBox;
|
||||
|
||||
typedef struct _GstMoofBox
|
||||
{
|
||||
GstMfhdBox mfhd;
|
||||
GArray *traf;
|
||||
} GstMoofBox;
|
||||
|
||||
GST_ISOFF_API
|
||||
GstMoofBox * gst_isoff_moof_box_parse (GstByteReader *reader);
|
||||
|
||||
GST_ISOFF_API
|
||||
void gst_isoff_moof_box_free (GstMoofBox *moof);
|
||||
|
||||
typedef struct _GstTkhdBox
|
||||
{
|
||||
guint32 track_id;
|
||||
} GstTkhdBox;
|
||||
|
||||
typedef struct _GstMdhdBox
|
||||
{
|
||||
guint32 timescale;
|
||||
} GstMdhdBox;
|
||||
|
||||
typedef struct _GstHdlrBox
|
||||
{
|
||||
guint32 handler_type;
|
||||
} GstHdlrBox;
|
||||
|
||||
typedef struct _GstMdiaBox
|
||||
{
|
||||
GstMdhdBox mdhd;
|
||||
GstHdlrBox hdlr;
|
||||
} GstMdiaBox;
|
||||
|
||||
typedef struct _GstTrakBox
|
||||
{
|
||||
GstTkhdBox tkhd;
|
||||
GstMdiaBox mdia;
|
||||
} GstTrakBox;
|
||||
|
||||
typedef struct _GstMoovBox
|
||||
{
|
||||
GArray *trak;
|
||||
} GstMoovBox;
|
||||
|
||||
GST_ISOFF_API
|
||||
GstMoovBox * gst_isoff_moov_box_parse (GstByteReader *reader);
|
||||
|
||||
GST_ISOFF_API
|
||||
void gst_isoff_moov_box_free (GstMoovBox *moov);
|
||||
|
||||
typedef struct _GstSidxBoxEntry
|
||||
{
|
||||
gboolean ref_type;
|
||||
guint32 size;
|
||||
GstClockTime duration;
|
||||
gboolean starts_with_sap;
|
||||
guint8 sap_type;
|
||||
guint32 sap_delta_time;
|
||||
|
||||
guint64 offset;
|
||||
GstClockTime pts;
|
||||
} GstSidxBoxEntry;
|
||||
|
||||
typedef struct _GstSidxBox
|
||||
{
|
||||
guint8 version;
|
||||
guint32 flags;
|
||||
|
||||
guint32 ref_id;
|
||||
guint32 timescale;
|
||||
guint64 earliest_pts;
|
||||
guint64 first_offset;
|
||||
|
||||
gint entry_index;
|
||||
gint entries_count;
|
||||
|
||||
GstSidxBoxEntry *entries;
|
||||
} GstSidxBox;
|
||||
|
||||
typedef enum _GstSidxParserStatus
|
||||
{
|
||||
GST_ISOFF_SIDX_PARSER_INIT,
|
||||
GST_ISOFF_SIDX_PARSER_HEADER,
|
||||
GST_ISOFF_SIDX_PARSER_DATA,
|
||||
GST_ISOFF_SIDX_PARSER_FINISHED
|
||||
} GstSidxParserStatus;
|
||||
|
||||
typedef struct _GstSidxParser
|
||||
{
|
||||
GstSidxParserStatus status;
|
||||
|
||||
guint64 size;
|
||||
guint64 cumulative_entry_size;
|
||||
guint64 cumulative_pts;
|
||||
|
||||
GstSidxBox sidx;
|
||||
} GstSidxParser;
|
||||
|
||||
GST_ISOFF_API
|
||||
void gst_isoff_sidx_parser_init (GstSidxParser * parser);
|
||||
|
||||
GST_ISOFF_API
|
||||
void gst_isoff_sidx_parser_clear (GstSidxParser * parser);
|
||||
|
||||
GST_ISOFF_API
|
||||
GstIsoffParserResult gst_isoff_sidx_parser_parse (GstSidxParser * parser, GstByteReader * reader, guint * consumed);
|
||||
|
||||
GST_ISOFF_API
|
||||
GstIsoffParserResult gst_isoff_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buf, guint * consumed);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ISOFF_H__ */
|
File diff suppressed because it is too large
Load diff
2676
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c
Normal file
2676
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,252 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
|
||||
* Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
|
||||
* Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
|
||||
*
|
||||
* gsthlsdemux.h:
|
||||
*
|
||||
* 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_HLS_DEMUX_H__
|
||||
#define __GST_HLS_DEMUX_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include "m3u8.h"
|
||||
#include "gstisoff.h"
|
||||
#include "gstadaptivedemux.h"
|
||||
#if defined(HAVE_OPENSSL)
|
||||
#include <openssl/evp.h>
|
||||
#elif defined(HAVE_NETTLE)
|
||||
#include <nettle/aes.h>
|
||||
#include <nettle/cbc.h>
|
||||
#elif defined(HAVE_LIBGCRYPT)
|
||||
#include <gcrypt.h>
|
||||
#endif
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_HLS_DEMUX2 \
|
||||
(gst_hls_demux2_get_type())
|
||||
#define GST_HLS_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HLS_DEMUX2,GstHLSDemux))
|
||||
#define GST_HLS_DEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HLS_DEMUX2,GstHLSDemuxClass))
|
||||
#define GST_IS_HLS_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HLS_DEMUX2))
|
||||
#define GST_IS_HLS_DEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HLS_DEMUX2))
|
||||
#define GST_HLS_DEMUX_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_HLS_DEMUX2,GstHLSDemuxClass))
|
||||
#define GST_HLS_DEMUX_CAST(obj) \
|
||||
((GstHLSDemux *)obj)
|
||||
|
||||
typedef struct _GstHLSDemux2 GstHLSDemux;
|
||||
typedef struct _GstHLSDemux2Class GstHLSDemuxClass;
|
||||
|
||||
#define GST_TYPE_HLS_DEMUX_STREAM \
|
||||
(gst_hls_demux_stream_get_type())
|
||||
#define GST_HLS_DEMUX_STREAM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HLS_DEMUX_STREAM,GstHLSDemuxStream))
|
||||
#define GST_HLS_DEMUX_STREAM_CAST(obj) ((GstHLSDemuxStream *)obj)
|
||||
|
||||
typedef struct _GstHLSDemuxStream GstHLSDemuxStream;
|
||||
typedef GstAdaptiveDemux2StreamClass GstHLSDemuxStreamClass;
|
||||
|
||||
typedef enum {
|
||||
GST_HLS_PARSER_NONE,
|
||||
GST_HLS_PARSER_MPEGTS,
|
||||
GST_HLS_PARSER_ID3,
|
||||
GST_HLS_PARSER_WEBVTT,
|
||||
GST_HLS_PARSER_ISOBMFF
|
||||
} GstHLSParserType;
|
||||
|
||||
typedef enum {
|
||||
/* More data is needed to parse the fragment */
|
||||
GST_HLS_PARSER_RESULT_NEED_MORE_DATA,
|
||||
/* An error happened, discard the fragment */
|
||||
GST_HLS_PARSER_RESULT_ERROR,
|
||||
/* Parsing suceeded, it no longer needs to be called for the fragment */
|
||||
GST_HLS_PARSER_RESULT_DONE,
|
||||
/* The fragment wasn't the expected one. Current data must be dropped and
|
||||
* GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT returned to the parent class */
|
||||
GST_HLS_PARSER_RESULT_RESYNC
|
||||
} GstHLSParserResult;
|
||||
|
||||
struct _GstHLSDemuxStream
|
||||
{
|
||||
GstAdaptiveDemux2Stream adaptive_demux_stream;
|
||||
|
||||
/* A stream either variants or renditions */
|
||||
gboolean is_variant;
|
||||
|
||||
/* Rendition-specific fields */
|
||||
GstStreamType rendition_type; /* FIXME: Also used by variant streams */
|
||||
gchar *lang;
|
||||
gchar *name;
|
||||
GstHLSRenditionStream *current_rendition;
|
||||
/* rendition to switch to */
|
||||
GstHLSRenditionStream *pending_rendition;
|
||||
/* End of Rendition-specific fields */
|
||||
|
||||
/* Whether the underlying playlist was fetched on creation */
|
||||
gboolean playlist_fetched;
|
||||
|
||||
/* The media playlist currently used */
|
||||
GstHLSMediaPlaylist *playlist;
|
||||
|
||||
/* The segment (from the above playlist) currently being used */
|
||||
GstM3U8MediaSegment *current_segment;
|
||||
|
||||
/* The previous ::advance_fragment() failed for live stream */
|
||||
gboolean pending_advance;
|
||||
|
||||
/* Whether we need to typefind the next buffer */
|
||||
gboolean do_typefind;
|
||||
|
||||
/* for collecting data until typefind succeeds */
|
||||
GstBuffer *pending_typefind_buffer;
|
||||
|
||||
/* for chunking data into 16 byte multiples for decryption */
|
||||
GstAdapter *pending_encrypted_data;
|
||||
|
||||
/* last decrypted buffer for pkcs7 unpadding. We only know that it is the last
|
||||
* on ::finish_fragment() */
|
||||
GstBuffer *pending_decrypted_buffer;
|
||||
|
||||
/* Current offset (in bytes) in fragment data we pushed downstream. Resets to
|
||||
* -1 at every fragment start */
|
||||
guint64 current_offset;
|
||||
|
||||
gboolean reset_pts;
|
||||
|
||||
/* decryption tooling */
|
||||
#if defined(HAVE_OPENSSL)
|
||||
# if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
EVP_CIPHER_CTX aes_ctx;
|
||||
# else
|
||||
EVP_CIPHER_CTX *aes_ctx;
|
||||
# endif
|
||||
#elif defined(HAVE_NETTLE)
|
||||
struct CBC_CTX (struct aes128_ctx, AES_BLOCK_SIZE) aes_ctx;
|
||||
#elif defined(HAVE_LIBGCRYPT)
|
||||
gcry_cipher_hd_t aes_ctx;
|
||||
#endif
|
||||
|
||||
gchar *current_key;
|
||||
guint8 *current_iv;
|
||||
|
||||
/* The type of parser used for data handling */
|
||||
GstHLSParserType parser_type;
|
||||
|
||||
/* Is content processing required ? */
|
||||
gboolean process_buffer_content;
|
||||
/* Data to be analyzed by */
|
||||
GstBuffer *pending_segment_data;
|
||||
/* TRUE if pending_segment_data contains data from a header/index */
|
||||
gboolean pending_data_is_header;
|
||||
|
||||
/* ISOBMFF */
|
||||
GstMoovBox *moov;
|
||||
|
||||
/* Presentation offset to use and report. This value will be appended to all
|
||||
* "output" stream times. Not enabled (i.e 0) if variant is ISOBMFF
|
||||
*/
|
||||
GstClockTime presentation_offset;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
guint8 data[16];
|
||||
} GstHLSKey;
|
||||
|
||||
/**
|
||||
* GstHLSDemux:
|
||||
*
|
||||
* Opaque #GstHLSDemux data structure.
|
||||
*/
|
||||
struct _GstHLSDemux2
|
||||
{
|
||||
GstAdaptiveDemux parent;
|
||||
|
||||
/* Initial bitrate to use before any bandwidth measurement */
|
||||
guint start_bitrate;
|
||||
|
||||
/* Decryption key cache: url => GstHLSKey */
|
||||
GHashTable *keys;
|
||||
GMutex keys_lock;
|
||||
|
||||
/* FIXME: check locking, protected automatically by manifest_lock already? */
|
||||
/* The master playlist with the available variant streams */
|
||||
GstHLSMasterPlaylist *master;
|
||||
|
||||
GstHLSVariantStream *current_variant;
|
||||
/* The variant to switch to */
|
||||
GstHLSVariantStream *pending_variant;
|
||||
|
||||
GstHLSDemuxStream *main_stream;
|
||||
|
||||
/* Time Mappings (GstHLSTimeMap) */
|
||||
GList *mappings;
|
||||
};
|
||||
|
||||
struct _GstHLSDemux2Class
|
||||
{
|
||||
GstAdaptiveDemuxClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
gchar *gst_hls_buf_to_utf8_text (GstBuffer * buf);
|
||||
|
||||
/* Private */
|
||||
|
||||
GstHLSParserResult gst_hlsdemux_handle_content_mpegts (GstHLSDemux *demux,
|
||||
GstHLSDemuxStream *hls_stream,
|
||||
gboolean draining,
|
||||
GstBuffer **buffer);
|
||||
|
||||
GstHLSParserResult gst_hlsdemux_handle_content_id3 (GstHLSDemux *demux,
|
||||
GstHLSDemuxStream *hls_stream,
|
||||
gboolean draining,
|
||||
GstBuffer **buffer);
|
||||
|
||||
GstHLSParserResult gst_hlsdemux_handle_content_isobmff (GstHLSDemux *demux,
|
||||
GstHLSDemuxStream *hls_stream,
|
||||
gboolean draining,
|
||||
GstBuffer **buffer);
|
||||
|
||||
GstHLSParserResult gst_hlsdemux_handle_content_webvtt (GstHLSDemux *demux,
|
||||
GstHLSDemuxStream *hls_stream,
|
||||
gboolean draining,
|
||||
GstBuffer **buffer);
|
||||
|
||||
GstHLSParserResult gst_hlsdemux_handle_internal_time (GstHLSDemux *demux,
|
||||
GstHLSDemuxStream *hls_stream,
|
||||
GstClockTime internal_time);
|
||||
|
||||
GstClockTimeDiff gst_hls_internal_to_stream_time (GstHLSTimeMap *map,
|
||||
GstClockTime internal_time);
|
||||
|
||||
GstHLSTimeMap *gst_hls_find_time_map (GstHLSDemux * demux, gint64 dsn);
|
||||
|
||||
GType gst_hls_demux2_get_type (void);
|
||||
GType gst_hls_demux_stream_get_type (void);
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (hlsdemux2);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_HLS_DEMUX_H__ */
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "gsthlselements.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (hls_debug);
|
||||
|
||||
void
|
||||
hls_element_init (void)
|
||||
{
|
||||
static gsize res = FALSE;
|
||||
if (g_once_init_enter (&res)) {
|
||||
GST_DEBUG_CATEGORY_INIT (hls_debug, "hlsng", 0,
|
||||
"HTTP Live Streaming (HLS) NG");
|
||||
g_once_init_leave (&res, TRUE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __GST_HLS_ELEMENT_H__
|
||||
#define __GST_HLS_ELEMENT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void hls_element_init (void);
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (hls_debug);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_HLS_ELEMENT_H__ */
|
2203
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c
Normal file
2203
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c
Normal file
File diff suppressed because it is too large
Load diff
416
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h
Normal file
416
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h
Normal file
|
@ -0,0 +1,416 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
|
||||
* Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
|
||||
* Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
|
||||
*
|
||||
* Copyright (C) 2021-2022 Centricular Ltd
|
||||
* Author: Edward Hervey <edward@centricular.com>
|
||||
* Author: Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* m3u8.h:
|
||||
*
|
||||
* 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 __M3U8_H__
|
||||
#define __M3U8_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstHLSMediaPlaylist GstHLSMediaPlaylist;
|
||||
typedef struct _GstHLSTimeMap GstHLSTimeMap;
|
||||
typedef struct _GstM3U8MediaSegment GstM3U8MediaSegment;
|
||||
typedef struct _GstM3U8InitFile GstM3U8InitFile;
|
||||
typedef struct _GstHLSRenditionStream GstHLSRenditionStream;
|
||||
typedef struct _GstM3U8Client GstM3U8Client;
|
||||
typedef struct _GstHLSVariantStream GstHLSVariantStream;
|
||||
typedef struct _GstHLSMasterPlaylist GstHLSMasterPlaylist;
|
||||
|
||||
#define GST_HLS_MEDIA_PLAYLIST(m) ((GstHLSMediaPlaylist*)m)
|
||||
#define GST_M3U8_MEDIA_SEGMENT(f) ((GstM3U8MediaSegment*)f)
|
||||
|
||||
#define GST_HLS_MEDIA_PLAYLIST_LOCK(m) g_mutex_lock (&m->lock);
|
||||
#define GST_HLS_MEDIA_PLAYLIST_UNLOCK(m) g_mutex_unlock (&m->lock);
|
||||
|
||||
#define GST_HLS_MEDIA_PLAYLIST_IS_LIVE(m) ((m)->endlist == FALSE)
|
||||
|
||||
/* hlsdemux must not get closer to the end of a live stream than
|
||||
GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE fragments. Section 6.3.3
|
||||
"Playing the Playlist file" of the HLS draft states that this
|
||||
value is three fragments */
|
||||
#define GST_M3U8_LIVE_MIN_FRAGMENT_DISTANCE 3
|
||||
|
||||
typedef enum {
|
||||
GST_HLS_PLAYLIST_TYPE_UNDEFINED,
|
||||
GST_HLS_PLAYLIST_TYPE_EVENT,
|
||||
GST_HLS_PLAYLIST_TYPE_VOD,
|
||||
} GstHLSPlaylistType;
|
||||
|
||||
/**
|
||||
* GstHLSMediaPlaylist:
|
||||
*
|
||||
* Official term in RFC : "Media Playlist". A List of Media Segments.
|
||||
*
|
||||
* It can be used by either a variant stream (GstHLSVariantStream) or an
|
||||
* alternate rendition (GstHLSMedia).
|
||||
*
|
||||
* Note: Was called `GstM3u8` in legacy elements
|
||||
*/
|
||||
|
||||
struct _GstHLSMediaPlaylist
|
||||
{
|
||||
gchar *uri; /* actually downloaded URI */
|
||||
gchar *base_uri; /* URI to use as base for resolving relative URIs.
|
||||
* This will be different to uri in case of redirects */
|
||||
/* Base Tag */
|
||||
gint version; /* EXT-X-VERSION (default 1) */
|
||||
|
||||
/* Media Playlist Tags */
|
||||
GstClockTime targetduration; /* EXT-X-TARGETDURATION, default GST_CLOCK_TIME_NONE */
|
||||
gint64 media_sequence; /* EXT-X-MEDIA-SEQUENCE, MSN of the first Media
|
||||
Segment in the playlist. */
|
||||
gint64 discont_sequence; /* EXT-X-DISCONTINUITY-SEQUENCE. Default : 0 */
|
||||
gboolean has_ext_x_dsn; /* EXT-X-DISCONTINUITY-SEQUENCE present and specified */
|
||||
gboolean endlist; /* EXT-X-ENDLIST present */
|
||||
GstHLSPlaylistType type; /* EXT-X-PLAYLIST-TYPE. Default:
|
||||
GST_HLS_PLAYLIST_TYE_UNDEFINED */
|
||||
gboolean i_frame; /* EXT-X-I-FRAMES-ONLY present. */
|
||||
|
||||
gboolean allowcache; /* deprecated EXT-X-ALLOW-CACHE */
|
||||
|
||||
/* Overview of contained media segments */
|
||||
gboolean ext_x_key_present; /* a valid EXT-X-KEY is present on at least one
|
||||
media segment */
|
||||
gboolean ext_x_pdt_present; /* a valid EXT-X-PROGRAM-DATE-TIME is present on
|
||||
at least one media segment */
|
||||
|
||||
GPtrArray *segments; /* Array of GstM3U8MediaSegment */
|
||||
|
||||
/* Generated information */
|
||||
GstClockTime duration; /* The estimated total duration of all segments
|
||||
contained in this playlist */
|
||||
|
||||
gboolean reloaded; /* If TRUE, this indicates that this playlist
|
||||
* was reloaded but had identical content */
|
||||
|
||||
/*< private > */
|
||||
GMutex lock;
|
||||
|
||||
/* Copy of the incoming data that created this media playlist.
|
||||
* See gst_hls_media_playlist_has_same_data() */
|
||||
gchar *last_data;
|
||||
|
||||
gint ref_count; /* ATOMIC */
|
||||
};
|
||||
|
||||
/* gst_hls_media_playlist_new: Internal function : Do not use from demuxer code, only for unit
|
||||
* testing purposes */
|
||||
GstHLSMediaPlaylist * gst_hls_media_playlist_new (const gchar * uri,
|
||||
const gchar * base_uri);
|
||||
|
||||
GstHLSMediaPlaylist * gst_hls_media_playlist_ref (GstHLSMediaPlaylist * m3u8);
|
||||
|
||||
void gst_hls_media_playlist_unref (GstHLSMediaPlaylist * m3u8);
|
||||
|
||||
/**
|
||||
* GstM3U8MediaSegment:
|
||||
*
|
||||
* Official term in RFC : "Media Segment"
|
||||
*
|
||||
* Note : Naming in legacy elements was GstM3U8MediaFile
|
||||
*/
|
||||
struct _GstM3U8MediaSegment
|
||||
{
|
||||
gchar *title;
|
||||
GstClockTimeDiff stream_time; /* Computed stream time */
|
||||
GstClockTime duration;
|
||||
gchar *uri;
|
||||
gint64 sequence; /* the sequence number of this segment */
|
||||
gint64 discont_sequence; /* The Discontinuity Sequence Number of this segment */
|
||||
gboolean discont; /* this file marks a discontinuity */
|
||||
gchar *key;
|
||||
guint8 iv[16];
|
||||
gint64 offset, size;
|
||||
gint ref_count; /* ATOMIC */
|
||||
GstM3U8InitFile *init_file; /* Media Initialization (hold ref) */
|
||||
GDateTime *datetime; /* EXT-X-PROGRAM-DATE-TIME */
|
||||
};
|
||||
|
||||
struct _GstM3U8InitFile
|
||||
{
|
||||
gchar *uri;
|
||||
gint64 offset, size;
|
||||
guint ref_count; /* ATOMIC */
|
||||
};
|
||||
|
||||
GstM3U8MediaSegment *
|
||||
gst_m3u8_media_segment_ref (GstM3U8MediaSegment * mfile);
|
||||
|
||||
void
|
||||
gst_m3u8_media_segment_unref (GstM3U8MediaSegment * mfile);
|
||||
|
||||
|
||||
gboolean
|
||||
gst_hls_media_playlist_has_same_data (GstHLSMediaPlaylist * m3u8,
|
||||
gchar * playlist_data);
|
||||
|
||||
GstHLSMediaPlaylist *
|
||||
gst_hls_media_playlist_parse (gchar * data,
|
||||
const gchar * uri,
|
||||
const gchar * base_uri);
|
||||
|
||||
void
|
||||
gst_hls_media_playlist_recalculate_stream_time (GstHLSMediaPlaylist *playlist,
|
||||
GstM3U8MediaSegment *anchor);
|
||||
|
||||
GstM3U8MediaSegment *
|
||||
gst_hls_media_playlist_sync_to_segment (GstHLSMediaPlaylist * m3u8,
|
||||
GstM3U8MediaSegment * segment);
|
||||
|
||||
gboolean
|
||||
gst_hls_media_playlist_has_next_fragment (GstHLSMediaPlaylist * m3u8,
|
||||
GstM3U8MediaSegment * current,
|
||||
gboolean forward);
|
||||
|
||||
GstM3U8MediaSegment *
|
||||
gst_hls_media_playlist_advance_fragment (GstHLSMediaPlaylist * m3u8,
|
||||
GstM3U8MediaSegment * current,
|
||||
gboolean forward);
|
||||
|
||||
GstM3U8MediaSegment *
|
||||
gst_hls_media_playlist_get_starting_segment (GstHLSMediaPlaylist *self);
|
||||
|
||||
GstClockTime
|
||||
gst_hls_media_playlist_get_duration (GstHLSMediaPlaylist * m3u8);
|
||||
|
||||
gchar *
|
||||
gst_hls_media_playlist_get_uri (GstHLSMediaPlaylist * m3u8);
|
||||
|
||||
gboolean
|
||||
gst_hls_media_playlist_is_live (GstHLSMediaPlaylist * m3u8);
|
||||
|
||||
gboolean
|
||||
gst_hls_media_playlist_get_seek_range (GstHLSMediaPlaylist * m3u8,
|
||||
gint64 * start,
|
||||
gint64 * stop);
|
||||
|
||||
GstM3U8MediaSegment *
|
||||
gst_hls_media_playlist_seek (GstHLSMediaPlaylist *playlist,
|
||||
gboolean forward,
|
||||
GstSeekFlags flags,
|
||||
GstClockTimeDiff ts);
|
||||
void
|
||||
gst_hls_media_playlist_dump (GstHLSMediaPlaylist* self);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_HLS_RENDITION_STREAM_TYPE_INVALID = -1,
|
||||
GST_HLS_RENDITION_STREAM_TYPE_AUDIO,
|
||||
GST_HLS_RENDITION_STREAM_TYPE_VIDEO,
|
||||
GST_HLS_RENDITION_STREAM_TYPE_SUBTITLES,
|
||||
GST_HLS_RENDITION_STREAM_TYPE_CLOSED_CAPTIONS,
|
||||
GST_HLS_N_MEDIA_TYPES
|
||||
} GstHLSRenditionStreamType;
|
||||
|
||||
/**
|
||||
* GstHLSRenditionStream:
|
||||
*
|
||||
* Official term in RFC : "Renditions are alternate versions of the content,
|
||||
* such as audio produced in different languages or video recorded from
|
||||
* different camera angles."
|
||||
*
|
||||
* Note: Was named GstHLSMedia in legacy elements
|
||||
*/
|
||||
|
||||
struct _GstHLSRenditionStream {
|
||||
GstHLSRenditionStreamType mtype;
|
||||
gchar *group_id;
|
||||
gchar *name;
|
||||
gchar *lang;
|
||||
gchar *uri;
|
||||
GstCaps *caps;
|
||||
gboolean is_default;
|
||||
gboolean autoselect;
|
||||
gboolean forced;
|
||||
|
||||
gint ref_count; /* ATOMIC */
|
||||
};
|
||||
|
||||
GstHLSRenditionStream *
|
||||
gst_hls_rendition_stream_ref (GstHLSRenditionStream * media);
|
||||
|
||||
void
|
||||
gst_hls_rendition_stream_unref (GstHLSRenditionStream * media);
|
||||
|
||||
const gchar *
|
||||
gst_hls_rendition_stream_type_get_name (GstHLSRenditionStreamType mtype);
|
||||
|
||||
|
||||
/**
|
||||
* GstHLSVariantStream:
|
||||
*
|
||||
* Official term in RFC :
|
||||
* """
|
||||
* A Master Playlist provides a set of Variant Streams, each of which describes
|
||||
* a different version of the same content.
|
||||
*
|
||||
* A Variant Stream includes a Media Playlist that specifies media encoded at a
|
||||
* particular bit rate, in a particular format, and at a particular resolution
|
||||
* for media containing video.
|
||||
* """
|
||||
*/
|
||||
struct _GstHLSVariantStream {
|
||||
gchar *name; /* This will be the "name" of the playlist, the original
|
||||
* relative/absolute uri in a variant playlist */
|
||||
gchar *uri;
|
||||
gchar *codecs;
|
||||
GstCaps *caps;
|
||||
GstStreamType codecs_stream_type; /* As defined by codecs */
|
||||
gint bandwidth; /* bits per second */
|
||||
gint program_id;
|
||||
gint width;
|
||||
gint height;
|
||||
gboolean iframe;
|
||||
|
||||
gint refcount; /* ATOMIC */
|
||||
|
||||
/* alternative renditions (names) */
|
||||
gchar *media_groups[GST_HLS_N_MEDIA_TYPES];
|
||||
|
||||
/* List of gchar* fallback uri */
|
||||
GList *fallback;
|
||||
};
|
||||
|
||||
/* Notes: #define are to avoid symbol clashes with legacy hlsdemux */
|
||||
|
||||
#define gst_hls_variant_stream_ref hls_variant_stream_ref
|
||||
GstHLSVariantStream * hls_variant_stream_ref (GstHLSVariantStream * stream);
|
||||
|
||||
#define gst_hls_variant_stream_unref hls_variant_stream_unref
|
||||
void hls_variant_stream_unref (GstHLSVariantStream * stream);
|
||||
|
||||
/**
|
||||
* GstHLSMasterPlaylist:
|
||||
*
|
||||
* Official term in RFC : "A Playlist is either a Media Playlist or a Master
|
||||
* Playlist."
|
||||
*
|
||||
* This is the top-level object, constructed by a manifest provided by external
|
||||
* means.
|
||||
*/
|
||||
struct _GstHLSMasterPlaylist
|
||||
{
|
||||
/* Available variant streams, sorted by bitrate (low -> high) */
|
||||
GList *variants; /* GstHLSVariantStream */
|
||||
GList *iframe_variants; /* GstHLSVariantStream */
|
||||
|
||||
/* Default variant, first in the list (originally, before sorting) */
|
||||
GstHLSVariantStream *default_variant;
|
||||
|
||||
/* Full list of Available Alternative Rendition (GstHLSRenditionStream) */
|
||||
GList *renditions;
|
||||
|
||||
/* EXT-X-VERSION. 0 if unspecified */
|
||||
gint version;
|
||||
|
||||
/* TRUE if this playlist is a simple media playlist (and not a master
|
||||
* playlist). Implies that there is only a single variant and no alternate
|
||||
* rendition groups */
|
||||
gboolean is_simple;
|
||||
|
||||
/* TRUE if all variants have codecs specified */
|
||||
gboolean have_codecs;
|
||||
|
||||
/*< private > */
|
||||
gchar *last_data; /* Copy of the incoming data that created this master playlist */
|
||||
|
||||
gint refcount; /* ATOMIC */
|
||||
};
|
||||
|
||||
/* Notes: #define are to avoid symbol clashes with legacy hlsdemux */
|
||||
|
||||
#define gst_hls_master_playlist_new_from_data hls_master_playlist_new_from_data
|
||||
GstHLSMasterPlaylist * hls_master_playlist_new_from_data (gchar * data,
|
||||
const gchar * base_uri);
|
||||
|
||||
#define gst_hls_master_playlist_get_variant_for_bitrate hls_master_playlist_get_variant_for_bitrate
|
||||
GstHLSVariantStream * hls_master_playlist_get_variant_for_bitrate (GstHLSMasterPlaylist * playlist,
|
||||
GstHLSVariantStream * current_variant,
|
||||
guint bitrate,
|
||||
guint min_bitrate);
|
||||
|
||||
#define gst_hls_master_playlist_get_common_caps hls_master_playlist_get_common_caps
|
||||
GstCaps * hls_master_playlist_get_common_caps (GstHLSMasterPlaylist *playlist);
|
||||
|
||||
#define gst_hls_master_playlist_unref hls_master_playlist_unref
|
||||
void hls_master_playlist_unref (GstHLSMasterPlaylist * playlist);
|
||||
|
||||
|
||||
/* Time Mapping
|
||||
*
|
||||
* Used to map GStreamer times to internal segment timestamps
|
||||
*/
|
||||
struct _GstHLSTimeMap {
|
||||
/* DISCONT SEQUENCE NUMBER */
|
||||
gint64 dsn;
|
||||
|
||||
/* The stream time (used for gst timestamps, gst segments, seeking ...) */
|
||||
GstClockTime stream_time;
|
||||
|
||||
/* The optional Program Date Time reference */
|
||||
GDateTime *pdt;
|
||||
|
||||
/* The internal time (ex: mpeg-ts PTS) */
|
||||
GstClockTime internal_time;
|
||||
};
|
||||
|
||||
GstStreamType gst_stream_type_from_hls_type (GstHLSRenditionStreamType stype);
|
||||
GstStreamType gst_hls_get_stream_type_from_structure (GstStructure *structure);
|
||||
GstStreamType gst_hls_get_stream_type_from_caps (GstCaps *caps);
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2, 62, 0)
|
||||
static inline gchar* g_date_time_format_iso8601(GDateTime* datetime) {
|
||||
GString* outstr = NULL;
|
||||
gchar* main_date = NULL;
|
||||
gint64 offset;
|
||||
|
||||
// Main date and time.
|
||||
main_date = g_date_time_format(datetime, "%Y-%m-%dT%H:%M:%S");
|
||||
outstr = g_string_new(main_date);
|
||||
g_free(main_date);
|
||||
|
||||
// Timezone. Format it as `%:::z` unless the offset is zero, in which case
|
||||
// we can simply use `Z`.
|
||||
offset = g_date_time_get_utc_offset(datetime);
|
||||
|
||||
if (offset == 0) {
|
||||
g_string_append_c(outstr, 'Z');
|
||||
} else {
|
||||
gchar* time_zone = g_date_time_format(datetime, "%:::z");
|
||||
g_string_append(outstr, time_zone);
|
||||
g_free(time_zone);
|
||||
}
|
||||
|
||||
return g_string_free(outstr, FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __M3U8_H__ */
|
|
@ -0,0 +1,41 @@
|
|||
hls_sources = [
|
||||
'hls/gsthlsdemux.c',
|
||||
'hls/gsthlsdemux-util.c',
|
||||
'hls/gsthlselement.c',
|
||||
'hls/m3u8.c',
|
||||
]
|
||||
|
||||
hls_cargs = []
|
||||
|
||||
hls_crypto = get_option('hls-crypto')
|
||||
hls_crypto_dep = dependency('', required : false)
|
||||
|
||||
if ['auto', 'nettle'].contains(hls_crypto)
|
||||
hls_crypto_dep = dependency('nettle', version : '>= 3.0', required : false)
|
||||
if hls_crypto_dep.found()
|
||||
hls_cargs += ['-DHAVE_NETTLE']
|
||||
endif
|
||||
endif
|
||||
|
||||
if not hls_crypto_dep.found() and ['auto', 'libgcrypt'].contains(hls_crypto)
|
||||
hls_crypto_dep = cc.find_library('gcrypt', required : false)
|
||||
if hls_crypto_dep.found()
|
||||
hls_cargs += ['-DHAVE_LIBGCRYPT']
|
||||
endif
|
||||
endif
|
||||
|
||||
if not hls_crypto_dep.found() and ['auto', 'openssl'].contains(hls_crypto)
|
||||
hls_crypto_dep = dependency('openssl', required : false)
|
||||
if hls_crypto_dep.found()
|
||||
hls_cargs += ['-DHAVE_OPENSSL']
|
||||
endif
|
||||
endif
|
||||
|
||||
if not hls_crypto_dep.found()
|
||||
if hls_crypto == 'auto'
|
||||
message('Could not find a supported crypto library for HLS support')
|
||||
else
|
||||
error('HLS crypto support library "@0@" not found'.format(hls_crypto))
|
||||
endif
|
||||
endif
|
||||
hls_dep = declare_dependency(include_directories : include_directories('.'))
|
153
subprojects/gst-plugins-good/ext/adaptivedemux2/meson.build
Normal file
153
subprojects/gst-plugins-good/ext/adaptivedemux2/meson.build
Normal file
|
@ -0,0 +1,153 @@
|
|||
dash_sources = [
|
||||
'dash/gstdashdemux.c',
|
||||
'dash/gstmpdnode.c',
|
||||
'dash/gstmpdrootnode.c',
|
||||
'dash/gstmpdbaseurlnode.c',
|
||||
'dash/gstmpdutctimingnode.c',
|
||||
'dash/gstmpdmetricsnode.c',
|
||||
'dash/gstmpdmetricsrangenode.c',
|
||||
'dash/gstmpdsnode.c',
|
||||
'dash/gstmpdsegmenttimelinenode.c',
|
||||
'dash/gstmpdsegmenttemplatenode.c',
|
||||
'dash/gstmpdsegmenturlnode.c',
|
||||
'dash/gstmpdsegmentlistnode.c',
|
||||
'dash/gstmpdsegmentbasenode.c',
|
||||
'dash/gstmpdperiodnode.c',
|
||||
'dash/gstmpdrepresentationbasenode.c',
|
||||
'dash/gstmpdmultsegmentbasenode.c',
|
||||
'dash/gstmpdrepresentationnode.c',
|
||||
'dash/gstmpdsubrepresentationnode.c',
|
||||
'dash/gstmpdcontentcomponentnode.c',
|
||||
'dash/gstmpdadaptationsetnode.c',
|
||||
'dash/gstmpdsubsetnode.c',
|
||||
'dash/gstmpdprograminformationnode.c',
|
||||
'dash/gstmpdlocationnode.c',
|
||||
'dash/gstmpdreportingnode.c',
|
||||
'dash/gstmpdurltypenode.c',
|
||||
'dash/gstmpddescriptortypenode.c',
|
||||
'dash/gstxmlhelper.c',
|
||||
'dash/gstmpdhelper.c',
|
||||
'dash/gstmpdparser.c',
|
||||
'dash/gstmpdclient.c'
|
||||
]
|
||||
|
||||
smoothstreaming_sources = [
|
||||
'mss/gstmssdemux.c',
|
||||
'mss/gstmssmanifest.c',
|
||||
'mss/gstmssfragmentparser.c',
|
||||
]
|
||||
|
||||
plugin_sources = [
|
||||
'plugin.c',
|
||||
'gstisoff.c',
|
||||
'gstadaptivedemux.c',
|
||||
'gstadaptivedemuxutils.c',
|
||||
'gstadaptivedemux-period.c',
|
||||
'gstadaptivedemux-stream.c',
|
||||
'gstadaptivedemux-track.c',
|
||||
'downloadhelper.c',
|
||||
'downloadrequest.c',
|
||||
'../soup/gstsouploader.c'
|
||||
]
|
||||
|
||||
# Used for unit tests, so need to be defined even if we skip the subdir
|
||||
hls_dep = dependency('', required : false)
|
||||
adaptivedemux2_dep = dependency('', required : false)
|
||||
|
||||
adaptivedemux2_opt = get_option('adaptivedemux2')
|
||||
if adaptivedemux2_opt.disabled()
|
||||
message('Not building adaptivedemux2 plugin because it was disabled')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
adaptive_xml2_dep = dependency('libxml-2.0', version : '>= 2.8', allow_fallback: true, required: adaptivedemux2_opt)
|
||||
|
||||
if not adaptive_xml2_dep.found()
|
||||
message(f'Not building adaptivedemux2 plugin: libxml2 is needed')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
subdir('hls')
|
||||
|
||||
plugin_sources += dash_sources
|
||||
plugin_sources += smoothstreaming_sources
|
||||
plugin_sources += hls_sources
|
||||
|
||||
soup_loader_args = ['-DBUILDING_ADAPTIVEDEMUX2']
|
||||
|
||||
default_library = get_option('default_library')
|
||||
if default_library in ['static', 'both']
|
||||
libsoup2_dep = dependency('libsoup-2.4', version : '>=2.48',
|
||||
required : false, fallback : ['libsoup', 'libsoup_dep'],
|
||||
default_options: ['sysprof=disabled'])
|
||||
libsoup3_dep = dependency('libsoup-3.0', required : false,
|
||||
fallback : ['libsoup3', 'libsoup_dep'])
|
||||
|
||||
if libsoup3_dep.found()
|
||||
soup_dep = libsoup3_dep
|
||||
static_soup_loader_args = ['-DSTATIC_SOUP=3']
|
||||
elif libsoup2_dep.found()
|
||||
soup_dep = libsoup2_dep
|
||||
static_soup_loader_args = ['-DSTATIC_SOUP=2']
|
||||
else
|
||||
if adaptivedemux2_opt.enabled()
|
||||
error(f'adaptivedemux2: Either libsoup2 or libsoup3 is needed for build with default_library=@default_library@')
|
||||
endif
|
||||
|
||||
message(f'Not building adaptivedemux2 plugin: either libsoup2 or libsoup3 is needed for build with default_library=@default_library@')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
# Static plugin links to libsoup directly at build time
|
||||
adaptivedemux2_static = static_library('gstadaptivedemux2',
|
||||
plugin_sources,
|
||||
include_directories: [configinc, libsinc],
|
||||
c_args: [gst_plugins_good_args, soup_loader_args, soup_loader_args, hls_cargs,
|
||||
'-DGST_ISOFF_API=G_GNUC_INTERNAL'],
|
||||
dependencies: [gsttag_dep, gstnet_dep, gstbase_dep,
|
||||
gstpbutils_dep, gstapp_dep, soup_dep,
|
||||
gio_dep, adaptive_xml2_dep,
|
||||
hls_crypto_dep, libm],
|
||||
install: true,
|
||||
install_dir: plugins_install_dir)
|
||||
endif
|
||||
|
||||
if default_library in ['shared', 'both']
|
||||
# Shared plugin doesn't link to libsoup but dlopen()s it at runtime
|
||||
libdl = cc.find_library('dl', required: false)
|
||||
|
||||
adaptivedemux2_shared = shared_library('gstadaptivedemux2',
|
||||
plugin_sources,
|
||||
include_directories: [configinc, libsinc],
|
||||
c_args: [gst_plugins_good_args, soup_loader_args, hls_cargs,
|
||||
'-DGST_ISOFF_API=G_GNUC_INTERNAL'],
|
||||
dependencies: [gsttag_dep, gstnet_dep, gstbase_dep,
|
||||
gstpbutils_dep, gstapp_dep, gio_dep,
|
||||
gmodule_dep, adaptive_xml2_dep,
|
||||
hls_crypto_dep, libm, libdl],
|
||||
install: true,
|
||||
install_dir: plugins_install_dir)
|
||||
endif
|
||||
|
||||
# Use the static library to generate the .pc file if it's available. The shared
|
||||
# library .pc file does not have a Requires: on libsoup, and we use plugin
|
||||
# .pc files to generate dependencies for linking plugins statically.
|
||||
if default_library == 'shared'
|
||||
pkgconfig.generate(adaptivedemux2_shared, install_dir: plugins_pkgconfig_install_dir)
|
||||
else
|
||||
pkgconfig.generate(adaptivedemux2_static, install_dir: plugins_pkgconfig_install_dir)
|
||||
endif
|
||||
|
||||
# Add the shared library to the plugins list if available. We pass this list of
|
||||
# plugins to hotdoc to generate the plugins cache, which introspects the plugin
|
||||
# by loading it. We need the shared plugin for that.
|
||||
if default_library == 'static'
|
||||
plugins += [adaptivedemux2_static]
|
||||
else
|
||||
plugins += [adaptivedemux2_shared]
|
||||
endif
|
||||
|
||||
# For unit tests
|
||||
adaptivedemux2_dep = declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
dependencies: adaptive_xml2_dep)
|
|
@ -0,0 +1,671 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2012 Smart TV Alliance
|
||||
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
|
||||
*
|
||||
* gstmssdemux.c:
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-mssdemux
|
||||
* @title: mssdemux
|
||||
*
|
||||
* Demuxes a Microsoft's Smooth Streaming manifest into its audio and/or video streams.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* == Internals
|
||||
*
|
||||
* = Smooth streaming in a few lines
|
||||
* A SS stream is defined by a xml manifest file. This file has a list of
|
||||
* tracks (StreamIndex), each one can have multiple QualityLevels, that define
|
||||
* different encoding/bitrates. When playing a track, only one of those
|
||||
* QualityLevels can be active at a time (per stream).
|
||||
*
|
||||
* The StreamIndex defines a URL with {time} and {bitrate} tags that are
|
||||
* replaced by values indicated by the fragment start times and the selected
|
||||
* QualityLevel, that generates the fragments URLs.
|
||||
*
|
||||
* Another relevant detail is that the Isomedia fragments for smoothstreaming
|
||||
* won't contains a 'moov' atom, nor a 'stsd', so there is no information
|
||||
* about the media type/configuration on the fragments, it must be extracted
|
||||
* from the Manifest and passed downstream. mssdemux does this via GstCaps.
|
||||
*
|
||||
* = How mssdemux works
|
||||
* There is a gstmssmanifest.c utility that holds the manifest and parses
|
||||
* and has functions to extract information from it. mssdemux received the
|
||||
* manifest from its sink pad and starts processing it when it gets EOS.
|
||||
*
|
||||
* The Manifest is parsed and the streams are exposed, 1 pad for each, with
|
||||
* a initially selected QualityLevel. Each stream starts its own GstTaks that
|
||||
* is responsible for downloading fragments and pushing them downstream.
|
||||
*
|
||||
* When a new connection-speed is set, mssdemux evaluates the available
|
||||
* QualityLevels and might decide to switch to another one. In this case it
|
||||
* pushes a new GstCaps event indicating the new caps on the pads.
|
||||
*
|
||||
* All operations that intend to update the GstTasks state should be protected
|
||||
* with the GST_OBJECT_LOCK.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gst/gst-i18n-plugin.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gstmssdemux.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (mssdemux2_debug);
|
||||
|
||||
static GstStaticPadTemplate gst_mss_demux_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/vnd.ms-sstr+xml")
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate gst_mss_demux_videosrc_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("video_%02u",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
static GstStaticPadTemplate gst_mss_demux_audiosrc_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("audio_%02u",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
typedef struct _GstMssDemux2 GstMssDemux2;
|
||||
typedef struct _GstMssDemux2Class GstMssDemux2Class;
|
||||
|
||||
#define gst_mss_demux2_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstMssDemux2, gst_mss_demux2, GST_TYPE_ADAPTIVE_DEMUX);
|
||||
|
||||
#define gst_hls_demux_stream_parent_class stream_parent_class
|
||||
G_DEFINE_TYPE (GstMssDemuxStream, gst_mss_demux_stream,
|
||||
GST_TYPE_ADAPTIVE_DEMUX2_STREAM);
|
||||
|
||||
GST_ELEMENT_REGISTER_DEFINE (mssdemux2, "mssdemux2",
|
||||
GST_RANK_PRIMARY + 1, GST_TYPE_MSS_DEMUX2);
|
||||
|
||||
static void gst_mss_demux_dispose (GObject * object);
|
||||
|
||||
static gboolean gst_mss_demux_is_live (GstAdaptiveDemux * demux);
|
||||
static gboolean gst_mss_demux_process_manifest (GstAdaptiveDemux * demux,
|
||||
GstBuffer * buffer);
|
||||
static GstClockTime gst_mss_demux_get_duration (GstAdaptiveDemux * demux);
|
||||
static void gst_mss_demux_reset (GstAdaptiveDemux * demux);
|
||||
static GstFlowReturn gst_mss_demux_stream_seek (GstAdaptiveDemux2Stream *
|
||||
stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff ts,
|
||||
GstClockTimeDiff * final_ts);
|
||||
static gboolean gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream
|
||||
* stream);
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream);
|
||||
static gboolean gst_mss_demux_stream_select_bitrate (GstAdaptiveDemux2Stream *
|
||||
stream, guint64 bitrate);
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream);
|
||||
static gboolean gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
|
||||
static gint64
|
||||
gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux);
|
||||
static GstClockTime
|
||||
gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
|
||||
stream);
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
|
||||
GstBuffer * buffer);
|
||||
static gboolean gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux,
|
||||
gint64 * start, gint64 * stop);
|
||||
static GstFlowReturn gst_mss_demux_data_received (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer);
|
||||
static gboolean
|
||||
gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux);
|
||||
GstStreamType gst_stream_type_from_mss_type (GstMssStreamType mtype);
|
||||
|
||||
static void
|
||||
gst_mss_demux_stream_class_init (GstMssDemuxStreamClass * klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mss_demux_stream_init (GstMssDemuxStream * stream)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mss_demux2_class_init (GstMssDemuxClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
GstAdaptiveDemuxClass *gstadaptivedemux_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gstadaptivedemux_class = (GstAdaptiveDemuxClass *) klass;
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_mss_demux_sink_template);
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_mss_demux_videosrc_template);
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_mss_demux_audiosrc_template);
|
||||
gst_element_class_set_static_metadata (gstelement_class,
|
||||
"Smooth Streaming demuxer (v2)", "Codec/Demuxer/Adaptive",
|
||||
"Parse and demultiplex a Smooth Streaming manifest into audio and video "
|
||||
"streams", "Thiago Santos <thiago.sousa.santos@collabora.com>");
|
||||
|
||||
gobject_class->dispose = gst_mss_demux_dispose;
|
||||
|
||||
gstadaptivedemux_class->process_manifest = gst_mss_demux_process_manifest;
|
||||
gstadaptivedemux_class->is_live = gst_mss_demux_is_live;
|
||||
gstadaptivedemux_class->get_duration = gst_mss_demux_get_duration;
|
||||
gstadaptivedemux_class->get_manifest_update_interval =
|
||||
gst_mss_demux_get_manifest_update_interval;
|
||||
gstadaptivedemux_class->reset = gst_mss_demux_reset;
|
||||
gstadaptivedemux_class->seek = gst_mss_demux_seek;
|
||||
gstadaptivedemux_class->stream_seek = gst_mss_demux_stream_seek;
|
||||
gstadaptivedemux_class->stream_advance_fragment =
|
||||
gst_mss_demux_stream_advance_fragment;
|
||||
gstadaptivedemux_class->stream_has_next_fragment =
|
||||
gst_mss_demux_stream_has_next_fragment;
|
||||
gstadaptivedemux_class->stream_select_bitrate =
|
||||
gst_mss_demux_stream_select_bitrate;
|
||||
gstadaptivedemux_class->stream_update_fragment_info =
|
||||
gst_mss_demux_stream_update_fragment_info;
|
||||
gstadaptivedemux_class->stream_get_fragment_waiting_time =
|
||||
gst_mss_demux_stream_get_fragment_waiting_time;
|
||||
gstadaptivedemux_class->update_manifest_data =
|
||||
gst_mss_demux_update_manifest_data;
|
||||
gstadaptivedemux_class->get_live_seek_range =
|
||||
gst_mss_demux_get_live_seek_range;
|
||||
gstadaptivedemux_class->data_received = gst_mss_demux_data_received;
|
||||
gstadaptivedemux_class->requires_periodical_playlist_update =
|
||||
gst_mss_demux_requires_periodical_playlist_update;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (mssdemux2_debug, "mssdemux2", 0,
|
||||
"mssdemux2 element");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mss_demux2_init (GstMssDemux * mssdemux)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mss_demux_reset (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
|
||||
if (mssdemux->manifest) {
|
||||
gst_mss_manifest_free (mssdemux->manifest);
|
||||
mssdemux->manifest = NULL;
|
||||
}
|
||||
g_free (mssdemux->base_url);
|
||||
mssdemux->base_url = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mss_demux_dispose (GObject * object)
|
||||
{
|
||||
gst_mss_demux_reset (GST_ADAPTIVE_DEMUX_CAST (object));
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_is_live (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
|
||||
g_return_val_if_fail (mssdemux->manifest != NULL, FALSE);
|
||||
|
||||
return gst_mss_manifest_is_live (mssdemux->manifest);
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
gst_mss_demux_get_duration (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
|
||||
g_return_val_if_fail (mssdemux->manifest != NULL, FALSE);
|
||||
|
||||
return gst_mss_manifest_get_gst_duration (mssdemux->manifest);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream)
|
||||
{
|
||||
GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (stream->demux);
|
||||
GstFlowReturn ret;
|
||||
gchar *path = NULL;
|
||||
|
||||
gst_adaptive_demux2_stream_fragment_clear (&stream->fragment);
|
||||
ret = gst_mss_stream_get_fragment_url (mssstream->manifest_stream, &path);
|
||||
|
||||
if (ret == GST_FLOW_OK) {
|
||||
stream->fragment.uri = g_strdup_printf ("%s/%s", mssdemux->base_url, path);
|
||||
stream->fragment.stream_time =
|
||||
gst_mss_stream_get_fragment_gst_timestamp (mssstream->manifest_stream);
|
||||
stream->fragment.duration =
|
||||
gst_mss_stream_get_fragment_gst_duration (mssstream->manifest_stream);
|
||||
}
|
||||
g_free (path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward,
|
||||
GstSeekFlags flags, GstClockTimeDiff ts, GstClockTimeDiff * final_ts)
|
||||
{
|
||||
GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
|
||||
|
||||
gst_mss_stream_seek (mssstream->manifest_stream, forward, flags, ts,
|
||||
final_ts);
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream)
|
||||
{
|
||||
GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
|
||||
|
||||
if (stream->demux->segment.rate >= 0)
|
||||
return gst_mss_stream_advance_fragment (mssstream->manifest_stream);
|
||||
else
|
||||
return gst_mss_stream_regress_fragment (mssstream->manifest_stream);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
create_mss_caps (GstMssDemuxStream * stream, GstCaps * caps)
|
||||
{
|
||||
return gst_caps_new_simple ("video/quicktime", "variant", G_TYPE_STRING,
|
||||
"mss-fragmented", "timescale", G_TYPE_UINT64,
|
||||
gst_mss_stream_get_timescale (stream->manifest_stream), "media-caps",
|
||||
GST_TYPE_CAPS, caps, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mss_demux_apply_protection_system (GstCaps * caps,
|
||||
const gchar * selected_system)
|
||||
{
|
||||
GstStructure *s;
|
||||
|
||||
g_return_if_fail (selected_system);
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
gst_structure_set (s,
|
||||
"original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
|
||||
GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
|
||||
NULL);
|
||||
gst_structure_set_name (s, "application/x-cenc");
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
GSList *streams = gst_mss_manifest_get_streams (mssdemux->manifest);
|
||||
GSList *active_streams = NULL;
|
||||
GSList *iter;
|
||||
const gchar *protection_system_id =
|
||||
gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
|
||||
const gchar *protection_data =
|
||||
gst_mss_manifest_get_protection_data (mssdemux->manifest);
|
||||
gboolean protected = protection_system_id && protection_data;
|
||||
const gchar *selected_system = NULL;
|
||||
guint64 max_bitrate = G_MAXUINT64;
|
||||
|
||||
if (streams == NULL) {
|
||||
GST_INFO_OBJECT (mssdemux, "No streams found in the manifest");
|
||||
GST_ELEMENT_ERROR (mssdemux, STREAM, DEMUX,
|
||||
(_("This file contains no playable streams.")),
|
||||
("no streams found at the Manifest"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (protected) {
|
||||
const gchar *sys_ids[2] = { protection_system_id, NULL };
|
||||
|
||||
selected_system = gst_protection_select_system (sys_ids);
|
||||
if (!selected_system) {
|
||||
GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
|
||||
"suitable decryptor element has been found");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (demux->connection_speed != 0)
|
||||
max_bitrate = demux->connection_speed;
|
||||
|
||||
for (iter = streams; iter; iter = g_slist_next (iter)) {
|
||||
GstAdaptiveDemux2Stream *stream = NULL;
|
||||
GstMssDemuxStream *mss_stream;
|
||||
GstMssStream *manifeststream = iter->data;
|
||||
GstAdaptiveDemuxTrack *track;
|
||||
GstStreamType stream_type =
|
||||
gst_stream_type_from_mss_type (gst_mss_stream_get_type
|
||||
(manifeststream));
|
||||
const gchar *lang, *stream_id = gst_stream_type_get_name (stream_type);
|
||||
gchar *name;
|
||||
GstCaps *caps;
|
||||
GstTagList *tags = NULL;
|
||||
|
||||
name = g_strdup_printf ("mss-stream-%s", stream_id);
|
||||
mss_stream = g_object_new (GST_TYPE_MSS_DEMUX_STREAM, "name", name, NULL);
|
||||
g_free (name);
|
||||
|
||||
stream = GST_ADAPTIVE_DEMUX2_STREAM_CAST (mss_stream);
|
||||
stream->stream_type = stream_type;
|
||||
|
||||
mss_stream->manifest_stream = manifeststream;
|
||||
gst_mss_stream_set_active (manifeststream, TRUE);
|
||||
|
||||
/* Set the maximum bitrate now that the underlying stream is active. This
|
||||
* ensures that we get the proper caps and information. */
|
||||
gst_mss_stream_select_bitrate (manifeststream, max_bitrate);
|
||||
|
||||
caps = gst_mss_stream_get_caps (mss_stream->manifest_stream);
|
||||
gst_adaptive_demux2_stream_set_caps (stream, create_mss_caps (mss_stream,
|
||||
caps));
|
||||
lang = gst_mss_stream_get_lang (mss_stream->manifest_stream);
|
||||
if (lang != NULL)
|
||||
tags = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, lang, NULL);
|
||||
|
||||
track = gst_adaptive_demux_track_new (demux, stream_type,
|
||||
GST_STREAM_FLAG_NONE, (gchar *) stream_id, create_mss_caps (mss_stream,
|
||||
caps), tags);
|
||||
|
||||
gst_adaptive_demux2_add_stream (demux, stream);
|
||||
gst_adaptive_demux2_stream_add_track (stream, track);
|
||||
gst_adaptive_demux_track_unref (track);
|
||||
|
||||
GST_DEBUG_OBJECT (stream, "Current quality bitrate %" G_GUINT64_FORMAT,
|
||||
gst_mss_stream_get_current_bitrate (manifeststream));
|
||||
|
||||
if (tags)
|
||||
gst_adaptive_demux2_stream_set_tags (stream, tags);
|
||||
|
||||
active_streams = g_slist_prepend (active_streams, mss_stream);
|
||||
}
|
||||
|
||||
for (iter = active_streams; iter; iter = g_slist_next (iter)) {
|
||||
GstMssDemuxStream *stream = iter->data;
|
||||
|
||||
if (protected) {
|
||||
GstBuffer *protection_buffer =
|
||||
gst_buffer_new_wrapped (g_strdup (protection_data),
|
||||
strlen (protection_data));
|
||||
GstEvent *event =
|
||||
gst_event_new_protection (protection_system_id, protection_buffer,
|
||||
"smooth-streaming");
|
||||
|
||||
GST_LOG_OBJECT (stream, "Queueing Protection event on source pad");
|
||||
gst_adaptive_demux2_stream_queue_event ((GstAdaptiveDemux2Stream *)
|
||||
stream, event);
|
||||
gst_buffer_unref (protection_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
g_slist_free (active_streams);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mss_demux_update_base_url (GstMssDemux * mssdemux)
|
||||
{
|
||||
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (mssdemux);
|
||||
gchar *baseurl_end;
|
||||
|
||||
g_free (mssdemux->base_url);
|
||||
|
||||
mssdemux->base_url =
|
||||
g_strdup (demux->manifest_base_uri ? demux->manifest_base_uri : demux->
|
||||
manifest_uri);
|
||||
baseurl_end = g_strrstr (mssdemux->base_url, "/Manifest");
|
||||
if (baseurl_end == NULL) {
|
||||
/* second try */
|
||||
baseurl_end = g_strrstr (mssdemux->base_url, "/manifest");
|
||||
}
|
||||
if (baseurl_end) {
|
||||
/* set the new end of the string */
|
||||
baseurl_end[0] = '\0';
|
||||
} else {
|
||||
GST_WARNING_OBJECT (mssdemux, "Stream's URI didn't end with /manifest");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
|
||||
gst_mss_demux_update_base_url (mssdemux);
|
||||
|
||||
mssdemux->manifest = gst_mss_manifest_new (buf);
|
||||
if (!mssdemux->manifest) {
|
||||
GST_ELEMENT_ERROR (mssdemux, STREAM, FORMAT, ("Bad manifest file"),
|
||||
("Xml manifest file couldn't be parsed"));
|
||||
return FALSE;
|
||||
}
|
||||
return gst_mss_demux_setup_streams (demux);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
|
||||
guint64 bitrate)
|
||||
{
|
||||
GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
GST_DEBUG_OBJECT (stream,
|
||||
"Using stream download bitrate %" G_GUINT64_FORMAT, bitrate);
|
||||
|
||||
if (gst_mss_stream_select_bitrate (mssstream->manifest_stream,
|
||||
bitrate / MAX (1.0, ABS (stream->demux->segment.rate)))) {
|
||||
GstCaps *caps;
|
||||
GstCaps *msscaps;
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (stream->demux);
|
||||
const gchar *protection_system_id =
|
||||
gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
|
||||
const gchar *protection_data =
|
||||
gst_mss_manifest_get_protection_data (mssdemux->manifest);
|
||||
gboolean protected = protection_system_id && protection_data;
|
||||
|
||||
caps = gst_mss_stream_get_caps (mssstream->manifest_stream);
|
||||
|
||||
GST_DEBUG_OBJECT (stream,
|
||||
"Starting streams reconfiguration due to bitrate changes");
|
||||
|
||||
if (protected) {
|
||||
const gchar *sys_ids[2] = { protection_system_id, NULL };
|
||||
const gchar *selected_system = gst_protection_select_system (sys_ids);
|
||||
|
||||
if (!selected_system) {
|
||||
GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
|
||||
"suitable decryptor element has been found");
|
||||
gst_caps_unref (caps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_mss_demux_apply_protection_system (caps, selected_system);
|
||||
}
|
||||
|
||||
msscaps = create_mss_caps (mssstream, caps);
|
||||
|
||||
GST_DEBUG_OBJECT (stream,
|
||||
"Stream changed bitrate to %" G_GUINT64_FORMAT " caps: %"
|
||||
GST_PTR_FORMAT,
|
||||
gst_mss_stream_get_current_bitrate (mssstream->manifest_stream), caps);
|
||||
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_adaptive_demux2_stream_set_caps (stream, msscaps);
|
||||
ret = TRUE;
|
||||
GST_DEBUG_OBJECT (stream, "Finished streams reconfiguration");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SEEK_UPDATES_PLAY_POSITION(r, start_type, stop_type) \
|
||||
((r >= 0 && start_type != GST_SEEK_TYPE_NONE) || \
|
||||
(r < 0 && stop_type != GST_SEEK_TYPE_NONE))
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
|
||||
{
|
||||
gdouble rate;
|
||||
GstFormat format;
|
||||
GstSeekFlags flags;
|
||||
GstSeekType start_type, stop_type;
|
||||
gint64 start, stop;
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
|
||||
gst_event_parse_seek (seek, &rate, &format, &flags, &start_type, &start,
|
||||
&stop_type, &stop);
|
||||
|
||||
GST_DEBUG_OBJECT (mssdemux,
|
||||
"seek event, rate: %f start: %" GST_TIME_FORMAT " stop: %"
|
||||
GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
|
||||
|
||||
if (SEEK_UPDATES_PLAY_POSITION (rate, start_type, stop_type)) {
|
||||
if (rate >= 0)
|
||||
gst_mss_manifest_seek (mssdemux->manifest, rate >= 0, start);
|
||||
else
|
||||
gst_mss_manifest_seek (mssdemux->manifest, rate >= 0, stop);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemux2Stream * stream)
|
||||
{
|
||||
GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
|
||||
|
||||
return gst_mss_stream_has_next_fragment (mssstream->manifest_stream);
|
||||
}
|
||||
|
||||
static gint64
|
||||
gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
GstClockTime interval;
|
||||
|
||||
/* Not much information about this in the MSS spec. It seems that
|
||||
* the fragments contain an UUID box that should tell the next
|
||||
* fragments time and duration so one wouldn't need to fetch
|
||||
* the Manifest again, but we need a fallback here. So use 2 times
|
||||
* the current fragment duration */
|
||||
|
||||
interval = gst_mss_manifest_get_min_fragment_duration (mssdemux->manifest);
|
||||
if (!GST_CLOCK_TIME_IS_VALID (interval))
|
||||
interval = 2 * GST_SECOND; /* default to 2 seconds */
|
||||
|
||||
interval = 2 * (interval / GST_USECOND);
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemux2Stream *
|
||||
stream)
|
||||
{
|
||||
/* Wait a second for live streams so we don't try premature fragments downloading */
|
||||
return GST_SECOND;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
|
||||
GstBuffer * buffer)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
|
||||
gst_mss_demux_update_base_url (mssdemux);
|
||||
|
||||
gst_mss_manifest_reload_fragments (mssdemux->manifest, buffer);
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
|
||||
gint64 * stop)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
|
||||
return gst_mss_manifest_get_live_seek_range (mssdemux->manifest, start, stop);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_mss_demux_data_received (GstAdaptiveDemux * demux,
|
||||
GstAdaptiveDemux2Stream * stream, GstBuffer * buffer)
|
||||
{
|
||||
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
|
||||
GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
|
||||
gsize available;
|
||||
|
||||
if (!gst_mss_manifest_is_live (mssdemux->manifest)) {
|
||||
return gst_adaptive_demux2_stream_push_buffer (stream, buffer);
|
||||
}
|
||||
|
||||
if (gst_mss_stream_fragment_parsing_needed (mssstream->manifest_stream)) {
|
||||
gst_mss_manifest_live_adapter_push (mssstream->manifest_stream, buffer);
|
||||
available =
|
||||
gst_mss_manifest_live_adapter_available (mssstream->manifest_stream);
|
||||
// FIXME: try to reduce this minimal size.
|
||||
if (available < 4096) {
|
||||
return GST_FLOW_OK;
|
||||
} else {
|
||||
GST_LOG_OBJECT (stream, "enough data, parsing fragment.");
|
||||
buffer =
|
||||
gst_mss_manifest_live_adapter_take_buffer (mssstream->manifest_stream,
|
||||
available);
|
||||
gst_mss_stream_parse_fragment (mssstream->manifest_stream, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return gst_adaptive_demux2_stream_push_buffer (stream, buffer);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GstStreamType
|
||||
gst_stream_type_from_mss_type (GstMssStreamType mtype)
|
||||
{
|
||||
switch (mtype) {
|
||||
case MSS_STREAM_TYPE_AUDIO:
|
||||
return GST_STREAM_TYPE_AUDIO;
|
||||
case MSS_STREAM_TYPE_VIDEO:
|
||||
return GST_STREAM_TYPE_VIDEO;
|
||||
default:
|
||||
return GST_STREAM_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2012 Smart TV Alliance
|
||||
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
|
||||
*
|
||||
* gstmssdemux.h:
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_MSSDEMUX_H__
|
||||
#define __GST_MSSDEMUX_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <gst/base/gstdataqueue.h>
|
||||
#include <gst/gstprotection.h>
|
||||
#include "gstmssmanifest.h"
|
||||
#include "../gstadaptivedemux.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (mssdemux2_debug);
|
||||
#define GST_CAT_DEFAULT mssdemux2_debug
|
||||
|
||||
#define GST_TYPE_MSS_DEMUX2 \
|
||||
(gst_mss_demux2_get_type())
|
||||
#define GST_MSS_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MSS_DEMUX,GstMssDemux))
|
||||
#define GST_MSS_DEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MSS_DEMUX,GstMssDemuxClass))
|
||||
#define GST_IS_MSS_DEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MSS_DEMUX))
|
||||
#define GST_IS_MSS_DEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MSS_DEMUX))
|
||||
|
||||
#define GST_MSS_DEMUX_CAST(obj) ((GstMssDemux *)(obj))
|
||||
|
||||
#define GST_TYPE_MSS_DEMUX_STREAM \
|
||||
(gst_mss_demux_stream_get_type())
|
||||
#define GST_MSS_DEMUX_STREAM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MSS_DEMUX_STREAM,GstMssDemuxStream))
|
||||
#define GST_MSS_DEMUX_STREAM_CAST(obj) ((GstMssDemuxStream *)obj)
|
||||
|
||||
typedef struct _GstMssDemuxStream GstMssDemuxStream;
|
||||
typedef GstAdaptiveDemux2StreamClass GstMssDemuxStreamClass;
|
||||
|
||||
typedef struct _GstMssDemux2 GstMssDemux;
|
||||
typedef struct _GstMssDemux2Class GstMssDemuxClass;
|
||||
|
||||
struct _GstMssDemuxStream {
|
||||
GstAdaptiveDemux2Stream parent;
|
||||
|
||||
GstMssStream *manifest_stream;
|
||||
};
|
||||
|
||||
struct _GstMssDemux2 {
|
||||
GstAdaptiveDemux bin;
|
||||
|
||||
GstMssManifest *manifest;
|
||||
gchar *base_url;
|
||||
};
|
||||
|
||||
struct _GstMssDemux2Class {
|
||||
GstAdaptiveDemuxClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_mss_demux2_get_type (void);
|
||||
GType gst_mss_demux_stream_get_type (void);
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (mssdemux2);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MSSDEMUX_H__ */
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Microsoft Smooth-Streaming fragment parsing library
|
||||
*
|
||||
* gstmssfragmentparser.h
|
||||
*
|
||||
* Copyright (C) 2016 Igalia S.L
|
||||
* Copyright (C) 2016 Metrological
|
||||
* Author: Philippe Normand <philn@igalia.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "gstmssfragmentparser.h"
|
||||
#include <gst/base/gstbytereader.h>
|
||||
#include <string.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (mssdemux2_debug);
|
||||
#define GST_CAT_DEFAULT mssdemux2_debug
|
||||
|
||||
void
|
||||
gst_mss_fragment_parser_init (GstMssFragmentParser * parser)
|
||||
{
|
||||
parser->status = GST_MSS_FRAGMENT_HEADER_PARSER_INIT;
|
||||
}
|
||||
|
||||
void
|
||||
gst_mss_fragment_parser_clear (GstMssFragmentParser * parser)
|
||||
{
|
||||
if (parser->moof)
|
||||
gst_isoff_moof_box_free (parser->moof);
|
||||
parser->moof = NULL;
|
||||
parser->current_fourcc = 0;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mss_fragment_parser_add_buffer (GstMssFragmentParser * parser,
|
||||
GstBuffer * buffer)
|
||||
{
|
||||
GstByteReader reader;
|
||||
GstMapInfo info;
|
||||
guint64 size;
|
||||
guint32 fourcc;
|
||||
guint header_size;
|
||||
gboolean error = FALSE;
|
||||
|
||||
if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_byte_reader_init (&reader, info.data, info.size);
|
||||
GST_TRACE ("Total buffer size: %u", gst_byte_reader_get_size (&reader));
|
||||
|
||||
do {
|
||||
parser->current_fourcc = 0;
|
||||
|
||||
if (!gst_isoff_parse_box_header (&reader, &fourcc, NULL, &header_size,
|
||||
&size)) {
|
||||
break;
|
||||
}
|
||||
|
||||
parser->current_fourcc = fourcc;
|
||||
|
||||
GST_LOG ("box %" GST_FOURCC_FORMAT " size %" G_GUINT64_FORMAT,
|
||||
GST_FOURCC_ARGS (fourcc), size);
|
||||
|
||||
parser->current_fourcc = fourcc;
|
||||
|
||||
if (parser->current_fourcc == GST_ISOFF_FOURCC_MOOF) {
|
||||
GstByteReader sub_reader;
|
||||
|
||||
g_assert (parser->moof == NULL);
|
||||
gst_byte_reader_get_sub_reader (&reader, &sub_reader, size - header_size);
|
||||
parser->moof = gst_isoff_moof_box_parse (&sub_reader);
|
||||
if (parser->moof == NULL) {
|
||||
GST_ERROR ("Failed to parse moof");
|
||||
error = TRUE;
|
||||
}
|
||||
} else if (parser->current_fourcc == GST_ISOFF_FOURCC_MDAT) {
|
||||
goto beach;
|
||||
} else {
|
||||
gst_byte_reader_skip (&reader, size - header_size);
|
||||
}
|
||||
} while (gst_byte_reader_get_remaining (&reader) > 0);
|
||||
|
||||
beach:
|
||||
|
||||
/* Do sanity check */
|
||||
if (parser->current_fourcc != GST_ISOFF_FOURCC_MDAT || !parser->moof ||
|
||||
parser->moof->traf->len == 0)
|
||||
error = TRUE;
|
||||
|
||||
if (!error) {
|
||||
GstTrafBox *traf = &g_array_index (parser->moof->traf, GstTrafBox, 0);
|
||||
if (!traf->tfxd) {
|
||||
GST_ERROR ("no tfxd box");
|
||||
error = TRUE;
|
||||
} else if (!traf->tfrf) {
|
||||
GST_ERROR ("no tfrf box");
|
||||
error = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error)
|
||||
parser->status = GST_MSS_FRAGMENT_HEADER_PARSER_FINISHED;
|
||||
|
||||
GST_LOG ("Fragment parsing successful: %s", error ? "no" : "yes");
|
||||
gst_buffer_unmap (buffer, &info);
|
||||
return !error;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Microsoft Smooth-Streaming fragment parsing library
|
||||
*
|
||||
* gstmssfragmentparser.h
|
||||
*
|
||||
* Copyright (C) 2016 Igalia S.L
|
||||
* Copyright (C) 2016 Metrological
|
||||
* Author: Philippe Normand <philn@igalia.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library (COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_MSS_FRAGMENT_PARSER_H__
|
||||
#define __GST_MSS_FRAGMENT_PARSER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "../gstisoff.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum _GstFragmentHeaderParserStatus
|
||||
{
|
||||
GST_MSS_FRAGMENT_HEADER_PARSER_INIT,
|
||||
GST_MSS_FRAGMENT_HEADER_PARSER_FINISHED
|
||||
} GstFragmentHeaderParserStatus;
|
||||
|
||||
typedef struct _GstMssFragmentParser
|
||||
{
|
||||
GstFragmentHeaderParserStatus status;
|
||||
GstMoofBox *moof;
|
||||
guint32 current_fourcc;
|
||||
} GstMssFragmentParser;
|
||||
|
||||
void gst_mss_fragment_parser_init (GstMssFragmentParser * parser);
|
||||
void gst_mss_fragment_parser_clear (GstMssFragmentParser * parser);
|
||||
gboolean gst_mss_fragment_parser_add_buffer (GstMssFragmentParser * parser, GstBuffer * buf);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MSS_FRAGMENT_PARSER_H__ */
|
1650
subprojects/gst-plugins-good/ext/adaptivedemux2/mss/gstmssmanifest.c
Normal file
1650
subprojects/gst-plugins-good/ext/adaptivedemux2/mss/gstmssmanifest.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,85 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2012 Smart TV Alliance
|
||||
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
|
||||
*
|
||||
* gstmssmanifest.h:
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_MSS_MANIFEST_H__
|
||||
#define __GST_MSS_MANIFEST_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstMssManifest GstMssManifest;
|
||||
typedef struct _GstMssStream GstMssStream;
|
||||
|
||||
typedef enum _GstMssStreamType {
|
||||
MSS_STREAM_TYPE_UNKNOWN = 0,
|
||||
MSS_STREAM_TYPE_VIDEO = 1,
|
||||
MSS_STREAM_TYPE_AUDIO = 2
|
||||
} GstMssStreamType;
|
||||
|
||||
GstMssManifest * gst_mss_manifest_new (GstBuffer * data);
|
||||
void gst_mss_manifest_free (GstMssManifest * manifest);
|
||||
GSList * gst_mss_manifest_get_streams (GstMssManifest * manifest);
|
||||
guint64 gst_mss_manifest_get_timescale (GstMssManifest * manifest);
|
||||
guint64 gst_mss_manifest_get_duration (GstMssManifest * manifest);
|
||||
GstClockTime gst_mss_manifest_get_gst_duration (GstMssManifest * manifest);
|
||||
void gst_mss_manifest_seek (GstMssManifest * manifest, gboolean forward, gint64 time);
|
||||
gboolean gst_mss_manifest_change_bitrate (GstMssManifest *manifest, guint64 bitrate);
|
||||
guint64 gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest);
|
||||
gboolean gst_mss_manifest_is_live (GstMssManifest * manifest);
|
||||
gint64 gst_mss_manifest_get_dvr_window_length (GstMssManifest * manifest);
|
||||
gint gst_mss_manifest_get_look_ahead_fragments_count (GstMssManifest * manifest);
|
||||
void gst_mss_manifest_reload_fragments (GstMssManifest * manifest, GstBuffer * data);
|
||||
GstClockTime gst_mss_manifest_get_min_fragment_duration (GstMssManifest * manifest);
|
||||
const gchar * gst_mss_manifest_get_protection_system_id (GstMssManifest * manifest);
|
||||
const gchar * gst_mss_manifest_get_protection_data (GstMssManifest * manifest);
|
||||
gboolean gst_mss_manifest_get_live_seek_range (GstMssManifest * manifest, gint64 * start, gint64 * stop);
|
||||
|
||||
GstMssStreamType gst_mss_stream_get_type (GstMssStream *stream);
|
||||
GstCaps * gst_mss_stream_get_caps (GstMssStream * stream);
|
||||
gboolean gst_mss_stream_select_bitrate (GstMssStream * stream, guint64 bitrate);
|
||||
guint64 gst_mss_stream_get_current_bitrate (GstMssStream * stream);
|
||||
void gst_mss_stream_set_active (GstMssStream * stream, gboolean active);
|
||||
guint64 gst_mss_stream_get_timescale (GstMssStream * stream);
|
||||
GstFlowReturn gst_mss_stream_get_fragment_url (GstMssStream * stream, gchar ** url);
|
||||
GstClockTime gst_mss_stream_get_fragment_gst_timestamp (GstMssStream * stream);
|
||||
GstClockTime gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream);
|
||||
gboolean gst_mss_stream_has_next_fragment (GstMssStream * stream);
|
||||
GstFlowReturn gst_mss_stream_advance_fragment (GstMssStream * stream);
|
||||
GstFlowReturn gst_mss_stream_regress_fragment (GstMssStream * stream);
|
||||
void gst_mss_stream_seek (GstMssStream * stream, gboolean forward, GstSeekFlags flags, gint64 time, gint64 * final_time);
|
||||
const gchar * gst_mss_stream_get_lang (GstMssStream * stream);
|
||||
|
||||
const gchar * gst_mss_stream_type_name (GstMssStreamType streamtype);
|
||||
|
||||
void gst_mss_manifest_live_adapter_push(GstMssStream * stream, GstBuffer * buffer);
|
||||
gsize gst_mss_manifest_live_adapter_available(GstMssStream * stream);
|
||||
GstBuffer * gst_mss_manifest_live_adapter_take_buffer(GstMssStream * stream, gsize nbytes);
|
||||
void gst_mss_manifest_live_adapter_clear (GstMssStream * stream);
|
||||
gboolean gst_mss_stream_fragment_parsing_needed(GstMssStream * stream);
|
||||
void gst_mss_stream_parse_fragment(GstMssStream * stream, GstBuffer * buffer);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_MSS_MANIFEST_H__ */
|
52
subprojects/gst-plugins-good/ext/adaptivedemux2/plugin.c
Normal file
52
subprojects/gst-plugins-good/ext/adaptivedemux2/plugin.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021-2022 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "dash/gstdashdemux.h"
|
||||
#include "hls/gsthlsdemux.h"
|
||||
#include "mss/gstmssdemux.h"
|
||||
#include "../soup/gstsouploader.h"
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
#ifndef STATIC_SOUP
|
||||
if (!gst_soup_load_library ()) {
|
||||
GST_WARNING ("Failed to load libsoup library");
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret |= GST_ELEMENT_REGISTER (hlsdemux2, plugin);
|
||||
ret |= GST_ELEMENT_REGISTER (dashdemux2, plugin);
|
||||
ret |= GST_ELEMENT_REGISTER (mssdemux2, plugin);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
adaptivedemux2,
|
||||
"Adaptive Streaming 2 plugin", plugin_init, VERSION,
|
||||
GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
|
@ -1,4 +1,5 @@
|
|||
subdir('aalib')
|
||||
subdir('adaptivedemux2')
|
||||
subdir('cairo')
|
||||
subdir('flac')
|
||||
subdir('gdk_pixbuf')
|
||||
|
|
|
@ -30,8 +30,14 @@
|
|||
#endif
|
||||
#endif /* G_OS_WIN32 */
|
||||
|
||||
#ifdef BUILDING_ADAPTIVEDEMUX2
|
||||
GST_DEBUG_CATEGORY (gst_adaptivedemux_soup_debug);
|
||||
#define GST_CAT_DEFAULT gst_adaptivedemux_soup_debug
|
||||
#else
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_soup_debug);
|
||||
#define GST_CAT_DEFAULT gst_soup_debug
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef STATIC_SOUP
|
||||
|
||||
|
@ -106,6 +112,11 @@ typedef struct _GstSoupVTable
|
|||
goffset (*_soup_message_headers_get_content_length) (SoupMessageHeaders * hdrs);
|
||||
const char *(*_soup_message_headers_get_content_type) (SoupMessageHeaders * hdrs,
|
||||
GHashTable ** value);
|
||||
#ifdef BUILDING_ADAPTIVEDEMUX2
|
||||
gboolean (*_soup_message_headers_get_content_range) (SoupMessageHeaders *hdrs, goffset *start,
|
||||
goffset *end, goffset *total_length);
|
||||
void (*_soup_message_headers_set_range) (SoupMessageHeaders *hdrs, goffset start, goffset end);
|
||||
#endif
|
||||
SoupEncoding (*_soup_message_headers_get_encoding) (SoupMessageHeaders * hdrs);
|
||||
const char *(*_soup_message_headers_get_one) (SoupMessageHeaders * hdrs,
|
||||
const char * name);
|
||||
|
@ -146,6 +157,11 @@ gst_soup_load_library (void)
|
|||
|
||||
g_assert (g_module_supported ());
|
||||
|
||||
#ifdef BUILDING_ADAPTIVEDEMUX2
|
||||
GST_DEBUG_CATEGORY_INIT (gst_adaptivedemux_soup_debug, "adaptivedemux2-soup",
|
||||
0, "adaptivedemux2-soup");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RTLD_NOLOAD
|
||||
{
|
||||
gpointer handle = NULL;
|
||||
|
@ -262,6 +278,10 @@ gst_soup_load_library (void)
|
|||
LOAD_SYMBOL (soup_message_headers_foreach);
|
||||
LOAD_SYMBOL (soup_message_headers_get_content_length);
|
||||
LOAD_SYMBOL (soup_message_headers_get_content_type);
|
||||
#ifdef BUILDING_ADAPTIVEDEMUX2
|
||||
LOAD_SYMBOL (soup_message_headers_get_content_range);
|
||||
LOAD_SYMBOL (soup_message_headers_set_range);
|
||||
#endif
|
||||
LOAD_SYMBOL (soup_message_headers_get_encoding);
|
||||
LOAD_SYMBOL (soup_message_headers_get_one);
|
||||
LOAD_SYMBOL (soup_message_headers_remove);
|
||||
|
@ -792,6 +812,34 @@ _soup_message_headers_get_content_type (SoupMessageHeaders * hdrs,
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef BUILDING_ADAPTIVEDEMUX2
|
||||
gboolean
|
||||
_soup_message_headers_get_content_range (SoupMessageHeaders * hdrs,
|
||||
goffset * start, goffset * end, goffset * total_length)
|
||||
{
|
||||
#ifdef STATIC_SOUP
|
||||
return soup_message_headers_get_content_range (hdrs, start, end,
|
||||
total_length);
|
||||
#else
|
||||
g_assert (gst_soup_vtable._soup_message_headers_get_content_range != NULL);
|
||||
return gst_soup_vtable._soup_message_headers_get_content_range (hdrs, start,
|
||||
end, total_length);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
_soup_message_headers_set_range (SoupMessageHeaders * hdrs, goffset start,
|
||||
goffset end)
|
||||
{
|
||||
#ifdef STATIC_SOUP
|
||||
soup_message_headers_set_range (hdrs, start, end);
|
||||
#else
|
||||
g_assert (gst_soup_vtable._soup_message_headers_set_range != NULL);
|
||||
gst_soup_vtable._soup_message_headers_set_range (hdrs, start, end);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
_soup_auth_authenticate (SoupAuth * auth, const char *username,
|
||||
const char *password)
|
||||
|
|
|
@ -96,6 +96,14 @@ void _soup_message_disable_feature (SoupMessage *msg, GType feature_type);
|
|||
const char *_soup_message_headers_get_content_type (SoupMessageHeaders *hdrs,
|
||||
GHashTable **params);
|
||||
|
||||
#ifdef BUILDING_ADAPTIVEDEMUX2
|
||||
gboolean _soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
|
||||
goffset *start, goffset *end,
|
||||
goffset *total_length);
|
||||
|
||||
void _soup_message_headers_set_range (SoupMessageHeaders *hdrs, goffset start, goffset end);
|
||||
#endif
|
||||
|
||||
void _soup_auth_authenticate (SoupAuth *auth, const char *username,
|
||||
const char *password);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ option('wavparse', type : 'feature', value : 'auto')
|
|||
option('y4m', type : 'feature', value : 'auto')
|
||||
|
||||
# Feature options for plugins with external deps
|
||||
option('adaptivedemux2', type : 'feature', value : 'auto', description : '2nd generation adaptive demuxer plugin')
|
||||
option('aalib', type : 'feature', value : 'auto', description : 'aalib text console video sink plugin')
|
||||
option('bz2', type : 'feature', value : 'auto', description : 'libbz2 support in the matroska plugin')
|
||||
option('cairo', type : 'feature', value : 'auto', description : 'Cairo overlay plugin')
|
||||
|
@ -74,6 +75,10 @@ option('vpx', type : 'feature', value : 'auto', description : 'VP8 and VP9 video
|
|||
option('waveform', type : 'feature', value : 'auto', description : 'Windows waveform audio sink plugin')
|
||||
option('wavpack', type : 'feature', value : 'auto', description : 'Wavpack audio codec plugin')
|
||||
|
||||
# HLS plugin options
|
||||
option('hls-crypto', type : 'combo', value : 'auto', choices : ['auto', 'nettle', 'libgcrypt', 'openssl'],
|
||||
description: 'Crypto library to use for HLS plugin')
|
||||
|
||||
# rpicamsrc plugin options
|
||||
option('rpicamsrc', type : 'feature', value : 'auto', description : 'Raspberry Pi camera module plugin')
|
||||
option('rpi-header-dir', type : 'string', value : '/opt/vc/include', description : 'Directory where VideoCore/MMAL headers and bcm_host.h can be found')
|
||||
|
|
6434
subprojects/gst-plugins-good/tests/check/elements/dash_mpd.c
Normal file
6434
subprojects/gst-plugins-good/tests/check/elements/dash_mpd.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,883 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* unit test for hlsdemux
|
||||
*
|
||||
* Copyright (C) <2012> Fluendo S.A <support@fluendo.com>
|
||||
* Authors: Andoni Morales Alastruey <amorales@fluendo.com>
|
||||
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
|
||||
#undef GST_CAT_DEFAULT
|
||||
#include "m3u8.h"
|
||||
#include "m3u8.c"
|
||||
|
||||
GST_DEBUG_CATEGORY (hls_debug);
|
||||
|
||||
static const gchar *INVALID_PLAYLIST = "#EXTM3 UINVALID";
|
||||
|
||||
static const gchar *ON_DEMAND_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-TARGETDURATION:10\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/001.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/002.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/003.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/004.ts\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *DOUBLES_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-TARGETDURATION:10\n\
|
||||
#EXTINF:10.321,Test\n\
|
||||
http://media.example.com/001.ts\n\
|
||||
#EXTINF:9.6789,Test\n\
|
||||
http://media.example.com/002.ts\n\
|
||||
#EXTINF:10.2344,Test\n\
|
||||
http://media.example.com/003.ts\n\
|
||||
#EXTINF:9.92,Test\n\
|
||||
http://media.example.com/004.ts\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *LIVE_PLAYLIST = "#EXTM3U\n\
|
||||
#EXT-X-TARGETDURATION:8\n\
|
||||
#EXT-X-MEDIA-SEQUENCE:2680\n\
|
||||
\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence2680.ts\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence2681.ts\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence2682.ts\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence2683.ts";
|
||||
|
||||
static const gchar *LIVE_ROTATED_PLAYLIST = "#EXTM3U\n\
|
||||
#EXT-X-TARGETDURATION:8\n\
|
||||
#EXT-X-MEDIA-SEQUENCE:3001\n\
|
||||
\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence3001.ts\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence3002.ts\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence3003.ts\n\
|
||||
#EXTINF:8,\n\
|
||||
https://priv.example.com/fileSequence3004.ts";
|
||||
|
||||
static const gchar *VARIANT_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000\n\
|
||||
http://example.com/low.m3u8\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=256000\n\
|
||||
http://example.com/mid.m3u8\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=768000\n\
|
||||
http://example.com/hi.m3u8\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n\
|
||||
http://example.com/audio-only.m3u8";
|
||||
|
||||
static const gchar *VARIANT_PLAYLIST_WITH_URI_MISSING = "#EXTM3U \n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000\n\
|
||||
http://example.com/low.m3u8\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=256000\n\
|
||||
\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=768000\n\
|
||||
http://example.com/hi.m3u8\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n\
|
||||
http://example.com/audio-only.m3u8";
|
||||
|
||||
static const gchar *EMPTY_LINES_VARIANT_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000\n\n\
|
||||
http://example.com/low.m3u8\n\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=256000\n\n\
|
||||
http://example.com/mid.m3u8\n\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=768000\n\n\
|
||||
http://example.com/hi.m3u8\n\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n\n\
|
||||
http://example.com/audio-only.m3u8";
|
||||
|
||||
static const gchar *WINDOWS_EMPTY_LINES_VARIANT_PLAYLIST = "#EXTM3U \r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000\r\n\r\n\
|
||||
http://example.com/low.m3u8\r\n\r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=256000\r\n\r\n\
|
||||
http://example.com/mid.m3u8\r\n\r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=768000\r\n\r\n\
|
||||
http://example.com/hi.m3u8\r\n\r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\r\n\r\n\
|
||||
http://example.com/audio-only.m3u8";
|
||||
|
||||
static const gchar *EMPTY_LINES_PLAYLIST = "#EXTM3U \n\n\
|
||||
#EXT-X-TARGETDURATION:10\n\
|
||||
#EXTINF:10,Testr\n\n\
|
||||
http://media.example.com/001.ts\n\n\
|
||||
#EXTINF:10,Test\n\n\
|
||||
http://media.example.com/002.ts\n\n\
|
||||
#EXTINF:10,Test\n\n\
|
||||
http://media.example.com/003.ts\n\n\
|
||||
#EXTINF:10,Test\n\n\
|
||||
http://media.example.com/004.ts\n\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *WINDOWS_EMPTY_LINES_PLAYLIST = "#EXTM3U \r\n\
|
||||
#EXT-X-TARGETDURATION:10\r\n\r\n\
|
||||
#EXTINF:10,Test\r\n\r\n\
|
||||
http://media.example.com/001.ts\r\n\r\n\
|
||||
#EXTINF:10,Test\r\n\r\n\
|
||||
http://media.example.com/002.ts\r\n\r\n\
|
||||
#EXTINF:10,Test\r\n\r\n\
|
||||
http://media.example.com/003.ts\r\n\r\n\
|
||||
#EXTINF:10,Test\r\n\r\n\
|
||||
http://media.example.com/004.ts\r\n\r\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *BYTE_RANGES_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-TARGETDURATION:40\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000@100\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000@1000\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000@2000\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000@3000\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *BYTE_RANGES_ACC_OFFSET_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-TARGETDURATION:40\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
#EXT-X-BYTERANGE:1000\n\
|
||||
http://media.example.com/all.ts\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *AES_128_ENCRYPTED_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-TARGETDURATION:10\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/mid/video-only-001.ts\n\
|
||||
#EXT-X-KEY:METHOD=NONE\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/mid/video-only-002.ts\n\
|
||||
#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.bin\"\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/mid/video-only-003.ts\n\
|
||||
#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key2.bin\",IV=0x00000000000000000000000000000001\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/mid/video-only-004.ts\n\
|
||||
#EXTINF:10,Test\n\
|
||||
http://media.example.com/mid/video-only-005.ts\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *WINDOWS_LINE_ENDINGS_PLAYLIST = "#EXTM3U \r\n\
|
||||
#EXT-X-TARGETDURATION:10\r\n\
|
||||
#EXTINF:10,Test\r\n\
|
||||
http://media.example.com/001.ts\r\n\
|
||||
#EXTINF:10,Test\r\n\
|
||||
http://media.example.com/002.ts\r\n\
|
||||
#EXTINF:10,Test\r\n\
|
||||
http://media.example.com/003.ts\r\n\
|
||||
#EXTINF:10,Test\r\n\
|
||||
http://media.example.com/004.ts\r\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static const gchar *WINDOWS_LINE_ENDINGS_VARIANT_PLAYLIST = "#EXTM3U \r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000\r\n\
|
||||
http://example.com/low.m3u8\r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=256000\r\n\
|
||||
http://example.com/mid.m3u8\r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=768000\r\n\
|
||||
http://example.com/hi.m3u8\r\n\
|
||||
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\r\n\
|
||||
http://example.com/audio-only.m3u8";
|
||||
|
||||
static const gchar *MAP_TAG_PLAYLIST = "#EXTM3U \n\
|
||||
#EXT-X-VERSION:7\n\
|
||||
#EXT-X-MAP:URI=\"init1.mp4\",BYTERANGE=\"50@50\"\n\
|
||||
#EXTINF:6.00000,\n\
|
||||
#EXT-X-BYTERANGE:100@50\n\
|
||||
main.mp4\n\
|
||||
#EXTINF:6.00000,\n\
|
||||
#EXT-X-BYTERANGE:100@150\n\
|
||||
main.mp4\n\
|
||||
#EXT-X-MAP:URI=\"init2.mp4\"\n\
|
||||
#EXTINF:6.00000,\n\
|
||||
#EXT-X-BYTERANGE:100@300\n\
|
||||
main.mp4\n\
|
||||
#EXT-X-ENDLIST";
|
||||
|
||||
static GstHLSMediaPlaylist *
|
||||
load_m3u8 (const gchar * data)
|
||||
{
|
||||
GstHLSMediaPlaylist *playlist;
|
||||
|
||||
playlist = gst_hls_media_playlist_parse (g_strdup (data),
|
||||
"http://localhost/test.m3u8", NULL);
|
||||
fail_unless (playlist != NULL);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
static GstHLSMasterPlaylist *
|
||||
load_master_playlist (const gchar * data)
|
||||
{
|
||||
GstHLSMasterPlaylist *master;
|
||||
|
||||
master = gst_hls_master_playlist_new_from_data (g_strdup (data),
|
||||
"http://localhost/test.m3u8");
|
||||
fail_unless (master != NULL);
|
||||
|
||||
return master;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_load_main_playlist_invalid)
|
||||
{
|
||||
GstHLSMasterPlaylist *master;
|
||||
|
||||
master =
|
||||
gst_hls_master_playlist_new_from_data (g_strdup (INVALID_PLAYLIST), NULL);
|
||||
fail_unless (master == NULL);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_load_main_playlist_rendition)
|
||||
{
|
||||
GstHLSMediaPlaylist *playlist;
|
||||
|
||||
playlist = load_m3u8 (ON_DEMAND_PLAYLIST);
|
||||
|
||||
assert_equals_int (playlist->segments->len, 4);
|
||||
assert_equals_int (playlist->version, 1);
|
||||
|
||||
gst_hls_media_playlist_unref (playlist);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static void
|
||||
do_test_load_main_playlist_variant (const gchar * playlist)
|
||||
{
|
||||
GstHLSMasterPlaylist *master;
|
||||
GstHLSVariantStream *stream;
|
||||
GList *tmp;
|
||||
|
||||
master = load_master_playlist (playlist);
|
||||
|
||||
assert_equals_int (g_list_length (master->variants), 4);
|
||||
|
||||
/* Audio-Only */
|
||||
tmp = g_list_first (master->variants);
|
||||
stream = tmp->data;
|
||||
assert_equals_int (stream->bandwidth, 65000);
|
||||
assert_equals_int (stream->program_id, 1);
|
||||
assert_equals_string (stream->uri, "http://example.com/audio-only.m3u8");
|
||||
assert_equals_string (stream->codecs, "mp4a.40.5");
|
||||
|
||||
/* Low */
|
||||
tmp = g_list_next (tmp);
|
||||
stream = tmp->data;
|
||||
assert_equals_int (stream->bandwidth, 128000);
|
||||
assert_equals_int (stream->program_id, 1);
|
||||
assert_equals_string (stream->uri, "http://example.com/low.m3u8");
|
||||
|
||||
/* Mid */
|
||||
tmp = g_list_next (tmp);
|
||||
stream = tmp->data;
|
||||
assert_equals_int (stream->bandwidth, 256000);
|
||||
assert_equals_int (stream->program_id, 1);
|
||||
assert_equals_string (stream->uri, "http://example.com/mid.m3u8");
|
||||
|
||||
/* High */
|
||||
tmp = g_list_next (tmp);
|
||||
stream = tmp->data;
|
||||
assert_equals_int (stream->bandwidth, 768000);
|
||||
assert_equals_int (stream->program_id, 1);
|
||||
assert_equals_string (stream->uri, "http://example.com/hi.m3u8");
|
||||
|
||||
/* Check the first playlist is selected */
|
||||
assert_equals_int (master->default_variant != NULL, TRUE);
|
||||
assert_equals_int (master->default_variant->bandwidth, 128000);
|
||||
|
||||
gst_hls_master_playlist_unref (master);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_load_main_playlist_variant)
|
||||
{
|
||||
do_test_load_main_playlist_variant (VARIANT_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_load_main_playlist_variant_with_missing_uri)
|
||||
{
|
||||
GstHLSMasterPlaylist *master;
|
||||
master = load_master_playlist (VARIANT_PLAYLIST_WITH_URI_MISSING);
|
||||
|
||||
assert_equals_int (g_list_length (master->variants), 3);
|
||||
gst_hls_master_playlist_unref (master);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_load_windows_line_endings_variant_playlist)
|
||||
{
|
||||
do_test_load_main_playlist_variant (WINDOWS_LINE_ENDINGS_VARIANT_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_load_main_playlist_with_empty_lines)
|
||||
{
|
||||
do_test_load_main_playlist_variant (EMPTY_LINES_VARIANT_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_load_windows_main_playlist_with_empty_lines)
|
||||
{
|
||||
do_test_load_main_playlist_variant (WINDOWS_EMPTY_LINES_VARIANT_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static void
|
||||
check_on_demand_playlist (const gchar * data)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GstM3U8MediaSegment *file;
|
||||
|
||||
pl = load_m3u8 (data);
|
||||
|
||||
/* Sequence should be 0 as it's an ondemand playlist */
|
||||
assert_equals_int (pl->media_sequence, 0);
|
||||
/* Check that we are not live */
|
||||
assert_equals_int (gst_hls_media_playlist_is_live (pl), FALSE);
|
||||
/* Check number of entries */
|
||||
assert_equals_int (pl->segments->len, 4);
|
||||
/* Check first media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
assert_equals_string (file->uri, "http://media.example.com/001.ts");
|
||||
assert_equals_int (file->sequence, 0);
|
||||
/* Check last media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 3));
|
||||
assert_equals_string (file->uri, "http://media.example.com/004.ts");
|
||||
assert_equals_int (file->sequence, 3);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_on_demand_playlist)
|
||||
{
|
||||
check_on_demand_playlist (ON_DEMAND_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_windows_line_endings_playlist)
|
||||
{
|
||||
check_on_demand_playlist (WINDOWS_LINE_ENDINGS_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_empty_lines_playlist)
|
||||
{
|
||||
check_on_demand_playlist (EMPTY_LINES_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_windows_empty_lines_playlist)
|
||||
{
|
||||
check_on_demand_playlist (WINDOWS_EMPTY_LINES_PLAYLIST);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
/* This test is for live streams in which we pause the stream for more than the
|
||||
* DVR window and we resume playback. The playlist has rotated completely and
|
||||
* there is a jump in the media sequence that must be handled correctly. */
|
||||
GST_START_TEST (test_live_playlist_rotated)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GstM3U8MediaSegment *file, *file2;
|
||||
|
||||
pl = load_m3u8 (LIVE_PLAYLIST);
|
||||
|
||||
/* Check first media segment */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
assert_equals_int (file->sequence, 2680);
|
||||
gst_m3u8_media_segment_ref (file);
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
|
||||
pl = load_m3u8 (LIVE_ROTATED_PLAYLIST);
|
||||
file2 = gst_hls_media_playlist_sync_to_segment (pl, file);
|
||||
fail_unless (file2 != NULL);
|
||||
gst_m3u8_media_segment_unref (file);
|
||||
gst_m3u8_media_segment_unref (file2);
|
||||
|
||||
/* FIXME: Sequence should last - 3. Should it? */
|
||||
/* Check first media segment */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
assert_equals_int (file->sequence, 3001);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_playlist_with_doubles_duration)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GstM3U8MediaSegment *file;
|
||||
gint64 start = -1;
|
||||
gint64 stop = -1;
|
||||
|
||||
pl = load_m3u8 (DOUBLES_PLAYLIST);
|
||||
|
||||
/* Check first media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
assert_equals_float (file->duration / (double) GST_SECOND, 10.321);
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 1));
|
||||
assert_equals_float (file->duration / (double) GST_SECOND, 9.6789);
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 2));
|
||||
assert_equals_float (file->duration / (double) GST_SECOND, 10.2344);
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 3));
|
||||
assert_equals_float (file->duration / (double) GST_SECOND, 9.92);
|
||||
fail_unless (gst_hls_media_playlist_get_seek_range (pl, &start, &stop));
|
||||
assert_equals_int64 (start, 0);
|
||||
assert_equals_float (stop / (double) GST_SECOND,
|
||||
10.321 + 9.6789 + 10.2344 + 9.92);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_playlist_with_encryption)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GstM3U8MediaSegment *file;
|
||||
guint8 iv1[16] = { 0, };
|
||||
guint8 iv2[16] = { 0, };
|
||||
|
||||
iv1[15] = 1;
|
||||
iv2[15] = 2;
|
||||
|
||||
pl = load_m3u8 (AES_128_ENCRYPTED_PLAYLIST);
|
||||
|
||||
assert_equals_int (pl->segments->len, 5);
|
||||
|
||||
/* Check all media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
fail_unless (file->key == NULL);
|
||||
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 1));
|
||||
fail_unless (file->key == NULL);
|
||||
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 2));
|
||||
fail_unless (file->key != NULL);
|
||||
assert_equals_string (file->key, "https://priv.example.com/key.bin");
|
||||
fail_unless (memcmp (&file->iv, iv2, 16) == 0);
|
||||
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 3));
|
||||
fail_unless (file->key != NULL);
|
||||
assert_equals_string (file->key, "https://priv.example.com/key2.bin");
|
||||
fail_unless (memcmp (&file->iv, iv1, 16) == 0);
|
||||
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 4));
|
||||
fail_unless (file->key != NULL);
|
||||
assert_equals_string (file->key, "https://priv.example.com/key2.bin");
|
||||
fail_unless (memcmp (&file->iv, iv1, 16) == 0);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_parse_invalid_playlist)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
|
||||
pl = gst_hls_media_playlist_parse (g_strdup ("#INVALID"),
|
||||
"http://localhost/test.m3u8", NULL);
|
||||
fail_if (pl != NULL);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_sync_playlist_to_segment)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
gchar *live_pl;
|
||||
GstM3U8MediaSegment *file, *file2;
|
||||
|
||||
/* Test updates in live playlists */
|
||||
pl = load_m3u8 (LIVE_PLAYLIST);
|
||||
assert_equals_int (pl->segments->len, 4);
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
gst_m3u8_media_segment_ref (file);
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
|
||||
/* Add a new entry to the playlist and check the update */
|
||||
live_pl = g_strdup_printf ("%s\n%s\n%s", LIVE_PLAYLIST, "#EXTINF:8",
|
||||
"https://priv.example.com/fileSequence2684.ts");
|
||||
pl = load_m3u8 (live_pl);
|
||||
fail_unless (pl != NULL);
|
||||
g_free (live_pl);
|
||||
file2 = gst_hls_media_playlist_sync_to_segment (pl, file);
|
||||
fail_unless (file2 != NULL);
|
||||
gst_m3u8_media_segment_unref (file);
|
||||
assert_equals_int (pl->segments->len, 5);
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
|
||||
/* Test sliding window */
|
||||
pl = load_m3u8 (LIVE_PLAYLIST);
|
||||
file = gst_hls_media_playlist_sync_to_segment (pl, file2);
|
||||
fail_unless (file != NULL);
|
||||
gst_m3u8_media_segment_unref (file);
|
||||
gst_m3u8_media_segment_unref (file2);
|
||||
assert_equals_int (pl->segments->len, 4);
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_playlist_media_files)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GstM3U8MediaSegment *file;
|
||||
|
||||
pl = load_m3u8 (ON_DEMAND_PLAYLIST);
|
||||
|
||||
/* Check number of entries */
|
||||
assert_equals_int (pl->segments->len, 4);
|
||||
/* Check first media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
assert_equals_string (file->uri, "http://media.example.com/001.ts");
|
||||
assert_equals_int (file->sequence, 0);
|
||||
assert_equals_float (file->duration, 10 * (double) GST_SECOND);
|
||||
assert_equals_int (file->offset, 0);
|
||||
assert_equals_int (file->size, -1);
|
||||
assert_equals_string (file->title, "Test");
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_playlist_byte_range_media_files)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GstM3U8MediaSegment *file;
|
||||
|
||||
pl = load_m3u8 (BYTE_RANGES_PLAYLIST);
|
||||
|
||||
/* Check number of entries */
|
||||
assert_equals_int (pl->segments->len, 4);
|
||||
/* Check first media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
assert_equals_string (file->uri, "http://media.example.com/all.ts");
|
||||
assert_equals_int (file->sequence, 0);
|
||||
assert_equals_float (file->duration, 10 * (double) GST_SECOND);
|
||||
assert_equals_int (file->offset, 100);
|
||||
assert_equals_int (file->size, 1000);
|
||||
/* Check last media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 3));
|
||||
assert_equals_string (file->uri, "http://media.example.com/all.ts");
|
||||
assert_equals_int (file->sequence, 3);
|
||||
assert_equals_float (file->duration, 10 * (double) GST_SECOND);
|
||||
assert_equals_int (file->offset, 3000);
|
||||
assert_equals_int (file->size, 1000);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
pl = load_m3u8 (BYTE_RANGES_ACC_OFFSET_PLAYLIST);
|
||||
|
||||
/* Check number of entries */
|
||||
assert_equals_int (pl->segments->len, 4);
|
||||
/* Check first media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 0));
|
||||
assert_equals_string (file->uri, "http://media.example.com/all.ts");
|
||||
assert_equals_int (file->sequence, 0);
|
||||
assert_equals_float (file->duration, 10 * (double) GST_SECOND);
|
||||
assert_equals_int (file->offset, 0);
|
||||
assert_equals_int (file->size, 1000);
|
||||
/* Check last media segments */
|
||||
file = GST_M3U8_MEDIA_SEGMENT (g_ptr_array_index (pl->segments, 3));
|
||||
assert_equals_string (file->uri, "http://media.example.com/all.ts");
|
||||
assert_equals_int (file->sequence, 3);
|
||||
assert_equals_float (file->duration, 10 * (double) GST_SECOND);
|
||||
assert_equals_int (file->offset, 3000);
|
||||
assert_equals_int (file->size, 1000);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_advance_fragment)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GstM3U8MediaSegment *mf;
|
||||
|
||||
pl = load_m3u8 (BYTE_RANGES_PLAYLIST);
|
||||
|
||||
/* Check the next fragment */
|
||||
mf = gst_hls_media_playlist_get_starting_segment (pl);
|
||||
fail_unless (mf != NULL);
|
||||
assert_equals_int (mf->discont, FALSE);
|
||||
assert_equals_string (mf->uri, "http://media.example.com/all.ts");
|
||||
assert_equals_uint64 (mf->stream_time, 0);
|
||||
assert_equals_uint64 (mf->duration, 10 * GST_SECOND);
|
||||
assert_equals_uint64 (mf->offset, 100);
|
||||
assert_equals_uint64 (mf->offset + mf->size, 1100);
|
||||
gst_m3u8_media_segment_unref (mf);
|
||||
|
||||
/* Check next media segments */
|
||||
mf = gst_hls_media_playlist_advance_fragment (pl, mf, TRUE);
|
||||
fail_unless (mf != NULL);
|
||||
assert_equals_int (mf->discont, FALSE);
|
||||
assert_equals_string (mf->uri, "http://media.example.com/all.ts");
|
||||
assert_equals_uint64 (mf->stream_time, 10 * GST_SECOND);
|
||||
assert_equals_uint64 (mf->duration, 10 * GST_SECOND);
|
||||
assert_equals_uint64 (mf->offset, 1000);
|
||||
assert_equals_uint64 (mf->offset + mf->size, 2000);
|
||||
gst_m3u8_media_segment_unref (mf);
|
||||
|
||||
/* Check next media segments */
|
||||
mf = gst_hls_media_playlist_advance_fragment (pl, mf, TRUE);
|
||||
assert_equals_int (mf->discont, FALSE);
|
||||
assert_equals_string (mf->uri, "http://media.example.com/all.ts");
|
||||
assert_equals_uint64 (mf->stream_time, 20 * GST_SECOND);
|
||||
assert_equals_uint64 (mf->duration, 10 * GST_SECOND);
|
||||
assert_equals_uint64 (mf->offset, 2000);
|
||||
assert_equals_uint64 (mf->offset + mf->size, 3000);
|
||||
gst_m3u8_media_segment_unref (mf);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_get_duration)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
|
||||
/* Test duration for on-demand playlists */
|
||||
pl = load_m3u8 (ON_DEMAND_PLAYLIST);
|
||||
assert_equals_uint64 (gst_hls_media_playlist_get_duration (pl),
|
||||
40 * GST_SECOND);
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
|
||||
/* Test duration for live playlists */
|
||||
pl = load_m3u8 (LIVE_PLAYLIST);
|
||||
assert_equals_uint64 (gst_hls_media_playlist_get_duration (pl),
|
||||
GST_CLOCK_TIME_NONE);
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_get_target_duration)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
|
||||
pl = load_m3u8 (ON_DEMAND_PLAYLIST);
|
||||
assert_equals_uint64 (pl->targetduration, 10 * GST_SECOND);
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_get_stream_for_bitrate)
|
||||
{
|
||||
GstHLSMasterPlaylist *master;
|
||||
GstHLSVariantStream *stream;
|
||||
|
||||
master = load_master_playlist (VARIANT_PLAYLIST);
|
||||
stream = gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 0, 0);
|
||||
|
||||
assert_equals_int (stream->bandwidth, 65000);
|
||||
|
||||
stream =
|
||||
gst_hls_master_playlist_get_variant_for_bitrate (master, NULL,
|
||||
G_MAXINT32, 0);
|
||||
assert_equals_int (stream->bandwidth, 768000);
|
||||
stream =
|
||||
gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 300000, 0);
|
||||
assert_equals_int (stream->bandwidth, 256000);
|
||||
stream =
|
||||
gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 500000, 0);
|
||||
assert_equals_int (stream->bandwidth, 256000);
|
||||
stream =
|
||||
gst_hls_master_playlist_get_variant_for_bitrate (master, NULL, 255000, 0);
|
||||
assert_equals_int (stream->bandwidth, 128000);
|
||||
|
||||
gst_hls_master_playlist_unref (master);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_url_with_slash_query_param)
|
||||
{
|
||||
static const gchar *MASTER_PLAYLIST = "#EXTM3U \n"
|
||||
"#EXT-X-VERSION:4\n"
|
||||
"#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1251135, CODECS=\"avc1.42001f, mp4a.40.2\", RESOLUTION=640x352\n"
|
||||
"1251/media.m3u8?acl=/*1054559_h264_1500k.mp4\n";
|
||||
GstHLSMasterPlaylist *master;
|
||||
GstHLSVariantStream *stream;
|
||||
|
||||
master = load_master_playlist (MASTER_PLAYLIST);
|
||||
|
||||
assert_equals_int (g_list_length (master->variants), 1);
|
||||
stream = g_list_nth_data (master->variants, 0);
|
||||
assert_equals_string (stream->uri,
|
||||
"http://localhost/1251/media.m3u8?acl=/*1054559_h264_1500k.mp4");
|
||||
|
||||
gst_hls_master_playlist_unref (master);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_stream_inf_tag)
|
||||
{
|
||||
static const gchar *MASTER_PLAYLIST = "#EXTM3U \n"
|
||||
"#EXT-X-VERSION:4\n"
|
||||
"#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1251135, CODECS=\"avc1.42001f, mp4a.40.2\", RESOLUTION=640x352\n"
|
||||
"media.m3u8\n";
|
||||
GstHLSMasterPlaylist *master;
|
||||
GstHLSVariantStream *stream;
|
||||
|
||||
master = load_master_playlist (MASTER_PLAYLIST);
|
||||
|
||||
assert_equals_int (g_list_length (master->variants), 1);
|
||||
stream = g_list_nth_data (master->variants, 0);
|
||||
|
||||
assert_equals_int64 (stream->program_id, 1);
|
||||
assert_equals_int64 (stream->width, 640);
|
||||
assert_equals_int64 (stream->height, 352);
|
||||
assert_equals_int64 (stream->bandwidth, 1251135);
|
||||
assert_equals_string (stream->codecs, "avc1.42001f, mp4a.40.2");
|
||||
gst_hls_master_playlist_unref (master);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_map_tag)
|
||||
{
|
||||
GstHLSMediaPlaylist *pl;
|
||||
GPtrArray *segments;
|
||||
GstM3U8MediaSegment *seg1, *seg2, *seg3;
|
||||
GstM3U8InitFile *init1, *init2;
|
||||
|
||||
/* Test EXT-X-MAP tag
|
||||
* This M3U8 has two EXT-X-MAP tag.
|
||||
* the first one is applied to the 1st and 2nd segments, and the other is
|
||||
* applied only to the 3rd segment
|
||||
*/
|
||||
|
||||
pl = load_m3u8 (MAP_TAG_PLAYLIST);
|
||||
segments = pl->segments;
|
||||
|
||||
assert_equals_int (segments->len, 3);
|
||||
|
||||
for (gsize i = 0; i < segments->len; i++) {
|
||||
GstM3U8MediaSegment *file = g_ptr_array_index (segments, i);
|
||||
|
||||
GstM3U8InitFile *init_file = file->init_file;
|
||||
fail_unless (init_file != NULL);
|
||||
fail_unless (init_file->uri != NULL);
|
||||
}
|
||||
|
||||
seg1 = g_ptr_array_index (segments, 0);
|
||||
seg2 = g_ptr_array_index (segments, 1);
|
||||
seg3 = g_ptr_array_index (segments, 2);
|
||||
|
||||
/* Segment 1 and 2 share the identical init segment */
|
||||
fail_unless (seg1->init_file == seg2->init_file);
|
||||
assert_equals_int (seg1->init_file->ref_count, 2);
|
||||
|
||||
fail_unless (seg2->init_file != seg3->init_file);
|
||||
assert_equals_int (seg3->init_file->ref_count, 1);
|
||||
|
||||
init1 = seg1->init_file;
|
||||
init2 = seg3->init_file;
|
||||
|
||||
fail_unless (g_strcmp0 (init1->uri, init2->uri));
|
||||
assert_equals_int (init1->offset, 50);
|
||||
assert_equals_int (init1->size, 50);
|
||||
|
||||
assert_equals_int (init2->offset, 0);
|
||||
assert_equals_int (init2->size, -1);
|
||||
|
||||
gst_hls_media_playlist_unref (pl);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
hlsdemux_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("hlsdemux_m3u8");
|
||||
TCase *tc_m3u8 = tcase_create ("m3u8client");
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (hls_debug, "hlsdemux_m3u", 0, "hlsdemux m3u test");
|
||||
|
||||
suite_add_tcase (s, tc_m3u8);
|
||||
tcase_add_test (tc_m3u8, test_load_main_playlist_invalid);
|
||||
tcase_add_test (tc_m3u8, test_load_main_playlist_rendition);
|
||||
tcase_add_test (tc_m3u8, test_load_main_playlist_variant);
|
||||
tcase_add_test (tc_m3u8, test_load_main_playlist_variant_with_missing_uri);
|
||||
tcase_add_test (tc_m3u8, test_load_windows_line_endings_variant_playlist);
|
||||
tcase_add_test (tc_m3u8, test_load_main_playlist_with_empty_lines);
|
||||
tcase_add_test (tc_m3u8, test_load_windows_main_playlist_with_empty_lines);
|
||||
tcase_add_test (tc_m3u8, test_on_demand_playlist);
|
||||
tcase_add_test (tc_m3u8, test_windows_line_endings_playlist);
|
||||
tcase_add_test (tc_m3u8, test_windows_empty_lines_playlist);
|
||||
tcase_add_test (tc_m3u8, test_empty_lines_playlist);
|
||||
tcase_add_test (tc_m3u8, test_live_playlist_rotated);
|
||||
tcase_add_test (tc_m3u8, test_playlist_with_doubles_duration);
|
||||
tcase_add_test (tc_m3u8, test_playlist_with_encryption);
|
||||
tcase_add_test (tc_m3u8, test_parse_invalid_playlist);
|
||||
tcase_add_test (tc_m3u8, test_sync_playlist_to_segment);
|
||||
tcase_add_test (tc_m3u8, test_playlist_media_files);
|
||||
tcase_add_test (tc_m3u8, test_playlist_byte_range_media_files);
|
||||
tcase_add_test (tc_m3u8, test_advance_fragment);
|
||||
tcase_add_test (tc_m3u8, test_get_duration);
|
||||
tcase_add_test (tc_m3u8, test_get_target_duration);
|
||||
tcase_add_test (tc_m3u8, test_get_stream_for_bitrate);
|
||||
tcase_add_test (tc_m3u8, test_url_with_slash_query_param);
|
||||
tcase_add_test (tc_m3u8, test_stream_inf_tag);
|
||||
tcase_add_test (tc_m3u8, test_map_tag);
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (hlsdemux);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue