|
|
|
@ -114,6 +114,7 @@ gst_base_ts_mux_pad_reset (GstBaseTsMuxPad * pad)
|
|
|
|
|
pad->free_func (pad->prepare_data);
|
|
|
|
|
pad->prepare_data = NULL;
|
|
|
|
|
pad->prepare_func = NULL;
|
|
|
|
|
pad->prepared_size_func = NULL;
|
|
|
|
|
pad->free_func = NULL;
|
|
|
|
|
|
|
|
|
|
if (pad->codec_data)
|
|
|
|
@ -254,19 +255,25 @@ enum
|
|
|
|
|
PROP_SI_INTERVAL,
|
|
|
|
|
PROP_BITRATE,
|
|
|
|
|
PROP_PCR_INTERVAL,
|
|
|
|
|
PROP_AUDIO_PES_TARGET_TIME,
|
|
|
|
|
PROP_AUDIO_PES_TARGET_BYTES,
|
|
|
|
|
PROP_SCTE_35_PID,
|
|
|
|
|
PROP_SCTE_35_NULL_INTERVAL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define DEFAULT_SCTE_35_PID 0
|
|
|
|
|
|
|
|
|
|
#define BASETSMUX_DEFAULT_ALIGNMENT -1
|
|
|
|
|
|
|
|
|
|
#define CLOCK_BASE 9LL
|
|
|
|
|
#define CLOCK_FREQ (CLOCK_BASE * 10000) /* 90 kHz PTS clock */
|
|
|
|
|
#define CLOCK_FREQ_SCR (CLOCK_FREQ * 300) /* 27 MHz SCR clock */
|
|
|
|
|
#define TS_MUX_CLOCK_BASE (TSMUX_CLOCK_FREQ * 10 * 360)
|
|
|
|
|
|
|
|
|
|
#define BASETSMUX_DEFAULT_ALIGNMENT -1
|
|
|
|
|
#define BASETSMUX_DEFAULT_AUDIO_PES_TARGET_TIME (CLOCK_FREQ / 1000 * 350) /* 350ms */
|
|
|
|
|
|
|
|
|
|
/* Aim for a PES header about every 16 TS packets, leaving space for a (max) 22-byte PES header */
|
|
|
|
|
#define BASETSMUX_DEFAULT_AUDIO_PES_TARGET_BYTES (16 * TSMUX_PAYLOAD_LENGTH - 22)
|
|
|
|
|
|
|
|
|
|
#define GSTTIME_TO_MPEGTIME(time) \
|
|
|
|
|
(((time) > 0 ? (gint64) 1 : (gint64) -1) * \
|
|
|
|
|
(gint64) gst_util_uint64_scale (ABS(time), CLOCK_BASE, GST_MSECOND/10))
|
|
|
|
@ -374,7 +381,6 @@ gst_base_ts_mux_reset (GstBaseTsMux * mux, gboolean alloc)
|
|
|
|
|
GList *l;
|
|
|
|
|
|
|
|
|
|
mux->first = TRUE;
|
|
|
|
|
mux->last_flow_ret = GST_FLOW_OK;
|
|
|
|
|
mux->last_ts = GST_CLOCK_TIME_NONE;
|
|
|
|
|
mux->is_delta = TRUE;
|
|
|
|
|
mux->is_header = FALSE;
|
|
|
|
@ -473,6 +479,7 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
|
|
|
|
|
|
|
|
|
|
g_clear_pointer (&ts_pad->codec_data, gst_buffer_unref);
|
|
|
|
|
ts_pad->prepare_func = NULL;
|
|
|
|
|
ts_pad->prepared_size_func = NULL;
|
|
|
|
|
|
|
|
|
|
stream_format = gst_structure_get_string (s, "stream-format");
|
|
|
|
|
|
|
|
|
@ -517,6 +524,7 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
|
|
|
|
|
ts_pad->codec_data =
|
|
|
|
|
gst_base_ts_mux_aac_mpeg2_make_codec_data (mux, caps);
|
|
|
|
|
ts_pad->prepare_func = gst_base_ts_mux_prepare_aac_mpeg2;
|
|
|
|
|
ts_pad->prepared_size_func = gst_base_ts_mux_prepared_size_aac;
|
|
|
|
|
if (ts_pad->codec_data == NULL) {
|
|
|
|
|
GST_ERROR_OBJECT (mux, "Invalid or incomplete caps for MPEG-2 AAC");
|
|
|
|
|
goto not_negotiated;
|
|
|
|
@ -536,6 +544,7 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
|
|
|
|
|
gst_buffer_get_size (codec_data));
|
|
|
|
|
ts_pad->codec_data = gst_buffer_ref (codec_data);
|
|
|
|
|
ts_pad->prepare_func = gst_base_ts_mux_prepare_aac_mpeg4;
|
|
|
|
|
ts_pad->prepared_size_func = gst_base_ts_mux_prepared_size_aac;
|
|
|
|
|
} else {
|
|
|
|
|
ts_pad->codec_data = NULL;
|
|
|
|
|
GST_ERROR_OBJECT (mux, "Need codec_data for raw MPEG-4 AAC");
|
|
|
|
@ -580,6 +589,7 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
|
|
|
|
|
st = TSMUX_ST_PS_TELETEXT;
|
|
|
|
|
/* needs a particularly sized layout */
|
|
|
|
|
ts_pad->prepare_func = gst_base_ts_mux_prepare_teletext;
|
|
|
|
|
ts_pad->prepared_size_func = gst_base_ts_mux_prepared_size_teletext;
|
|
|
|
|
} else if (strcmp (mt, "audio/x-opus") == 0) {
|
|
|
|
|
guint8 channels, mapping_family, stream_count, coupled_count;
|
|
|
|
|
guint8 channel_mapping[256];
|
|
|
|
@ -672,6 +682,7 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
|
|
|
|
|
|
|
|
|
|
st = TSMUX_ST_PS_OPUS;
|
|
|
|
|
ts_pad->prepare_func = gst_base_ts_mux_prepare_opus;
|
|
|
|
|
ts_pad->prepared_size_func = gst_base_ts_mux_prepared_size_opus;
|
|
|
|
|
} else if (strcmp (mt, "meta/x-klv") == 0) {
|
|
|
|
|
st = TSMUX_ST_PS_KLV;
|
|
|
|
|
} else if (strcmp (mt, "image/x-jpc") == 0) {
|
|
|
|
@ -767,6 +778,7 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
|
|
|
|
|
}
|
|
|
|
|
st = TSMUX_ST_VIDEO_JP2K;
|
|
|
|
|
ts_pad->prepare_func = gst_base_ts_mux_prepare_jpeg2000;
|
|
|
|
|
ts_pad->prepared_size_func = gst_base_ts_mux_prepared_size_jpeg2000;
|
|
|
|
|
ts_pad->prepare_data = private_data;
|
|
|
|
|
ts_pad->free_func = gst_base_ts_mux_free_jpeg2000;
|
|
|
|
|
} else {
|
|
|
|
@ -796,6 +808,13 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
|
|
|
|
|
ts_pad->stream =
|
|
|
|
|
tsmux_create_stream (mux->tsmux, st, stream_number, ts_pad->pid,
|
|
|
|
|
ts_pad->language, ts_pad->bitrate, ts_pad->max_bitrate);
|
|
|
|
|
|
|
|
|
|
/* if this is an audio stream, report the PES target time in the latency */
|
|
|
|
|
if (ts_pad->stream->is_audio) {
|
|
|
|
|
gst_aggregator_set_latency (GST_AGGREGATOR (mux),
|
|
|
|
|
mux->audio_pes_target_time, GST_CLOCK_TIME_NONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ts_pad->stream == NULL)
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
@ -1314,75 +1333,25 @@ alloc_packet_cb (GstBuffer ** buf, void *user_data)
|
|
|
|
|
klass->allocate_packet (mux, buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sends the buffer to tsmux for output.
|
|
|
|
|
* Called with mux->lock held. Releases the lock before exit */
|
|
|
|
|
static GstFlowReturn
|
|
|
|
|
gst_base_ts_mux_aggregate_buffer (GstBaseTsMux * mux,
|
|
|
|
|
GstAggregatorPad * agg_pad, GstBuffer * buf)
|
|
|
|
|
gst_base_ts_mux_output_buffer_locked (GstBaseTsMux * mux,
|
|
|
|
|
GstBaseTsMuxPad * best, GstBuffer * buf, gint64 bufdts)
|
|
|
|
|
{
|
|
|
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
|
GstBaseTsMuxPad *best = GST_BASE_TS_MUX_PAD (agg_pad);
|
|
|
|
|
TsMuxProgram *prog;
|
|
|
|
|
gint64 pts = GST_CLOCK_STIME_NONE;
|
|
|
|
|
TsMuxProgram *prog = best->prog;
|
|
|
|
|
gint64 dts = GST_CLOCK_STIME_NONE;
|
|
|
|
|
gint64 pts = GST_CLOCK_STIME_NONE;
|
|
|
|
|
gboolean delta = TRUE, header = FALSE;
|
|
|
|
|
StreamData *stream_data;
|
|
|
|
|
GstMpegtsSection *scte_section = NULL;
|
|
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (mux, "Pads collected");
|
|
|
|
|
|
|
|
|
|
if (buf && gst_buffer_get_size (buf) == 0
|
|
|
|
|
&& GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)) {
|
|
|
|
|
gst_buffer_unref (buf);
|
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_mutex_lock (&mux->lock);
|
|
|
|
|
if (G_UNLIKELY (mux->first)) {
|
|
|
|
|
ret = gst_base_ts_mux_create_streams (mux);
|
|
|
|
|
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
|
|
|
|
|
if (buf)
|
|
|
|
|
gst_buffer_unref (buf);
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mux->first = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prog = best->prog;
|
|
|
|
|
if (prog == NULL) {
|
|
|
|
|
GList *cur;
|
|
|
|
|
|
|
|
|
|
gst_base_ts_mux_create_pad_stream (mux, GST_PAD (best));
|
|
|
|
|
tsmux_resend_pat (mux->tsmux);
|
|
|
|
|
tsmux_resend_si (mux->tsmux);
|
|
|
|
|
prog = best->prog;
|
|
|
|
|
g_assert_nonnull (prog);
|
|
|
|
|
|
|
|
|
|
/* output PMT for each program */
|
|
|
|
|
for (cur = mux->tsmux->programs; cur; cur = cur->next) {
|
|
|
|
|
TsMuxProgram *program = (TsMuxProgram *) cur->data;
|
|
|
|
|
|
|
|
|
|
tsmux_resend_pmt (program);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_assert (buf != NULL);
|
|
|
|
|
|
|
|
|
|
if (best->prepare_func) {
|
|
|
|
|
GstBuffer *tmp;
|
|
|
|
|
|
|
|
|
|
tmp = best->prepare_func (buf, best, mux);
|
|
|
|
|
g_assert (tmp);
|
|
|
|
|
gst_buffer_unref (buf);
|
|
|
|
|
buf = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mux->force_key_unit_event != NULL && best->stream->is_video_stream) {
|
|
|
|
|
GstEvent *event;
|
|
|
|
|
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
event = check_pending_key_unit_event (mux->force_key_unit_event,
|
|
|
|
|
&agg_pad->segment, GST_BUFFER_PTS (buf),
|
|
|
|
|
&GST_AGGREGATOR_PAD_CAST (best)->segment, GST_BUFFER_PTS (buf),
|
|
|
|
|
GST_BUFFER_FLAGS (buf), mux->pending_key_unit_ts);
|
|
|
|
|
if (event) {
|
|
|
|
|
GstClockTime running_time;
|
|
|
|
@ -1446,10 +1415,10 @@ gst_base_ts_mux_aggregate_buffer (GstBaseTsMux * mux,
|
|
|
|
|
GST_BUFFER_FLAG_DELTA_UNIT) ? " (keyframe)" : "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (GST_CLOCK_STIME_IS_VALID (best->dts)) {
|
|
|
|
|
dts = GSTTIME_TO_MPEGTIME (best->dts);
|
|
|
|
|
if (GST_CLOCK_STIME_IS_VALID (bufdts)) {
|
|
|
|
|
dts = GSTTIME_TO_MPEGTIME (bufdts);
|
|
|
|
|
GST_DEBUG_OBJECT (mux, "Buffer has DTS %" GST_STIME_FORMAT " dts %"
|
|
|
|
|
G_GINT64_FORMAT, GST_STIME_ARGS (best->dts), dts);
|
|
|
|
|
G_GINT64_FORMAT, GST_STIME_ARGS (bufdts), dts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* should not have a DTS without PTS */
|
|
|
|
@ -1506,10 +1475,86 @@ gst_base_ts_mux_aggregate_buffer (GstBaseTsMux * mux,
|
|
|
|
|
/* ERRORS */
|
|
|
|
|
write_fail:
|
|
|
|
|
{
|
|
|
|
|
return mux->last_flow_ret;
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
|
gst_base_ts_mux_prepare_and_accumulate_buffer (GstBaseTsMux * mux,
|
|
|
|
|
GstAggregatorPad * agg_pad, GstBuffer * buf)
|
|
|
|
|
{
|
|
|
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
|
GstBaseTsMuxPad *best = GST_BASE_TS_MUX_PAD (agg_pad);
|
|
|
|
|
TsMuxProgram *prog;
|
|
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (mux, "Pads collected");
|
|
|
|
|
|
|
|
|
|
if (buf && gst_buffer_get_size (buf) == 0
|
|
|
|
|
&& GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)) {
|
|
|
|
|
gst_buffer_unref (buf);
|
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_mutex_lock (&mux->lock);
|
|
|
|
|
if (G_UNLIKELY (mux->first)) {
|
|
|
|
|
ret = gst_base_ts_mux_create_streams (mux);
|
|
|
|
|
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
|
|
|
|
|
if (buf)
|
|
|
|
|
gst_buffer_unref (buf);
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mux->first = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prog = best->prog;
|
|
|
|
|
if (prog == NULL) {
|
|
|
|
|
GList *cur;
|
|
|
|
|
|
|
|
|
|
gst_base_ts_mux_create_pad_stream (mux, GST_PAD (best));
|
|
|
|
|
tsmux_resend_pat (mux->tsmux);
|
|
|
|
|
tsmux_resend_si (mux->tsmux);
|
|
|
|
|
prog = best->prog;
|
|
|
|
|
g_assert_nonnull (prog);
|
|
|
|
|
|
|
|
|
|
/* output PMT for each program */
|
|
|
|
|
for (cur = mux->tsmux->programs; cur; cur = cur->next) {
|
|
|
|
|
TsMuxProgram *program = (TsMuxProgram *) cur->data;
|
|
|
|
|
|
|
|
|
|
tsmux_resend_pmt (program);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_assert (buf != NULL);
|
|
|
|
|
|
|
|
|
|
if (best->prepare_func) {
|
|
|
|
|
GstBuffer *tmp;
|
|
|
|
|
|
|
|
|
|
tmp = best->prepare_func (buf, best, mux);
|
|
|
|
|
g_assert (tmp);
|
|
|
|
|
gst_buffer_unref (buf);
|
|
|
|
|
buf = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (best->stream->is_audio) {
|
|
|
|
|
if (gst_buffer_get_size (buf) < mux->audio_pes_target_bytes) {
|
|
|
|
|
/* Start accumulating audio buffers on this pad */
|
|
|
|
|
GST_DEBUG_OBJECT (best, "Starting to accumulate audio PES on pad");
|
|
|
|
|
|
|
|
|
|
mux->current_pad = gst_object_ref (best);
|
|
|
|
|
mux->current_buffer = buf;
|
|
|
|
|
mux->current_buffer_dts = best->dts;
|
|
|
|
|
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gst_base_ts_mux_output_buffer_locked (mux, best, buf, best->dts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* GstElement implementation */
|
|
|
|
|
static gboolean
|
|
|
|
|
gst_base_ts_mux_has_pad_with_pid (GstBaseTsMux * mux, guint16 pid)
|
|
|
|
@ -2414,6 +2459,39 @@ beach:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
can_accumulate_buffer (GstBaseTsMux * mux,
|
|
|
|
|
GstBaseTsMuxPad * pad, GstBuffer * new_buf, GstBuffer * current_buf)
|
|
|
|
|
{
|
|
|
|
|
gsize new_buf_size;
|
|
|
|
|
|
|
|
|
|
/* If this buffer would overflow the target time, don't accumulate it */
|
|
|
|
|
if (GST_BUFFER_DTS_IS_VALID (current_buf) && GST_BUFFER_DTS_IS_VALID (new_buf)
|
|
|
|
|
&& GST_CLOCK_DIFF (GST_BUFFER_DTS (current_buf),
|
|
|
|
|
GST_BUFFER_DTS (new_buf)) > mux->audio_pes_target_time) {
|
|
|
|
|
GST_LOG_OBJECT (pad,
|
|
|
|
|
"Not accumulating %" GST_PTR_FORMAT " into pending %" GST_PTR_FORMAT
|
|
|
|
|
" - time overflow", new_buf, current_buf);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this buffer would overflow the target size, don't accumulate it */
|
|
|
|
|
if (pad->prepared_size_func != NULL)
|
|
|
|
|
new_buf_size = pad->prepared_size_func (pad, new_buf);
|
|
|
|
|
else
|
|
|
|
|
new_buf_size = gst_buffer_get_size (new_buf);
|
|
|
|
|
|
|
|
|
|
if (gst_buffer_get_size (current_buf) + new_buf_size >
|
|
|
|
|
mux->audio_pes_target_bytes) {
|
|
|
|
|
GST_LOG_OBJECT (pad,
|
|
|
|
|
"Not accumulating %" GST_PTR_FORMAT " into pending %" GST_PTR_FORMAT
|
|
|
|
|
" - bytes overflow", new_buf, current_buf);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GstBaseTsMuxPad *
|
|
|
|
|
gst_base_ts_mux_find_best_pad (GstAggregator * aggregator)
|
|
|
|
|
{
|
|
|
|
@ -2478,13 +2556,35 @@ gst_base_ts_mux_are_all_pads_eos (GstBaseTsMux * mux)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
|
finish_current_pes (GstBaseTsMux * mux)
|
|
|
|
|
{
|
|
|
|
|
GstFlowReturn ret;
|
|
|
|
|
|
|
|
|
|
/* Output the accumulated buffer, then choose a new best pad */
|
|
|
|
|
GST_DEBUG_OBJECT (mux->current_pad,
|
|
|
|
|
"Outputting accumulated audio PES on pad: %" GST_PTR_FORMAT,
|
|
|
|
|
mux->current_buffer);
|
|
|
|
|
|
|
|
|
|
g_mutex_lock (&mux->lock);
|
|
|
|
|
ret =
|
|
|
|
|
gst_base_ts_mux_output_buffer_locked (GST_BASE_TS_MUX (mux),
|
|
|
|
|
mux->current_pad, mux->current_buffer, mux->current_buffer_dts);
|
|
|
|
|
|
|
|
|
|
mux->current_buffer = NULL;
|
|
|
|
|
|
|
|
|
|
gst_object_unref (mux->current_pad);
|
|
|
|
|
mux->current_pad = NULL;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
|
gst_base_ts_mux_aggregate (GstAggregator * agg, gboolean timeout)
|
|
|
|
|
{
|
|
|
|
|
GstBaseTsMux *mux = GST_BASE_TS_MUX (agg);
|
|
|
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
|
GstBaseTsMuxPad *best = gst_base_ts_mux_find_best_pad (agg);
|
|
|
|
|
GstBaseTsMuxPad *best = NULL;
|
|
|
|
|
GstCaps *caps;
|
|
|
|
|
|
|
|
|
|
/* set caps on the srcpad if no caps were set yet */
|
|
|
|
@ -2501,6 +2601,56 @@ gst_base_ts_mux_aggregate (GstAggregator * agg, gboolean timeout)
|
|
|
|
|
}
|
|
|
|
|
gst_caps_unref (caps);
|
|
|
|
|
|
|
|
|
|
/* If we're accumulating packets from an audio pad, see if we can fit
|
|
|
|
|
* more */
|
|
|
|
|
if (mux->current_pad != NULL) {
|
|
|
|
|
GstBuffer *buffer =
|
|
|
|
|
gst_aggregator_pad_peek_buffer (GST_AGGREGATOR_PAD (mux->current_pad));
|
|
|
|
|
gboolean can_accumulate;
|
|
|
|
|
|
|
|
|
|
if (buffer == NULL)
|
|
|
|
|
goto maybe_eos;
|
|
|
|
|
|
|
|
|
|
can_accumulate =
|
|
|
|
|
can_accumulate_buffer (mux, mux->current_pad, buffer,
|
|
|
|
|
mux->current_buffer);
|
|
|
|
|
gst_buffer_unref (buffer);
|
|
|
|
|
|
|
|
|
|
if (can_accumulate) {
|
|
|
|
|
GstBuffer *tmp;
|
|
|
|
|
|
|
|
|
|
/* Collect another buffer from the current pad */
|
|
|
|
|
best = mux->current_pad;
|
|
|
|
|
|
|
|
|
|
buffer = gst_aggregator_pad_pop_buffer (GST_AGGREGATOR_PAD (best));
|
|
|
|
|
if (!buffer) {
|
|
|
|
|
/* We might have gotten a flush event after we picked the pad */
|
|
|
|
|
goto maybe_eos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (best->prepare_func) {
|
|
|
|
|
tmp = best->prepare_func (buffer, best, mux);
|
|
|
|
|
gst_buffer_unref (buffer);
|
|
|
|
|
buffer = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GST_LOG_OBJECT (best,
|
|
|
|
|
"Accumulating from current pad. Concatenating %" GST_PTR_FORMAT
|
|
|
|
|
" and %" GST_PTR_FORMAT, mux->current_buffer, buffer);
|
|
|
|
|
|
|
|
|
|
/* Append the new packet to the current PES */
|
|
|
|
|
mux->current_buffer = gst_buffer_append (mux->current_buffer, buffer);
|
|
|
|
|
goto done;
|
|
|
|
|
} else {
|
|
|
|
|
ret = finish_current_pes (mux);
|
|
|
|
|
if (ret != GST_FLOW_OK)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (best == NULL)
|
|
|
|
|
best = gst_base_ts_mux_find_best_pad (agg);
|
|
|
|
|
|
|
|
|
|
if (best) {
|
|
|
|
|
GstBuffer *buffer;
|
|
|
|
|
|
|
|
|
@ -2511,7 +2661,7 @@ gst_base_ts_mux_aggregate (GstAggregator * agg, gboolean timeout)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret =
|
|
|
|
|
gst_base_ts_mux_aggregate_buffer (GST_BASE_TS_MUX (agg),
|
|
|
|
|
gst_base_ts_mux_prepare_and_accumulate_buffer (GST_BASE_TS_MUX (agg),
|
|
|
|
|
GST_AGGREGATOR_PAD (best), buffer);
|
|
|
|
|
|
|
|
|
|
gst_object_unref (best);
|
|
|
|
@ -2520,9 +2670,21 @@ gst_base_ts_mux_aggregate (GstAggregator * agg, gboolean timeout)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybe_eos:
|
|
|
|
|
if (mux->current_pad != NULL
|
|
|
|
|
&& gst_aggregator_pad_is_eos (GST_AGGREGATOR_PAD (mux->current_pad))) {
|
|
|
|
|
/* drain some possibly cached data */
|
|
|
|
|
if (mux->current_pad != NULL) {
|
|
|
|
|
ret = finish_current_pes (mux);
|
|
|
|
|
if (ret != GST_FLOW_OK)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gst_base_ts_mux_are_all_pads_eos (mux)) {
|
|
|
|
|
GstBaseTsMuxClass *klass = GST_BASE_TS_MUX_GET_CLASS (mux);
|
|
|
|
|
/* drain some possibly cached data */
|
|
|
|
|
GST_DEBUG_OBJECT (mux, "All pads are EOS. Finishing up");
|
|
|
|
|
|
|
|
|
|
if (klass->drain)
|
|
|
|
|
klass->drain (mux);
|
|
|
|
|
gst_base_ts_mux_push_packets (mux, TRUE);
|
|
|
|
@ -2633,15 +2795,15 @@ gst_base_ts_mux_set_property (GObject * object, guint prop_id,
|
|
|
|
|
break;
|
|
|
|
|
case PROP_PMT_INTERVAL:
|
|
|
|
|
mux->pmt_interval = g_value_get_uint (value);
|
|
|
|
|
g_mutex_lock (&mux->lock);
|
|
|
|
|
GST_OBJECT_LOCK (mux);
|
|
|
|
|
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
|
|
|
|
|
GstBaseTsMuxPad *ts_pad = GST_BASE_TS_MUX_PAD (l->data);
|
|
|
|
|
|
|
|
|
|
g_mutex_lock (&mux->lock);
|
|
|
|
|
tsmux_set_pmt_interval (ts_pad->prog, mux->pmt_interval);
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
}
|
|
|
|
|
GST_OBJECT_UNLOCK (mux);
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_ALIGNMENT:
|
|
|
|
|
mux->alignment = g_value_get_int (value);
|
|
|
|
@ -2666,6 +2828,14 @@ gst_base_ts_mux_set_property (GObject * object, guint prop_id,
|
|
|
|
|
tsmux_set_pcr_interval (mux->tsmux, mux->pcr_interval);
|
|
|
|
|
g_mutex_unlock (&mux->lock);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_AUDIO_PES_TARGET_TIME:
|
|
|
|
|
mux->audio_pes_target_time_ticks = g_value_get_uint (value);
|
|
|
|
|
mux->audio_pes_target_time =
|
|
|
|
|
MPEGTIME_TO_GSTTIME (mux->audio_pes_target_time_ticks);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_AUDIO_PES_TARGET_BYTES:
|
|
|
|
|
mux->audio_pes_target_bytes = g_value_get_uint (value);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_SCTE_35_PID:
|
|
|
|
|
mux->scte35_pid = g_value_get_uint (value);
|
|
|
|
|
break;
|
|
|
|
@ -2706,6 +2876,12 @@ gst_base_ts_mux_get_property (GObject * object, guint prop_id,
|
|
|
|
|
case PROP_PCR_INTERVAL:
|
|
|
|
|
g_value_set_uint (value, mux->pcr_interval);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_AUDIO_PES_TARGET_TIME:
|
|
|
|
|
g_value_set_uint (value, mux->audio_pes_target_time_ticks);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_AUDIO_PES_TARGET_BYTES:
|
|
|
|
|
g_value_set_uint (value, mux->audio_pes_target_bytes);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_SCTE_35_PID:
|
|
|
|
|
g_value_set_uint (value, mux->scte35_pid);
|
|
|
|
|
break;
|
|
|
|
@ -2865,6 +3041,20 @@ gst_base_ts_mux_class_init (GstBaseTsMuxClass * klass)
|
|
|
|
|
TSMUX_DEFAULT_SCTE_35_NULL_INTERVAL,
|
|
|
|
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
|
|
|
|
|
|
|
|
|
g_object_class_install_property (G_OBJECT_CLASS (klass),
|
|
|
|
|
PROP_AUDIO_PES_TARGET_TIME, g_param_spec_uint ("audio-pes-target-time",
|
|
|
|
|
"Audio PES target time",
|
|
|
|
|
"Target audio PES duration for aggregating raw audio packets (in ticks of the 90kHz clock)",
|
|
|
|
|
0, G_MAXUINT, BASETSMUX_DEFAULT_AUDIO_PES_TARGET_TIME,
|
|
|
|
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
|
|
|
|
|
|
|
|
|
g_object_class_install_property (G_OBJECT_CLASS (klass),
|
|
|
|
|
PROP_AUDIO_PES_TARGET_BYTES, g_param_spec_uint ("audio-pes-target-bytes",
|
|
|
|
|
"Audio PES target size",
|
|
|
|
|
"Target audio PES size for aggregating raw audio packets (in bytes)",
|
|
|
|
|
0, G_MAXUINT, BASETSMUX_DEFAULT_AUDIO_PES_TARGET_BYTES,
|
|
|
|
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
|
|
|
|
|
|
|
|
|
gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
|
|
|
|
|
&gst_base_ts_mux_src_factory, GST_TYPE_AGGREGATOR_PAD);
|
|
|
|
|
|
|
|
|
@ -2890,5 +3080,10 @@ gst_base_ts_mux_init (GstBaseTsMux * mux)
|
|
|
|
|
mux->packet_size = GST_BASE_TS_MUX_NORMAL_PACKET_LENGTH;
|
|
|
|
|
mux->automatic_alignment = 0;
|
|
|
|
|
|
|
|
|
|
mux->audio_pes_target_time_ticks = BASETSMUX_DEFAULT_AUDIO_PES_TARGET_TIME;
|
|
|
|
|
mux->audio_pes_target_time =
|
|
|
|
|
MPEGTIME_TO_GSTTIME (mux->audio_pes_target_time_ticks);
|
|
|
|
|
mux->audio_pes_target_bytes = BASETSMUX_DEFAULT_AUDIO_PES_TARGET_BYTES;
|
|
|
|
|
|
|
|
|
|
g_mutex_init (&mux->lock);
|
|
|
|
|
}
|
|
|
|
|