Add missing markers. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1335>
		
			
				
	
	
		
			554 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			554 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * GStreamer AVTP Plugin
 | |
|  * Copyright (c) 2021, Fastree3D
 | |
|  * Adrian Fiergolski <Adrian.Fiergolski@fastree3d.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
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * SECTION:element-avtprvfpay
 | |
|  * @see_also: avtprvfdepay
 | |
|  *
 | |
|  * Payload raw video into AVTPDUs according
 | |
|  * to IEEE 1722-2016. For detailed information see
 | |
|  * https://standards.ieee.org/standard/1722-2016.html.
 | |
|  *
 | |
|  * <refsect2>
 | |
|  * <title>Example pipeline</title>
 | |
|  * |[
 | |
|  * gst-launch-1.0 videotestsrc ! avtprvfpay ! avtpsink
 | |
|  * ]| This example pipeline will payload raw video. Refer to the avtprvfdepay
 | |
|  * example to depayload and play the AVTP stream.
 | |
|  * </refsect2>
 | |
|  * Since: 1.24
 | |
|  */
 | |
| 
 | |
| #include <avtp.h>
 | |
| #include <avtp_rvf.h>
 | |
| 
 | |
| #include <gst/video/video.h>
 | |
| #include "gstavtprvfpay.h"
 | |
| 
 | |
| GST_DEBUG_CATEGORY_STATIC (avtprvfpay_debug);
 | |
| #define GST_CAT_DEFAULT avtprvfpay_debug
 | |
| 
 | |
| /* prototypes */
 | |
| static GstStateChangeReturn gst_avtp_rvf_change_state (GstElement *
 | |
|     element, GstStateChange transition);
 | |
| 
 | |
| static gboolean gst_avtp_rvf_pay_new_caps (GstAvtpVfPayBase * avtpvfpaybase,
 | |
|     GstCaps * caps);
 | |
| static gboolean gst_avtp_rvf_pay_prepare_avtp_packets (GstAvtpVfPayBase *
 | |
|     avtpvfpaybase, GstBuffer * buffer, GPtrArray * avtp_packets);
 | |
| 
 | |
| enum
 | |
| {
 | |
|   PROP_0,
 | |
| };
 | |
| 
 | |
| #define AVTP_RVF_HEADER_SIZE (sizeof(struct avtp_stream_pdu) + sizeof(uint64_t))
 | |
| 
 | |
| /* pad templates */
 | |
| 
 | |
| static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
 | |
|     GST_PAD_SINK,
 | |
|     GST_PAD_ALWAYS,
 | |
|     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{GRAY16_LE}"))
 | |
|     );
 | |
| 
 | |
| /* class initialization */
 | |
| 
 | |
| #define gst_avtp_rvf_pay_parent_class parent_class
 | |
| G_DEFINE_TYPE (GstAvtpRvfPay, gst_avtp_rvf_pay, GST_TYPE_AVTP_VF_PAY_BASE);
 | |
| GST_ELEMENT_REGISTER_DEFINE (avtprvfpay, "avtprvfpay", GST_RANK_NONE,
 | |
|     GST_TYPE_AVTP_RVF_PAY);
 | |
| 
 | |
| static void
 | |
| gst_avtp_rvf_pay_class_init (GstAvtpRvfPayClass * klass)
 | |
| {
 | |
|   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
 | |
|   GstAvtpVfPayBaseClass *avtpvfpaybase_class =
 | |
|       GST_AVTP_VF_PAY_BASE_CLASS (klass);
 | |
| 
 | |
|   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
 | |
| 
 | |
|   gst_element_class_set_static_metadata (gstelement_class,
 | |
|       "AVTP Raw Video Format (RVF) payloader",
 | |
|       "Codec/Payloader/Network/AVTP",
 | |
|       "Payload-encode raw video into RVF AVTPDU (IEEE 1722)",
 | |
|       "Adrian Fiergolski <Adrian.Fiergolski@fastree3d.com>");
 | |
| 
 | |
|   gstelement_class->change_state =
 | |
|       GST_DEBUG_FUNCPTR (gst_avtp_rvf_change_state);
 | |
| 
 | |
|   avtpvfpaybase_class->new_caps = GST_DEBUG_FUNCPTR (gst_avtp_rvf_pay_new_caps);
 | |
|   avtpvfpaybase_class->prepare_avtp_packets =
 | |
|       GST_DEBUG_FUNCPTR (gst_avtp_rvf_pay_prepare_avtp_packets);
 | |
| 
 | |
|   GST_DEBUG_CATEGORY_INIT (avtprvfpay_debug, "avtprvfpay",
 | |
|       0, "debug category for avtprvfpay element");
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_avtp_rvf_pay_init (GstAvtpRvfPay * avtprvfpay)
 | |
| {
 | |
|   avtprvfpay->header = NULL;
 | |
|   /* size of the payload */
 | |
|   avtprvfpay->fragment_size = 0;
 | |
|   /* large raster: number of data bytes for a fragment at the end of line */
 | |
|   avtprvfpay->fragment_eol_size = 0;
 | |
|   avtprvfpay->fragment_padding = NULL;
 | |
|   avtprvfpay->num_lines = 0;
 | |
|   /* size of the line in bytes */
 | |
|   avtprvfpay->line_size = 0;
 | |
|   /* large raster: maximum i_seq_num */
 | |
|   avtprvfpay->i_seq_max = 0;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| gst_avtp_rvf_pay_new_caps (GstAvtpVfPayBase * avtpvfpaybase, GstCaps * caps)
 | |
| {
 | |
|   GstAvtpRvfPay *avtprvfpay = GST_AVTP_RVF_PAY (avtpvfpaybase);
 | |
|   GstMapInfo map;
 | |
|   struct avtp_stream_pdu *pdu;
 | |
| 
 | |
|   GstVideoInfo info;
 | |
|   unsigned int fps_up, fps_down;
 | |
| 
 | |
|   gboolean ret = FALSE;
 | |
| 
 | |
|   gsize fragment_padding_size;
 | |
| 
 | |
|   GST_DEBUG_OBJECT (avtprvfpay, "gst_avtp_rvf_pay_new_caps");
 | |
| 
 | |
|   gst_buffer_map (avtprvfpay->header, &map, GST_MAP_WRITE);
 | |
|   pdu = (struct avtp_stream_pdu *) map.data;
 | |
| 
 | |
|   if (!gst_video_info_from_caps (&info, caps)) {
 | |
|     GST_ERROR_OBJECT (avtprvfpay,
 | |
|         "Can't retrieve the video information from caps");
 | |
|     goto error;
 | |
|   }
 | |
| 
 | |
|   avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_ACTIVE_PIXELS, info.width);
 | |
|   avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_TOTAL_LINES, info.height);
 | |
| 
 | |
|   switch (info.interlace_mode) {
 | |
|     case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_I, 0);
 | |
|       break;
 | |
|       /* to-do: support for interleaved modes */
 | |
|     case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
 | |
|     case GST_VIDEO_INTERLACE_MODE_FIELDS:
 | |
|     case GST_VIDEO_INTERLACE_MODE_ALTERNATE:
 | |
|     default:
 | |
|       GST_ERROR_OBJECT (avtprvfpay, "Unsupported interlace mode");
 | |
|       goto error;
 | |
|   }
 | |
|   avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_F, 0);
 | |
| 
 | |
|   switch (*info.finfo->depth) {
 | |
|     case 8:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_PIXEL_DEPTH,
 | |
|           AVTP_RVF_PIXEL_DEPTH_8);
 | |
|       break;
 | |
|     case 16:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_PIXEL_DEPTH,
 | |
|           AVTP_RVF_PIXEL_DEPTH_16);
 | |
|       break;
 | |
|       /* to-do: add support for 10 and 12 bit pixel depth
 | |
|        * it requires shifting of the buffer data */
 | |
|     case 10:
 | |
|     case 12:
 | |
|     default:
 | |
|       GST_ERROR_OBJECT (avtprvfpay, "Unsupported pixel depth");
 | |
|       goto error;
 | |
|   }
 | |
| 
 | |
|   if (info.finfo->n_planes != 1) {
 | |
|     GST_ERROR_OBJECT (avtprvfpay, "Planar formats are not supported");
 | |
|     goto error;
 | |
|   }
 | |
|   //All pixels are active
 | |
|   avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_AP, 1);
 | |
| 
 | |
|   switch (info.finfo->format) {
 | |
|     case GST_VIDEO_FORMAT_GRAY16_LE:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_PIXEL_FORMAT,
 | |
|           AVTP_RVF_PIXEL_FORMAT_MONO);
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_COLORSPACE,
 | |
|           AVTP_RVF_COLORSPACE_GRAY);
 | |
|       break;
 | |
|       /* to-do: support more formats */
 | |
|     default:
 | |
|       GST_ERROR_OBJECT (avtprvfpay, "Unsupported video format");
 | |
|       goto error;
 | |
|   }
 | |
| 
 | |
|   avtprvfpay->line_size =
 | |
|       (info.finfo->n_components * (*info.finfo->depth) * info.width) / 8;
 | |
|   //video_data_payload field for large rasters
 | |
|   if (avtprvfpay->line_size > avtpvfpaybase->mtu - AVTP_RVF_HEADER_SIZE) {
 | |
|     avtprvfpay->num_lines = 0;
 | |
| 
 | |
|     avtprvfpay->fragment_size = avtpvfpaybase->mtu - AVTP_RVF_HEADER_SIZE;
 | |
|     avtprvfpay->fragment_eol_size =
 | |
|         avtprvfpay->line_size % avtprvfpay->fragment_size;
 | |
|     avtprvfpay->i_seq_max = avtprvfpay->line_size / avtprvfpay->fragment_size;
 | |
| 
 | |
|     fragment_padding_size =
 | |
|         avtprvfpay->fragment_size - avtprvfpay->fragment_eol_size;
 | |
|   }
 | |
|   //video_data_payload field for small rasters
 | |
|   else {
 | |
|     //only full lines
 | |
|     avtprvfpay->num_lines =
 | |
|         (avtpvfpaybase->mtu - AVTP_RVF_HEADER_SIZE) / avtprvfpay->line_size;
 | |
| 
 | |
|     //Full video frame is smaller than MTU
 | |
|     if (avtprvfpay->num_lines > info.height)
 | |
|       avtprvfpay->num_lines = info.height;
 | |
| 
 | |
|     //num_lines field is 4 bit only
 | |
|     if (avtprvfpay->num_lines > 15)
 | |
|       avtprvfpay->num_lines = 15;
 | |
| 
 | |
|     avtprvfpay->fragment_size = avtprvfpay->num_lines * avtprvfpay->line_size;
 | |
|     avtprvfpay->fragment_eol_size = 0;
 | |
|     avtprvfpay->i_seq_max = 0;
 | |
| 
 | |
|     fragment_padding_size =
 | |
|         avtprvfpay->fragment_size -
 | |
|         ((info.height % avtprvfpay->num_lines) * avtprvfpay->line_size);
 | |
|   }
 | |
|   avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_NUM_LINES, avtprvfpay->num_lines);
 | |
| 
 | |
|   //FPS
 | |
|   fps_down = info.fps_n / info.fps_d;   //Round down
 | |
|   fps_up = (info.fps_n + (info.fps_d - 1)) / info.fps_d;        //Round up
 | |
| 
 | |
|   if (fps_down == fps_up) {
 | |
|     avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_PD, 0);
 | |
|   } else {
 | |
|     if ((info.fps_n * 1001) == info.fps_d * 1000 * fps_up)
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_PD, 1);
 | |
|     else
 | |
|       GST_ERROR_OBJECT (avtprvfpay, "Unsupported frame rate");
 | |
|     goto error;
 | |
|   }
 | |
| 
 | |
|   switch (fps_up) {
 | |
|     case 1:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_1);
 | |
|       break;
 | |
|     case 2:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_2);
 | |
|       break;
 | |
|     case 5:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_5);
 | |
|       break;
 | |
|     case 10:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_10);
 | |
|       break;
 | |
|     case 15:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_15);
 | |
|       break;
 | |
|     case 20:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_20);
 | |
|       break;
 | |
|     case 24:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_24);
 | |
|       break;
 | |
|     case 25:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_25);
 | |
|       break;
 | |
|     case 30:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_30);
 | |
|       break;
 | |
|     case 48:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_48);
 | |
|       break;
 | |
|     case 50:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_50);
 | |
|       break;
 | |
|     case 60:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_60);
 | |
|       break;
 | |
|     case 72:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_72);
 | |
|       break;
 | |
|     case 85:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_85);
 | |
|       break;
 | |
|     case 100:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_100);
 | |
|       break;
 | |
|     case 120:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_120);
 | |
|       break;
 | |
|     case 150:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_150);
 | |
|       break;
 | |
|     case 200:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_200);
 | |
|       break;
 | |
|     case 240:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_240);
 | |
|       break;
 | |
|     case 300:
 | |
|       avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_FRAME_RATE,
 | |
|           AVTP_RVF_FRAME_RATE_300);
 | |
|       break;
 | |
|     default:
 | |
|       GST_ERROR_OBJECT (avtprvfpay, "Unsupported frame rate");
 | |
|       goto error;
 | |
|   }
 | |
| 
 | |
|   //padding bytes
 | |
|   avtprvfpay->fragment_padding = gst_buffer_new_allocate (NULL,
 | |
|       fragment_padding_size, NULL);
 | |
|   if (G_UNLIKELY (avtprvfpay->fragment_padding == NULL)) {
 | |
|     GST_ERROR_OBJECT (avtprvfpay,
 | |
|         "Could not allocate memory for padding bytes");
 | |
|     goto error;
 | |
|   }
 | |
|   gst_buffer_memset (avtprvfpay->fragment_padding, 0, 0U,
 | |
|       fragment_padding_size);
 | |
| 
 | |
|   ret = TRUE;
 | |
| 
 | |
| error:
 | |
|   gst_buffer_unmap (avtprvfpay->header, &map);
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static GstStateChangeReturn
 | |
| gst_avtp_rvf_change_state (GstElement * element, GstStateChange transition)
 | |
| {
 | |
|   GstAvtpRvfPay *avtprvfpay = GST_AVTP_RVF_PAY (element);
 | |
|   GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (avtprvfpay);
 | |
|   GstStateChangeReturn ret;
 | |
| 
 | |
|   if (transition == GST_STATE_CHANGE_NULL_TO_READY) {
 | |
|     GstMapInfo map;
 | |
|     struct avtp_stream_pdu *pdu;
 | |
|     int res;
 | |
| 
 | |
|     avtprvfpay->header = gst_buffer_new_allocate (NULL,
 | |
|         AVTP_RVF_HEADER_SIZE, NULL);
 | |
|     if (avtprvfpay->header == NULL) {
 | |
|       GST_ERROR_OBJECT (avtprvfpay, "Could not allocate buffer");
 | |
|       return GST_STATE_CHANGE_FAILURE;
 | |
|     }
 | |
| 
 | |
|     gst_buffer_map (avtprvfpay->header, &map, GST_MAP_WRITE);
 | |
|     pdu = (struct avtp_stream_pdu *) map.data;
 | |
| 
 | |
|     res = avtp_rvf_pdu_init (pdu);
 | |
|     g_assert (res == 0);
 | |
| 
 | |
|     res =
 | |
|         avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_STREAM_ID,
 | |
|         avtpbasepayload->streamid);
 | |
|     g_assert (res == 0);
 | |
| 
 | |
|     gst_buffer_unmap (avtprvfpay->header, &map);
 | |
|   }
 | |
| 
 | |
|   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
 | |
|   if (ret == GST_STATE_CHANGE_FAILURE) {
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
 | |
|     gst_buffer_unref (avtprvfpay->header);
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /* Checks if stream is large raster video (see: 12.2.9) */
 | |
| static gboolean
 | |
| is_large_raster (GstAvtpRvfPay * avtprvfpay)
 | |
| {
 | |
|   return avtprvfpay->num_lines == 0;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| gst_avtp_rvf_pay_prepare_avtp_packets (GstAvtpVfPayBase * avtpvfpaybase,
 | |
|     GstBuffer * buffer, GPtrArray * avtp_packets)
 | |
| {
 | |
|   GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (avtpvfpaybase);
 | |
|   GstAvtpRvfPay *avtprvfpay = GST_AVTP_RVF_PAY (avtpvfpaybase);
 | |
|   GstBuffer *header;
 | |
|   GstMapInfo map;
 | |
|   guint64 avtp_time;
 | |
|   gsize offset, buffer_size;
 | |
|   gsize i_seq_num, line_number;
 | |
| 
 | |
|   GST_LOG_OBJECT (avtprvfpay,
 | |
|       "Preparing AVTP packets for video frame whose size is %" G_GSIZE_FORMAT,
 | |
|       gst_buffer_get_size (buffer));
 | |
| 
 | |
|   /* Calculate timestamps using PTS as base
 | |
|    * - code inherited from avtpbasepayload.
 | |
|    * Also worth noting: `avtpbasepayload->latency` is updated after
 | |
|    * first call to gst_avtp_base_payload_calc_ptime, so we MUST call
 | |
|    * it before using the latency value */
 | |
|   avtp_time = gst_avtp_base_payload_calc_ptime (avtpbasepayload, buffer);
 | |
| 
 | |
|   offset = 0;
 | |
|   buffer_size = gst_buffer_get_size (buffer);
 | |
|   i_seq_num = 0;
 | |
|   line_number = 1;
 | |
|   while (offset != buffer_size) {
 | |
|     GstBuffer *packet;
 | |
|     struct avtp_stream_pdu *pdu;
 | |
|     gint res;
 | |
|     GstBuffer *fragment;
 | |
|     gsize fragment_size;
 | |
| 
 | |
|     /* Copy header to reuse common fields and change what is needed */
 | |
|     header = gst_buffer_copy (avtprvfpay->header);
 | |
|     gst_buffer_map (header, &map, GST_MAP_WRITE);
 | |
|     pdu = (struct avtp_stream_pdu *) map.data;
 | |
| 
 | |
|     /* Prepare the fragment */
 | |
|     if (is_large_raster (avtprvfpay)) {
 | |
|       if (i_seq_num == avtprvfpay->i_seq_max)
 | |
|         fragment_size = avtprvfpay->fragment_eol_size;
 | |
|       else
 | |
|         fragment_size = avtprvfpay->fragment_size;
 | |
|     } else {
 | |
|       guint reamaining_size = buffer_size - offset;
 | |
|       if (reamaining_size < avtprvfpay->fragment_size)
 | |
|         fragment_size = reamaining_size;
 | |
|       else
 | |
|         fragment_size = avtprvfpay->fragment_size;
 | |
|     }
 | |
| 
 | |
|     fragment = gst_buffer_copy_region (buffer,
 | |
|         GST_BUFFER_COPY_MEMORY, offset, fragment_size);
 | |
| 
 | |
|     offset += fragment_size;
 | |
| 
 | |
|     /* video_data_payload is always the same size
 | |
|      * so add padding bytes if needed */
 | |
|     if (fragment_size != avtprvfpay->fragment_size) {
 | |
|       fragment = gst_buffer_append (fragment, avtprvfpay->fragment_padding);
 | |
|     }
 | |
| 
 | |
|     GST_DEBUG_OBJECT (avtprvfpay,
 | |
|         "Generated fragment with size %" G_GSIZE_FORMAT,
 | |
|         avtprvfpay->fragment_size);
 | |
| 
 | |
|     /* Stream data len includes AVTP raw header len as this is part of
 | |
|      * the payload too. It's just the uint64_t */
 | |
|     res =
 | |
|         avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_STREAM_DATA_LEN,
 | |
|         avtprvfpay->fragment_size + sizeof (uint64_t));
 | |
|     g_assert (res == 0);
 | |
| 
 | |
|     res =
 | |
|         avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_SEQ_NUM,
 | |
|         avtpbasepayload->seqnum++);
 | |
|     g_assert (res == 0);
 | |
| 
 | |
|     /* AVTP_TS fields */
 | |
|     if ((is_large_raster (avtprvfpay) && i_seq_num == 0)
 | |
|         || (!is_large_raster (avtprvfpay)
 | |
|             && line_number == 1)) {
 | |
|       res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_TV, 1);
 | |
|       g_assert (res == 0);
 | |
| 
 | |
|       res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_TIMESTAMP, avtp_time);
 | |
|       g_assert (res == 0);
 | |
| 
 | |
|       GST_LOG_OBJECT (avtprvfpay, "TV packet sent, PTS: %" GST_TIME_FORMAT
 | |
|           " DTS: %" GST_TIME_FORMAT " AVTP_TS: %" GST_TIME_FORMAT,
 | |
|           GST_TIME_ARGS (avtp_time),
 | |
|           GST_TIME_ARGS (GST_BUFFER_DTS (buffer)),
 | |
|           GST_TIME_ARGS (avtp_time & 0xffffffff));
 | |
|     }
 | |
| 
 | |
|     /* Set ef */
 | |
|     if (offset == buffer_size) {        //last fragment
 | |
|       res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_EF, 1);
 | |
|       g_assert (res == 0);
 | |
|     }
 | |
| 
 | |
|     /* Set line_number */
 | |
|     res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_LINE_NUMBER, line_number);
 | |
|     g_assert (res == 0);
 | |
| 
 | |
|     if (is_large_raster (avtprvfpay)) {
 | |
|       if (i_seq_num == avtprvfpay->i_seq_max)
 | |
|         line_number++;
 | |
|     } else {
 | |
|       line_number += avtprvfpay->num_lines;
 | |
|     }
 | |
| 
 | |
|     /* Handle i_seq_num only for large raster */
 | |
|     if (is_large_raster (avtprvfpay)) {
 | |
|       res = avtp_rvf_pdu_set (pdu, AVTP_RVF_FIELD_RAW_I_SEQ_NUM, i_seq_num);
 | |
|       g_assert (res == 0);
 | |
| 
 | |
|       if (i_seq_num < avtprvfpay->i_seq_max)
 | |
|         i_seq_num++;
 | |
|       else
 | |
|         i_seq_num = 0;
 | |
|     }
 | |
| 
 | |
|     packet = gst_buffer_append (header, fragment);
 | |
| 
 | |
|     /* Keep original timestamps */
 | |
|     GST_BUFFER_PTS (packet) = GST_BUFFER_PTS (buffer);
 | |
|     GST_BUFFER_DTS (packet) = GST_BUFFER_DTS (buffer);
 | |
| 
 | |
|     g_ptr_array_add (avtp_packets, packet);
 | |
| 
 | |
|     gst_buffer_unmap (header, &map);
 | |
|   }
 | |
| 
 | |
|   gst_buffer_unref (buffer);
 | |
| 
 | |
|   GST_LOG_OBJECT (avtprvfpay, "Prepared %u AVTP packets", avtp_packets->len);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 |