Merge branch 'vuplus-1.6' of 192.168.102.66:/var/ikseong/repo/openembedded into test_0921
[vuplus_openembedded] / recipes / gstreamer / gst-plugins-bad / mpegtsmux_indexing_alignment.diff
1 From: Andreas Frisch <fraxinas@opendreambox.org>
2 Date: Sat, 17 Sep 2011 23:29:21 +0200
3 Subject: [PATCH] [mpegtsmux] introduce an alignment property which specifies
4  the amount of packets, that buffers must contain before
5  they are pushed. On EOS, dummy packets are generated and
6  used for padding until the final buffer is also aligned.
7  With a value of 32 given, this allows creating blu-ray
8  compliant streams.
9
10 From: Andreas Frisch <fraxinas@opendreambox.org>
11 Date: Mon, 28 Mar 2011 10:31:23 +0200
12 Subject: [PATCH] [mpegtsmux] add indexing capabilities to generate a SPN/PTS map on the fly in m2ts-mode
13
14 diff -u a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c
15 --- a/gst/mpegtsmux/mpegtsmux.c 2011-04-21 15:17:35.000000000 +0200
16 +++ b/gst/mpegtsmux/mpegtsmux.c 2011-09-17 23:39:08.671044710 +0200
17 @@ -102,7 +102,8 @@
18    ARG_PROG_MAP,
19    ARG_M2TS_MODE,
20    ARG_PAT_INTERVAL,
21 -  ARG_PMT_INTERVAL
22 +  ARG_PMT_INTERVAL,
23 +  ARG_ALIGNMENT
24  };
25  
26  static GstStaticPadTemplate mpegtsmux_sink_factory =
27 @@ -151,6 +152,11 @@
28  static GstStateChangeReturn mpegtsmux_change_state (GstElement * element,
29      GstStateChange transition);
30  static void mpegtsdemux_set_header_on_caps (MpegTsMux * mux);
31 +static void mpegtsmux_set_index (GstElement * element, GstIndex * index);
32 +static GstIndex *mpegtsmux_get_index (GstElement * element);
33 +
34 +static GstFormat pts_format;
35 +static GstFormat spn_format;
36  
37  GST_BOILERPLATE (MpegTsMux, mpegtsmux, GstElement, GST_TYPE_ELEMENT);
38  
39 @@ -169,6 +175,10 @@
40        "MPEG Transport Stream Muxer", "Codec/Muxer",
41        "Multiplexes media streams into an MPEG Transport Stream",
42        "Fluendo <contact@fluendo.com>");
43 +
44 +  pts_format =
45 +      gst_format_register ("PTS", "MPEG System Presentation Time Stamp");
46 +  spn_format = gst_format_register ("SPN", "Source Packet Number");
47  }
48  
49  static void
50 @@ -185,6 +195,9 @@
51    gstelement_class->release_pad = mpegtsmux_release_pad;
52    gstelement_class->change_state = mpegtsmux_change_state;
53  
54 +  gstelement_class->set_index = GST_DEBUG_FUNCPTR (mpegtsmux_set_index);
55 +  gstelement_class->get_index = GST_DEBUG_FUNCPTR (mpegtsmux_get_index);
56 +
57    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROG_MAP,
58        g_param_spec_boxed ("prog-map", "Program map",
59            "A GstStructure specifies the mapping from elementary streams to programs",
60 @@ -207,6 +220,12 @@
61            "Set the interval (in ticks of the 90kHz clock) for writing out the PMT table",
62            1, G_MAXUINT, TSMUX_DEFAULT_PMT_INTERVAL,
63            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
64 +
65 +  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALIGNMENT,
66 +      g_param_spec_uint ("alignment", "packet alignment",
67 +          "Queue this amount of ts/m2ts packets before pushing buffer. On EOS, pad with dummy packets until aligned. Default: 32 for m2ts streams, else disabled.",
68 +          0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
69 +
70  }
71  
72  static void
73 @@ -238,6 +257,17 @@
74    mux->prog_map = NULL;
75    mux->streamheader = NULL;
76    mux->streamheader_sent = FALSE;
77 +
78 +  mux->last_key_ts = 0;
79 +  mux->last_key_spn = 0;
80 +  mux->spn_count = 0;
81 +
82 +  mux->element_index = NULL;
83 +  mux->element_index_writer_id = -1;
84 +
85 +  mux->arbitrary_align = FALSE;
86 +  mux->alignment_adapter = gst_adapter_new ();
87 +  mux->packets_per_buffer = 0;
88  }
89  
90  static void
91 @@ -279,6 +309,15 @@
92      g_list_free (mux->streamheader);
93      mux->streamheader = NULL;
94    }
95 +  if (mux->alignment_adapter) {
96 +    gst_adapter_clear (mux->alignment_adapter);
97 +    g_object_unref (mux->alignment_adapter);
98 +    mux->alignment_adapter = NULL;
99 +  }
100 +
101 +  if (mux->element_index)
102 +    gst_object_unref (mux->element_index);
103 +
104    GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
105  }
106  
107 @@ -322,12 +361,27 @@
108          walk = g_slist_next (walk);
109        }
110        break;
111 +    case ARG_ALIGNMENT:
112 +      mux->packets_per_buffer = g_value_get_uint (value);
113 +      mux->arbitrary_align = TRUE;
114 +      break;
115      default:
116        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117        break;
118    }
119  }
120  
121 +guint
122 +get_packets_per_buffer (MpegTsMux * mux)
123 +{
124 +  if (mux->arbitrary_align == TRUE) {
125 +    return mux->packets_per_buffer;
126 +  } else if (mux->m2ts_mode) {
127 +    return BDMV_PACKETS_PER_BUFFER;
128 +  }
129 +  return DEFAULT_PACKETS_PER_BUFFER;
130 +}
131 +
132  static void
133  gst_mpegtsmux_get_property (GObject * object, guint prop_id,
134      GValue * value, GParamSpec * pspec)
135 @@ -347,6 +401,9 @@
136      case ARG_PMT_INTERVAL:
137        g_value_set_uint (value, mux->pmt_interval);
138        break;
139 +    case ARG_ALIGNMENT:
140 +      g_value_set_uint (value, get_packets_per_buffer (mux));
141 +      break;
142      default:
143        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
144        break;
145 @@ -354,6 +411,37 @@
146  }
147  
148  static void
149 +mpegtsmux_set_index (GstElement * element, GstIndex * index)
150 +{
151 +  MpegTsMux *mux = GST_MPEG_TSMUX (element);
152 +
153 +  GST_OBJECT_LOCK (mux);
154 +  if (mux->element_index)
155 +    gst_object_unref (mux->element_index);
156 +  mux->element_index = index ? gst_object_ref (index) : NULL;
157 +  GST_OBJECT_UNLOCK (mux);
158 +  GST_DEBUG_OBJECT (mux, "Set index %" GST_PTR_FORMAT, mux->element_index);
159 +  gst_index_add_format (index, mux->element_index_writer_id, pts_format);
160 +  gst_index_add_format (index, mux->element_index_writer_id, spn_format);
161 +}
162 +
163 +static GstIndex *
164 +mpegtsmux_get_index (GstElement * element)
165 +{
166 +  GstIndex *result = NULL;
167 +  MpegTsMux *mux = GST_MPEG_TSMUX (element);
168 +
169 +  GST_OBJECT_LOCK (mux);
170 +  if (mux->element_index)
171 +    result = gst_object_ref (mux->element_index);
172 +  GST_OBJECT_UNLOCK (mux);
173 +
174 +  GST_DEBUG_OBJECT (mux, "Returning index %" GST_PTR_FORMAT, result);
175 +
176 +  return result;
177 +}
178 +
179 +static void
180  release_buffer_cb (guint8 * data, void *user_data)
181  {
182    GstBuffer *buf = (GstBuffer *) user_data;
183 @@ -496,6 +584,24 @@
184      ret = GST_FLOW_OK;
185    }
186  
187 +  if (mux->element_index) {
188 +    gboolean parsed = FALSE;
189 +    if (ts_data->stream->is_video_stream) {
190 +      if (gst_structure_get_boolean (s, "parsed", &parsed) && parsed) {
191 +        if (mux->element_index_writer_id == -1) {
192 +          gst_index_get_writer_id (mux->element_index, GST_OBJECT (mux),
193 +              &mux->element_index_writer_id);
194 +          GST_INFO_OBJECT (mux,
195 +              "created GstIndex writer_id = %d for PID 0x%04x",
196 +              mux->element_index_writer_id, ts_data->pid);
197 +        }
198 +      } else
199 +        GST_WARNING_OBJECT (pad,
200 +            "Indexing capability for PID=0x%04x disabled - parsed input stream is required!",
201 +            ts_data->pid);
202 +    }
203 +  }
204 +
205  beach:
206    gst_caps_unref (caps);
207    return ret;
208 @@ -642,12 +748,113 @@
209      }
210    }
211    if (c_best) {
212 -    gst_buffer_unref (gst_collect_pads_pop (mux->collect, c_best));
213 +    GstBuffer *buffer;
214 +    if ((buffer = gst_collect_pads_pop (mux->collect, c_best)))
215 +      gst_buffer_unref (buffer);
216    }
217  
218    return best;
219  }
220  
221 +static GstFlowReturn
222 +aligned_push (MpegTsMux * mux, GstBuffer * buf)
223 +{
224 +  guint accu_bytes, packet_length;
225 +  GstBuffer *out_buf;
226 +
227 +  if (get_packets_per_buffer (mux) == 0) {
228 +    return gst_pad_push (mux->srcpad, buf);
229 +  }
230 +
231 +  packet_length = mux->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH;
232 +  gst_adapter_push (mux->alignment_adapter, buf);
233 +
234 +  accu_bytes = gst_adapter_available (mux->alignment_adapter);
235 +  GST_DEBUG_OBJECT (mux,
236 +      "Accumulating packet in alignment adapter, accu_bytes=%i", accu_bytes);
237 +
238 +  if (accu_bytes == get_packets_per_buffer (mux) * packet_length) {
239 +    out_buf = gst_adapter_take_buffer (mux->alignment_adapter, accu_bytes);
240 +    gst_buffer_set_caps (out_buf, GST_PAD_CAPS (mux->srcpad));
241 +    gst_adapter_clear (mux->alignment_adapter);
242 +    GST_DEBUG_OBJECT (mux,
243 +        "Accumulated desired amount of packets in alignment unit, handing off %i bytes",
244 +        accu_bytes);
245 +    return gst_pad_push (mux->srcpad, out_buf);
246 +  } else if (accu_bytes > get_packets_per_buffer (mux) * packet_length) {
247 +    GST_WARNING_OBJECT (mux, "Packet alignment error!");
248 +    gst_adapter_clear (mux->alignment_adapter);
249 +    return GST_FLOW_CUSTOM_ERROR;
250 +  }
251 +
252 +  return GST_FLOW_OK;
253 +}
254 +
255 +static void
256 +mpegtsmux_eos_align (MpegTsMux * mux)
257 +{
258 +  guint accu_bytes, packet_length, packets_needed, dummy_packet_count;
259 +  guint32 m2ts_header;
260 +  guint continuity_counter;
261 +  unsigned char header[4];
262 +  guint p;
263 +  GstBuffer *buf;
264 +
265 +  accu_bytes = gst_adapter_available (mux->alignment_adapter);
266 +  packet_length = mux->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH;
267 +  packets_needed = get_packets_per_buffer (mux) - accu_bytes / packet_length;
268 +
269 +  if (get_packets_per_buffer (mux) == 0 || accu_bytes == 0) {
270 +    return;
271 +  }
272 +
273 +  GST_DEBUG_OBJECT (mux,
274 +      "received EOS - %i bytes accumulated in alignment adapter -> %i dummy packets needed for padding!\n",
275 +      accu_bytes, packets_needed);
276 +
277 +  if (mux->m2ts_mode) {
278 +    gst_adapter_copy (mux->alignment_adapter, header,
279 +        accu_bytes - packet_length, 4);
280 +    m2ts_header = GST_READ_UINT32_BE (header);
281 +    gst_adapter_copy (mux->alignment_adapter, header,
282 +        accu_bytes - packet_length + 7, 1);
283 +  } else {
284 +    gst_adapter_copy (mux->alignment_adapter, header,
285 +        accu_bytes - packet_length + 3, 1);
286 +  }
287 +
288 +  continuity_counter = header[0] & 0xF;
289 +
290 +  for (dummy_packet_count = 0; dummy_packet_count < packets_needed;
291 +      dummy_packet_count++) {
292 +    buf = gst_buffer_new_and_alloc (packet_length);
293 +    if (mux->m2ts_mode) {
294 +      // monotonically increase m2ts_header
295 +      m2ts_header++;
296 +      GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), m2ts_header);
297 +      p = GST_BUFFER_DATA (buf) + 4;
298 +    } else {
299 +      p = GST_BUFFER_DATA (buf);
300 +    }
301 +    GST_WRITE_UINT8 (p++, TSMUX_SYNC_BYTE);
302 +    // dummy PID
303 +    GST_WRITE_UINT16_BE (p, 0x1FFF);
304 +    p += 2;
305 +    // adaptation field exists | no payload exists | continuity counter
306 +    GST_WRITE_UINT8 (p++, 0x20 + ((++continuity_counter) & 0xF));
307 +    // adaptation field length | flags
308 +    GST_WRITE_UINT16_BE (p, 0xB700);
309 +    p += 2;
310 +    // adaptation field
311 +    memset (p, 0xFF, 0xB6);
312 +
313 +    aligned_push (mux, buf);
314 +    GST_LOG_OBJECT (mux,
315 +        "generated dummy packet %i with m2ts_header=0x%x, contiuity=0x%02x\n",
316 +        dummy_packet_count, m2ts_header, continuity_counter);
317 +  }
318 +}
319 +
320  #define COLLECT_DATA_PAD(collect_data) (((GstCollectData *)(collect_data))->pad)
321  
322  static GstFlowReturn
323 @@ -679,20 +886,19 @@
324      if (prog == NULL) {
325        GST_ELEMENT_ERROR (mux, STREAM, MUX,
326            ("Stream on pad %" GST_PTR_FORMAT
327 -              " is not associated with any program", best), (NULL));
328 +              " is not associated with any program", COLLECT_DATA_PAD (best)),
329 +          (NULL));
330        return GST_FLOW_ERROR;
331      }
332  
333      if (G_UNLIKELY (prog->pcr_stream == NULL)) {
334 -      if (best) {
335 -        /* Take the first data stream for the PCR */
336 -        GST_DEBUG_OBJECT (COLLECT_DATA_PAD (best),
337 -            "Use stream (pid=%d) from pad as PCR for program (prog_id = %d)",
338 -            MPEG_TS_PAD_DATA (best)->pid, MPEG_TS_PAD_DATA (best)->prog_id);
339 +      /* Take the first data stream for the PCR */
340 +      GST_DEBUG_OBJECT (COLLECT_DATA_PAD (best),
341 +          "Use stream (pid=%d) from pad as PCR for program (prog_id = %d)",
342 +          MPEG_TS_PAD_DATA (best)->pid, MPEG_TS_PAD_DATA (best)->prog_id);
343  
344 -        /* Set the chosen PCR stream */
345 -        tsmux_program_set_pcr_stream (prog, best->stream);
346 -      }
347 +      /* Set the chosen PCR stream */
348 +      tsmux_program_set_pcr_stream (prog, best->stream);
349      }
350  
351      g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
352 @@ -709,6 +915,9 @@
353            G_GINT64_FORMAT, GST_TIME_ARGS (best->cur_ts), pts);
354      }
355  
356 +    if (G_UNLIKELY (!delta))
357 +      mux->last_key_ts = best->cur_ts;
358 +
359      tsmux_stream_add_data (best->stream, GST_BUFFER_DATA (buf),
360          GST_BUFFER_SIZE (buf), buf, pts, -1, !delta);
361      best->queued_buf = NULL;
362 @@ -730,6 +939,7 @@
363    } else {
364      /* FIXME: Drain all remaining streams */
365      /* At EOS */
366 +    mpegtsmux_eos_align (mux);
367      gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
368    }
369  
370 @@ -839,6 +1049,7 @@
371    } else {
372      GST_DEBUG_OBJECT (mux, "marking as non-delta unit");
373      mux->is_delta = TRUE;
374 +    mux->last_key_spn = mux->spn_count;
375    }
376  }
377  
378 @@ -862,6 +1073,8 @@
379  
380    new_packet_common_init (mux, buf, data, len);
381  
382 +  mux->spn_count++;
383 +
384    /* copies the TS data of 188 bytes to the m2ts buffer at an offset
385       of 4 bytes to leave space for writing the timestamp later */
386    memcpy (GST_BUFFER_DATA (buf) + 4, data, len);
387 @@ -925,12 +1138,22 @@
388        gst_buffer_set_caps (out_buf, GST_PAD_CAPS (mux->srcpad));
389        GST_BUFFER_TIMESTAMP (out_buf) = MPEG_SYS_TIME_TO_GSTTIME (cur_pcr);
390  
391 +      if (mux->element_index) {
392 +        if (!GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
393 +          gst_index_add_association (mux->element_index,
394 +              mux->element_index_writer_id,
395 +              GST_ASSOCIATION_FLAG_KEY_UNIT, spn_format,
396 +              mux->last_key_spn, pts_format,
397 +              GSTTIME_TO_MPEGTIME (mux->last_key_ts), NULL);
398 +        }
399 +      }
400 +
401        /* Write the 4 byte timestamp value, bottom 30 bits only = PCR */
402        GST_WRITE_UINT32_BE (GST_BUFFER_DATA (out_buf), cur_pcr & 0x3FFFFFFF);
403  
404        GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
405            G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, cur_pcr);
406 -      ret = gst_pad_push (mux->srcpad, out_buf);
407 +      ret = aligned_push (mux, out_buf);
408        if (G_UNLIKELY (ret != GST_FLOW_OK)) {
409          mux->last_flow_ret = ret;
410          return FALSE;
411 @@ -946,7 +1169,7 @@
412  
413    GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
414        G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, new_pcr);
415 -  ret = gst_pad_push (mux->srcpad, buf);
416 +  ret = aligned_push (mux, buf);
417    if (G_UNLIKELY (ret != GST_FLOW_OK)) {
418      mux->last_flow_ret = ret;
419      return FALSE;
420 @@ -976,7 +1199,7 @@
421    memcpy (GST_BUFFER_DATA (buf), data, len);
422    GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
423  
424 -  ret = gst_pad_push (mux->srcpad, buf);
425 +  ret = aligned_push (mux, buf);
426    if (G_UNLIKELY (ret != GST_FLOW_OK)) {
427      mux->last_flow_ret = ret;
428      return FALSE;
429 @@ -1074,6 +1297,8 @@
430      case GST_STATE_CHANGE_READY_TO_NULL:
431        if (mux->adapter)
432          gst_adapter_clear (mux->adapter);
433 +      if (mux->alignment_adapter)
434 +        gst_adapter_clear (mux->alignment_adapter);
435        break;
436      default:
437        break;
438 diff -u gst-original/mpegtsmux/mpegtsmux.h b/gst/mpegtsmux/mpegtsmux.h
439 --- gst-original/mpegtsmux/mpegtsmux.h  2011-04-08 01:35:37.000000000 +0200
440 +++ b/gst/mpegtsmux/mpegtsmux.h 2011-09-17 23:39:06.427894850 +0200
441 @@ -128,6 +128,16 @@
442  
443    GList *streamheader;
444    gboolean streamheader_sent;
445 +
446 +  guint32 spn_count;
447 +  guint64 last_key_ts;
448 +  guint32 last_key_spn;
449 +  GstIndex *element_index;
450 +  gint      element_index_writer_id;
451
452 +  gboolean arbitrary_align;
453 +  guint packets_per_buffer;
454 +  GstAdapter *alignment_adapter;
455  };
456  
457  struct MpegTsMuxClass  {
458 @@ -183,6 +193,9 @@
459  #define MAX_PROG_NUMBER        32
460  #define DEFAULT_PROG_ID        0
461  
462 +#define DEFAULT_PACKETS_PER_BUFFER 0
463 +#define BDMV_PACKETS_PER_BUFFER           32
464 +
465  G_END_DECLS
466  
467  #endif