1f90cf607SDaniel W. S. Almeida // SPDX-License-Identifier: GPL-2.0
2f90cf607SDaniel W. S. Almeida /*
3f90cf607SDaniel W. S. Almeida * Vidtv serves as a reference DVB driver and helps validate the existing APIs
4f90cf607SDaniel W. S. Almeida * in the media subsystem. It can also aid developers working on userspace
5f90cf607SDaniel W. S. Almeida * applications.
6f90cf607SDaniel W. S. Almeida *
7f90cf607SDaniel W. S. Almeida * This file contains the logic to translate the ES data for one access unit
8f90cf607SDaniel W. S. Almeida * from an encoder into MPEG TS packets. It does so by first encapsulating it
9f90cf607SDaniel W. S. Almeida * with a PES header and then splitting it into TS packets.
10f90cf607SDaniel W. S. Almeida *
11f90cf607SDaniel W. S. Almeida * Copyright (C) 2020 Daniel W. S. Almeida
12f90cf607SDaniel W. S. Almeida */
13f90cf607SDaniel W. S. Almeida
14f90cf607SDaniel W. S. Almeida #define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__
15f90cf607SDaniel W. S. Almeida
16f90cf607SDaniel W. S. Almeida #include <linux/types.h>
17f90cf607SDaniel W. S. Almeida #include <linux/printk.h>
18f90cf607SDaniel W. S. Almeida #include <linux/ratelimit.h>
19f90cf607SDaniel W. S. Almeida
20f90cf607SDaniel W. S. Almeida #include "vidtv_pes.h"
21f90cf607SDaniel W. S. Almeida #include "vidtv_common.h"
22f90cf607SDaniel W. S. Almeida #include "vidtv_encoder.h"
23f90cf607SDaniel W. S. Almeida #include "vidtv_ts.h"
24f90cf607SDaniel W. S. Almeida
25f90cf607SDaniel W. S. Almeida #define PRIVATE_STREAM_1_ID 0xbd /* private_stream_1. See SMPTE 302M-2007 p.6 */
26f90cf607SDaniel W. S. Almeida #define PES_HEADER_MAX_STUFFING_BYTES 32
27f90cf607SDaniel W. S. Almeida #define PES_TS_HEADER_MAX_STUFFING_BYTES 182
28f90cf607SDaniel W. S. Almeida
vidtv_pes_op_get_len(bool send_pts,bool send_dts)29f90cf607SDaniel W. S. Almeida static u32 vidtv_pes_op_get_len(bool send_pts, bool send_dts)
30f90cf607SDaniel W. S. Almeida {
31f90cf607SDaniel W. S. Almeida u32 len = 0;
32f90cf607SDaniel W. S. Almeida
33f90cf607SDaniel W. S. Almeida /* the flags must always be sent */
34f90cf607SDaniel W. S. Almeida len += sizeof(struct vidtv_pes_optional);
35f90cf607SDaniel W. S. Almeida
36f90cf607SDaniel W. S. Almeida /* From all optionals, we might send these for now */
37f90cf607SDaniel W. S. Almeida if (send_pts && send_dts)
38f90cf607SDaniel W. S. Almeida len += sizeof(struct vidtv_pes_optional_pts_dts);
39f90cf607SDaniel W. S. Almeida else if (send_pts)
40f90cf607SDaniel W. S. Almeida len += sizeof(struct vidtv_pes_optional_pts);
41f90cf607SDaniel W. S. Almeida
42f90cf607SDaniel W. S. Almeida return len;
43f90cf607SDaniel W. S. Almeida }
44f90cf607SDaniel W. S. Almeida
45a61d7d19SMauro Carvalho Chehab #define SIZE_PCR (6 + sizeof(struct vidtv_mpeg_ts_adaption))
46a61d7d19SMauro Carvalho Chehab
vidtv_pes_h_get_len(bool send_pts,bool send_dts)47f90cf607SDaniel W. S. Almeida static u32 vidtv_pes_h_get_len(bool send_pts, bool send_dts)
48f90cf607SDaniel W. S. Almeida {
49f90cf607SDaniel W. S. Almeida u32 len = 0;
50f90cf607SDaniel W. S. Almeida
51a61d7d19SMauro Carvalho Chehab /* PES header length notwithstanding stuffing bytes */
52a61d7d19SMauro Carvalho Chehab
53f90cf607SDaniel W. S. Almeida len += sizeof(struct vidtv_mpeg_pes);
54f90cf607SDaniel W. S. Almeida len += vidtv_pes_op_get_len(send_pts, send_dts);
55f90cf607SDaniel W. S. Almeida
56f90cf607SDaniel W. S. Almeida return len;
57f90cf607SDaniel W. S. Almeida }
58f90cf607SDaniel W. S. Almeida
vidtv_pes_write_header_stuffing(struct pes_header_write_args * args)59163d72a2SMauro Carvalho Chehab static u32 vidtv_pes_write_header_stuffing(struct pes_header_write_args *args)
60f90cf607SDaniel W. S. Almeida {
61f90cf607SDaniel W. S. Almeida /*
62f90cf607SDaniel W. S. Almeida * This is a fixed 8-bit value equal to '0xFF' that can be inserted
63f90cf607SDaniel W. S. Almeida * by the encoder, for example to meet the requirements of the channel.
64f90cf607SDaniel W. S. Almeida * It is discarded by the decoder. No more than 32 stuffing bytes shall
65f90cf607SDaniel W. S. Almeida * be present in one PES packet header.
66f90cf607SDaniel W. S. Almeida */
67163d72a2SMauro Carvalho Chehab if (args->n_pes_h_s_bytes > PES_HEADER_MAX_STUFFING_BYTES) {
68f90cf607SDaniel W. S. Almeida pr_warn_ratelimited("More than %d stuffing bytes in PES packet header\n",
69f90cf607SDaniel W. S. Almeida PES_HEADER_MAX_STUFFING_BYTES);
70163d72a2SMauro Carvalho Chehab args->n_pes_h_s_bytes = PES_HEADER_MAX_STUFFING_BYTES;
71f90cf607SDaniel W. S. Almeida }
72f90cf607SDaniel W. S. Almeida
73163d72a2SMauro Carvalho Chehab return vidtv_memset(args->dest_buf,
74163d72a2SMauro Carvalho Chehab args->dest_offset,
75163d72a2SMauro Carvalho Chehab args->dest_buf_sz,
76f90cf607SDaniel W. S. Almeida TS_FILL_BYTE,
77163d72a2SMauro Carvalho Chehab args->n_pes_h_s_bytes);
78f90cf607SDaniel W. S. Almeida }
79f90cf607SDaniel W. S. Almeida
vidtv_pes_write_pts_dts(struct pes_header_write_args * args)80163d72a2SMauro Carvalho Chehab static u32 vidtv_pes_write_pts_dts(struct pes_header_write_args *args)
81f90cf607SDaniel W. S. Almeida {
82f90cf607SDaniel W. S. Almeida u32 nbytes = 0; /* the number of bytes written by this function */
83f90cf607SDaniel W. S. Almeida
84f90cf607SDaniel W. S. Almeida struct vidtv_pes_optional_pts pts = {};
85f90cf607SDaniel W. S. Almeida struct vidtv_pes_optional_pts_dts pts_dts = {};
86f90cf607SDaniel W. S. Almeida void *op = NULL;
87f90cf607SDaniel W. S. Almeida size_t op_sz = 0;
88f90cf607SDaniel W. S. Almeida u64 mask1;
89f90cf607SDaniel W. S. Almeida u64 mask2;
90f90cf607SDaniel W. S. Almeida u64 mask3;
91f90cf607SDaniel W. S. Almeida
92163d72a2SMauro Carvalho Chehab if (!args->send_pts && args->send_dts)
93f90cf607SDaniel W. S. Almeida return 0;
94f90cf607SDaniel W. S. Almeida
952e2fa2c5SMauro Carvalho Chehab mask1 = GENMASK_ULL(32, 30);
962e2fa2c5SMauro Carvalho Chehab mask2 = GENMASK_ULL(29, 15);
972e2fa2c5SMauro Carvalho Chehab mask3 = GENMASK_ULL(14, 0);
98f90cf607SDaniel W. S. Almeida
99f90cf607SDaniel W. S. Almeida /* see ISO/IEC 13818-1 : 2000 p. 32 */
100163d72a2SMauro Carvalho Chehab if (args->send_pts && args->send_dts) {
101163d72a2SMauro Carvalho Chehab pts_dts.pts1 = (0x3 << 4) | ((args->pts & mask1) >> 29) | 0x1;
102163d72a2SMauro Carvalho Chehab pts_dts.pts2 = cpu_to_be16(((args->pts & mask2) >> 14) | 0x1);
103163d72a2SMauro Carvalho Chehab pts_dts.pts3 = cpu_to_be16(((args->pts & mask3) << 1) | 0x1);
104f90cf607SDaniel W. S. Almeida
105163d72a2SMauro Carvalho Chehab pts_dts.dts1 = (0x1 << 4) | ((args->dts & mask1) >> 29) | 0x1;
106163d72a2SMauro Carvalho Chehab pts_dts.dts2 = cpu_to_be16(((args->dts & mask2) >> 14) | 0x1);
107163d72a2SMauro Carvalho Chehab pts_dts.dts3 = cpu_to_be16(((args->dts & mask3) << 1) | 0x1);
108f90cf607SDaniel W. S. Almeida
109f90cf607SDaniel W. S. Almeida op = &pts_dts;
110f90cf607SDaniel W. S. Almeida op_sz = sizeof(pts_dts);
111f90cf607SDaniel W. S. Almeida
112163d72a2SMauro Carvalho Chehab } else if (args->send_pts) {
113163d72a2SMauro Carvalho Chehab pts.pts1 = (0x1 << 5) | ((args->pts & mask1) >> 29) | 0x1;
114163d72a2SMauro Carvalho Chehab pts.pts2 = cpu_to_be16(((args->pts & mask2) >> 14) | 0x1);
115163d72a2SMauro Carvalho Chehab pts.pts3 = cpu_to_be16(((args->pts & mask3) << 1) | 0x1);
116f90cf607SDaniel W. S. Almeida
117f90cf607SDaniel W. S. Almeida op = &pts;
118f90cf607SDaniel W. S. Almeida op_sz = sizeof(pts);
119f90cf607SDaniel W. S. Almeida }
120f90cf607SDaniel W. S. Almeida
121f90cf607SDaniel W. S. Almeida /* copy PTS/DTS optional */
122163d72a2SMauro Carvalho Chehab nbytes += vidtv_memcpy(args->dest_buf,
123163d72a2SMauro Carvalho Chehab args->dest_offset + nbytes,
124163d72a2SMauro Carvalho Chehab args->dest_buf_sz,
125f90cf607SDaniel W. S. Almeida op,
126f90cf607SDaniel W. S. Almeida op_sz);
127f90cf607SDaniel W. S. Almeida
128f90cf607SDaniel W. S. Almeida return nbytes;
129f90cf607SDaniel W. S. Almeida }
130f90cf607SDaniel W. S. Almeida
vidtv_pes_write_h(struct pes_header_write_args * args)131163d72a2SMauro Carvalho Chehab static u32 vidtv_pes_write_h(struct pes_header_write_args *args)
132f90cf607SDaniel W. S. Almeida {
133f90cf607SDaniel W. S. Almeida u32 nbytes = 0; /* the number of bytes written by this function */
134f90cf607SDaniel W. S. Almeida
135f90cf607SDaniel W. S. Almeida struct vidtv_mpeg_pes pes_header = {};
136f90cf607SDaniel W. S. Almeida struct vidtv_pes_optional pes_optional = {};
137163d72a2SMauro Carvalho Chehab struct pes_header_write_args pts_dts_args;
138163d72a2SMauro Carvalho Chehab u32 stream_id = (args->encoder_id == S302M) ? PRIVATE_STREAM_1_ID : args->stream_id;
139bfea1d81SMauro Carvalho Chehab u16 pes_opt_bitfield = 0x01 << 15;
140f90cf607SDaniel W. S. Almeida
141f90cf607SDaniel W. S. Almeida pes_header.bitfield = cpu_to_be32((PES_START_CODE_PREFIX << 8) | stream_id);
142f90cf607SDaniel W. S. Almeida
143163d72a2SMauro Carvalho Chehab pes_header.length = cpu_to_be16(vidtv_pes_op_get_len(args->send_pts,
144163d72a2SMauro Carvalho Chehab args->send_dts) +
145163d72a2SMauro Carvalho Chehab args->access_unit_len);
146f90cf607SDaniel W. S. Almeida
147163d72a2SMauro Carvalho Chehab if (args->send_pts && args->send_dts)
148f90cf607SDaniel W. S. Almeida pes_opt_bitfield |= (0x3 << 6);
149163d72a2SMauro Carvalho Chehab else if (args->send_pts)
150f90cf607SDaniel W. S. Almeida pes_opt_bitfield |= (0x1 << 7);
151f90cf607SDaniel W. S. Almeida
152f90cf607SDaniel W. S. Almeida pes_optional.bitfield = cpu_to_be16(pes_opt_bitfield);
153163d72a2SMauro Carvalho Chehab pes_optional.length = vidtv_pes_op_get_len(args->send_pts, args->send_dts) +
154163d72a2SMauro Carvalho Chehab args->n_pes_h_s_bytes -
155f90cf607SDaniel W. S. Almeida sizeof(struct vidtv_pes_optional);
156f90cf607SDaniel W. S. Almeida
157f90cf607SDaniel W. S. Almeida /* copy header */
158163d72a2SMauro Carvalho Chehab nbytes += vidtv_memcpy(args->dest_buf,
159163d72a2SMauro Carvalho Chehab args->dest_offset + nbytes,
160163d72a2SMauro Carvalho Chehab args->dest_buf_sz,
161f90cf607SDaniel W. S. Almeida &pes_header,
162f90cf607SDaniel W. S. Almeida sizeof(pes_header));
163f90cf607SDaniel W. S. Almeida
164f90cf607SDaniel W. S. Almeida /* copy optional header bits */
165163d72a2SMauro Carvalho Chehab nbytes += vidtv_memcpy(args->dest_buf,
166163d72a2SMauro Carvalho Chehab args->dest_offset + nbytes,
167163d72a2SMauro Carvalho Chehab args->dest_buf_sz,
168f90cf607SDaniel W. S. Almeida &pes_optional,
169f90cf607SDaniel W. S. Almeida sizeof(pes_optional));
170f90cf607SDaniel W. S. Almeida
171f90cf607SDaniel W. S. Almeida /* copy the timing information */
172163d72a2SMauro Carvalho Chehab pts_dts_args = *args;
173163d72a2SMauro Carvalho Chehab pts_dts_args.dest_offset = args->dest_offset + nbytes;
174163d72a2SMauro Carvalho Chehab nbytes += vidtv_pes_write_pts_dts(&pts_dts_args);
175f90cf607SDaniel W. S. Almeida
176f90cf607SDaniel W. S. Almeida /* write any PES header stuffing */
177f90cf607SDaniel W. S. Almeida nbytes += vidtv_pes_write_header_stuffing(args);
178f90cf607SDaniel W. S. Almeida
179f90cf607SDaniel W. S. Almeida return nbytes;
180f90cf607SDaniel W. S. Almeida }
181f90cf607SDaniel W. S. Almeida
vidtv_pes_write_pcr_bits(u8 * to,u32 to_offset,u64 pcr)182a61d7d19SMauro Carvalho Chehab static u32 vidtv_pes_write_pcr_bits(u8 *to, u32 to_offset, u64 pcr)
183f90cf607SDaniel W. S. Almeida {
184a61d7d19SMauro Carvalho Chehab /* Exact same from ffmpeg. PCR is a counter driven by a 27Mhz clock */
185a61d7d19SMauro Carvalho Chehab u64 div;
186a61d7d19SMauro Carvalho Chehab u64 rem;
187a61d7d19SMauro Carvalho Chehab u8 *buf = to + to_offset;
188a61d7d19SMauro Carvalho Chehab u64 pcr_low;
189a61d7d19SMauro Carvalho Chehab u64 pcr_high;
190a61d7d19SMauro Carvalho Chehab
191a61d7d19SMauro Carvalho Chehab div = div64_u64_rem(pcr, 300, &rem);
192a61d7d19SMauro Carvalho Chehab
193a61d7d19SMauro Carvalho Chehab pcr_low = rem; /* pcr_low = pcr % 300 */
194a61d7d19SMauro Carvalho Chehab pcr_high = div; /* pcr_high = pcr / 300 */
195a61d7d19SMauro Carvalho Chehab
196a61d7d19SMauro Carvalho Chehab *buf++ = pcr_high >> 25;
197a61d7d19SMauro Carvalho Chehab *buf++ = pcr_high >> 17;
198a61d7d19SMauro Carvalho Chehab *buf++ = pcr_high >> 9;
199a61d7d19SMauro Carvalho Chehab *buf++ = pcr_high >> 1;
200a61d7d19SMauro Carvalho Chehab *buf++ = pcr_high << 7 | pcr_low >> 8 | 0x7e;
201a61d7d19SMauro Carvalho Chehab *buf++ = pcr_low;
202a61d7d19SMauro Carvalho Chehab
203a61d7d19SMauro Carvalho Chehab return 6;
204a61d7d19SMauro Carvalho Chehab }
205a61d7d19SMauro Carvalho Chehab
vidtv_pes_write_stuffing(struct pes_ts_header_write_args * args,u32 dest_offset,bool need_pcr,u64 * last_pcr)206a61d7d19SMauro Carvalho Chehab static u32 vidtv_pes_write_stuffing(struct pes_ts_header_write_args *args,
207a61d7d19SMauro Carvalho Chehab u32 dest_offset, bool need_pcr,
208a61d7d19SMauro Carvalho Chehab u64 *last_pcr)
209a61d7d19SMauro Carvalho Chehab {
210f90cf607SDaniel W. S. Almeida struct vidtv_mpeg_ts_adaption ts_adap = {};
211a61d7d19SMauro Carvalho Chehab int stuff_nbytes;
212a61d7d19SMauro Carvalho Chehab u32 nbytes = 0;
213f90cf607SDaniel W. S. Almeida
21409196d86SMauro Carvalho Chehab if (!args->n_stuffing_bytes)
215a61d7d19SMauro Carvalho Chehab return 0;
216f90cf607SDaniel W. S. Almeida
217a61d7d19SMauro Carvalho Chehab ts_adap.random_access = 1;
218f90cf607SDaniel W. S. Almeida
219f90cf607SDaniel W. S. Almeida /* length _immediately_ following 'adaptation_field_length' */
220a61d7d19SMauro Carvalho Chehab if (need_pcr) {
221a61d7d19SMauro Carvalho Chehab ts_adap.PCR = 1;
222a61d7d19SMauro Carvalho Chehab ts_adap.length = SIZE_PCR;
223a61d7d19SMauro Carvalho Chehab } else {
224a61d7d19SMauro Carvalho Chehab ts_adap.length = sizeof(ts_adap);
225a61d7d19SMauro Carvalho Chehab }
226a61d7d19SMauro Carvalho Chehab stuff_nbytes = args->n_stuffing_bytes - ts_adap.length;
227a61d7d19SMauro Carvalho Chehab
228a61d7d19SMauro Carvalho Chehab ts_adap.length -= sizeof(ts_adap.length);
229a61d7d19SMauro Carvalho Chehab
230a61d7d19SMauro Carvalho Chehab if (unlikely(stuff_nbytes < 0))
231a61d7d19SMauro Carvalho Chehab stuff_nbytes = 0;
232a61d7d19SMauro Carvalho Chehab
233a61d7d19SMauro Carvalho Chehab ts_adap.length += stuff_nbytes;
234f90cf607SDaniel W. S. Almeida
235f90cf607SDaniel W. S. Almeida /* write the adap after the TS header */
23609196d86SMauro Carvalho Chehab nbytes += vidtv_memcpy(args->dest_buf,
237f90cf607SDaniel W. S. Almeida dest_offset + nbytes,
23809196d86SMauro Carvalho Chehab args->dest_buf_sz,
239f90cf607SDaniel W. S. Almeida &ts_adap,
240f90cf607SDaniel W. S. Almeida sizeof(ts_adap));
241f90cf607SDaniel W. S. Almeida
242a61d7d19SMauro Carvalho Chehab /* write the optional PCR */
243a61d7d19SMauro Carvalho Chehab if (need_pcr) {
244a61d7d19SMauro Carvalho Chehab nbytes += vidtv_pes_write_pcr_bits(args->dest_buf,
245a61d7d19SMauro Carvalho Chehab dest_offset + nbytes,
246a61d7d19SMauro Carvalho Chehab args->pcr);
247a61d7d19SMauro Carvalho Chehab
248a61d7d19SMauro Carvalho Chehab *last_pcr = args->pcr;
249a61d7d19SMauro Carvalho Chehab }
250a61d7d19SMauro Carvalho Chehab
251a61d7d19SMauro Carvalho Chehab /* write the stuffing bytes, if are there anything left */
252a61d7d19SMauro Carvalho Chehab if (stuff_nbytes)
25309196d86SMauro Carvalho Chehab nbytes += vidtv_memset(args->dest_buf,
254f90cf607SDaniel W. S. Almeida dest_offset + nbytes,
25509196d86SMauro Carvalho Chehab args->dest_buf_sz,
256f90cf607SDaniel W. S. Almeida TS_FILL_BYTE,
257f90cf607SDaniel W. S. Almeida stuff_nbytes);
258f90cf607SDaniel W. S. Almeida
259a61d7d19SMauro Carvalho Chehab /*
260a61d7d19SMauro Carvalho Chehab * The n_stuffing_bytes contain a pre-calculated value of
261a61d7d19SMauro Carvalho Chehab * the amount of data that this function would read, made from
262a61d7d19SMauro Carvalho Chehab * vidtv_pes_h_get_len(). If something went wrong, print a warning
263a61d7d19SMauro Carvalho Chehab */
26409196d86SMauro Carvalho Chehab if (nbytes != args->n_stuffing_bytes)
265f90cf607SDaniel W. S. Almeida pr_warn_ratelimited("write size was %d, expected %d\n",
266a61d7d19SMauro Carvalho Chehab nbytes, args->n_stuffing_bytes);
267f90cf607SDaniel W. S. Almeida
268f90cf607SDaniel W. S. Almeida return nbytes;
269f90cf607SDaniel W. S. Almeida }
270f90cf607SDaniel W. S. Almeida
vidtv_pes_write_ts_h(struct pes_ts_header_write_args args,bool need_pcr,u64 * last_pcr)271a61d7d19SMauro Carvalho Chehab static u32 vidtv_pes_write_ts_h(struct pes_ts_header_write_args args,
272a61d7d19SMauro Carvalho Chehab bool need_pcr, u64 *last_pcr)
273f90cf607SDaniel W. S. Almeida {
274f90cf607SDaniel W. S. Almeida /* number of bytes written by this function */
275f90cf607SDaniel W. S. Almeida u32 nbytes = 0;
276f90cf607SDaniel W. S. Almeida struct vidtv_mpeg_ts ts_header = {};
277f90cf607SDaniel W. S. Almeida u16 payload_start = !args.wrote_pes_header;
278f90cf607SDaniel W. S. Almeida
279f90cf607SDaniel W. S. Almeida ts_header.sync_byte = TS_SYNC_BYTE;
280f90cf607SDaniel W. S. Almeida ts_header.bitfield = cpu_to_be16((payload_start << 14) | args.pid);
281f90cf607SDaniel W. S. Almeida ts_header.scrambling = 0;
282f90cf607SDaniel W. S. Almeida ts_header.adaptation_field = (args.n_stuffing_bytes) > 0;
283f90cf607SDaniel W. S. Almeida ts_header.payload = (args.n_stuffing_bytes) < PES_TS_HEADER_MAX_STUFFING_BYTES;
284f90cf607SDaniel W. S. Almeida
285f90cf607SDaniel W. S. Almeida ts_header.continuity_counter = *args.continuity_counter;
286f90cf607SDaniel W. S. Almeida
287f90cf607SDaniel W. S. Almeida vidtv_ts_inc_cc(args.continuity_counter);
288f90cf607SDaniel W. S. Almeida
289f90cf607SDaniel W. S. Almeida /* write the TS header */
290f90cf607SDaniel W. S. Almeida nbytes += vidtv_memcpy(args.dest_buf,
291f90cf607SDaniel W. S. Almeida args.dest_offset + nbytes,
292f90cf607SDaniel W. S. Almeida args.dest_buf_sz,
293f90cf607SDaniel W. S. Almeida &ts_header,
294f90cf607SDaniel W. S. Almeida sizeof(ts_header));
295f90cf607SDaniel W. S. Almeida
296f90cf607SDaniel W. S. Almeida /* write stuffing, if any */
297a61d7d19SMauro Carvalho Chehab nbytes += vidtv_pes_write_stuffing(&args, args.dest_offset + nbytes,
298a61d7d19SMauro Carvalho Chehab need_pcr, last_pcr);
299f90cf607SDaniel W. S. Almeida
300f90cf607SDaniel W. S. Almeida return nbytes;
301f90cf607SDaniel W. S. Almeida }
302f90cf607SDaniel W. S. Almeida
vidtv_pes_write_into(struct pes_write_args * args)303163d72a2SMauro Carvalho Chehab u32 vidtv_pes_write_into(struct pes_write_args *args)
304f90cf607SDaniel W. S. Almeida {
305163d72a2SMauro Carvalho Chehab u32 unaligned_bytes = (args->dest_offset % TS_PACKET_LEN);
306163d72a2SMauro Carvalho Chehab struct pes_ts_header_write_args ts_header_args = {
307163d72a2SMauro Carvalho Chehab .dest_buf = args->dest_buf,
308163d72a2SMauro Carvalho Chehab .dest_buf_sz = args->dest_buf_sz,
309163d72a2SMauro Carvalho Chehab .pid = args->pid,
310163d72a2SMauro Carvalho Chehab .pcr = args->pcr,
311163d72a2SMauro Carvalho Chehab .continuity_counter = args->continuity_counter,
312163d72a2SMauro Carvalho Chehab };
313163d72a2SMauro Carvalho Chehab struct pes_header_write_args pes_header_args = {
314163d72a2SMauro Carvalho Chehab .dest_buf = args->dest_buf,
315163d72a2SMauro Carvalho Chehab .dest_buf_sz = args->dest_buf_sz,
316163d72a2SMauro Carvalho Chehab .encoder_id = args->encoder_id,
317163d72a2SMauro Carvalho Chehab .send_pts = args->send_pts,
318163d72a2SMauro Carvalho Chehab .pts = args->pts,
319163d72a2SMauro Carvalho Chehab .send_dts = args->send_dts,
320163d72a2SMauro Carvalho Chehab .dts = args->dts,
321163d72a2SMauro Carvalho Chehab .stream_id = args->stream_id,
322163d72a2SMauro Carvalho Chehab .n_pes_h_s_bytes = args->n_pes_h_s_bytes,
323163d72a2SMauro Carvalho Chehab .access_unit_len = args->access_unit_len,
324163d72a2SMauro Carvalho Chehab };
325163d72a2SMauro Carvalho Chehab u32 remaining_len = args->access_unit_len;
326f90cf607SDaniel W. S. Almeida bool wrote_pes_header = false;
327163d72a2SMauro Carvalho Chehab u64 last_pcr = args->pcr;
328a61d7d19SMauro Carvalho Chehab bool need_pcr = true;
329a61d7d19SMauro Carvalho Chehab u32 available_space;
330a61d7d19SMauro Carvalho Chehab u32 payload_size;
331a61d7d19SMauro Carvalho Chehab u32 stuff_bytes;
332a61d7d19SMauro Carvalho Chehab u32 nbytes = 0;
333f90cf607SDaniel W. S. Almeida
334a61d7d19SMauro Carvalho Chehab if (unaligned_bytes) {
335a61d7d19SMauro Carvalho Chehab pr_warn_ratelimited("buffer is misaligned, while starting PES\n");
336f90cf607SDaniel W. S. Almeida
337f90cf607SDaniel W. S. Almeida /* forcibly align and hope for the best */
338163d72a2SMauro Carvalho Chehab nbytes += vidtv_memset(args->dest_buf,
339163d72a2SMauro Carvalho Chehab args->dest_offset + nbytes,
340163d72a2SMauro Carvalho Chehab args->dest_buf_sz,
341f90cf607SDaniel W. S. Almeida TS_FILL_BYTE,
342a61d7d19SMauro Carvalho Chehab TS_PACKET_LEN - unaligned_bytes);
343f90cf607SDaniel W. S. Almeida }
344f90cf607SDaniel W. S. Almeida
345f90cf607SDaniel W. S. Almeida while (remaining_len) {
346a61d7d19SMauro Carvalho Chehab available_space = TS_PAYLOAD_LEN;
347f90cf607SDaniel W. S. Almeida /*
348f90cf607SDaniel W. S. Almeida * The amount of space initially available in the TS packet.
349f90cf607SDaniel W. S. Almeida * if this is the beginning of the PES packet, take into account
350f90cf607SDaniel W. S. Almeida * the space needed for the TS header _and_ for the PES header
351f90cf607SDaniel W. S. Almeida */
352a61d7d19SMauro Carvalho Chehab if (!wrote_pes_header)
353163d72a2SMauro Carvalho Chehab available_space -= vidtv_pes_h_get_len(args->send_pts,
354163d72a2SMauro Carvalho Chehab args->send_dts);
355f90cf607SDaniel W. S. Almeida
356a61d7d19SMauro Carvalho Chehab /*
357a61d7d19SMauro Carvalho Chehab * if the encoder has inserted stuffing bytes in the PES
358f90cf607SDaniel W. S. Almeida * header, account for them.
359f90cf607SDaniel W. S. Almeida */
360163d72a2SMauro Carvalho Chehab available_space -= args->n_pes_h_s_bytes;
361f90cf607SDaniel W. S. Almeida
362a61d7d19SMauro Carvalho Chehab /* Take the extra adaptation into account if need to send PCR */
363a61d7d19SMauro Carvalho Chehab if (need_pcr) {
364a61d7d19SMauro Carvalho Chehab available_space -= SIZE_PCR;
365a61d7d19SMauro Carvalho Chehab stuff_bytes = SIZE_PCR;
366a61d7d19SMauro Carvalho Chehab } else {
367a61d7d19SMauro Carvalho Chehab stuff_bytes = 0;
368a61d7d19SMauro Carvalho Chehab }
369f90cf607SDaniel W. S. Almeida
370f90cf607SDaniel W. S. Almeida /*
371f90cf607SDaniel W. S. Almeida * how much of the _actual_ payload should be written in this
372f90cf607SDaniel W. S. Almeida * packet.
373f90cf607SDaniel W. S. Almeida */
374a61d7d19SMauro Carvalho Chehab if (remaining_len >= available_space) {
375a61d7d19SMauro Carvalho Chehab payload_size = available_space;
376a61d7d19SMauro Carvalho Chehab } else {
377a61d7d19SMauro Carvalho Chehab /* Last frame should ensure 188-bytes PS alignment */
378a61d7d19SMauro Carvalho Chehab payload_size = remaining_len;
379a61d7d19SMauro Carvalho Chehab stuff_bytes += available_space - payload_size;
380f90cf607SDaniel W. S. Almeida
381a61d7d19SMauro Carvalho Chehab /*
382a61d7d19SMauro Carvalho Chehab * Ensure that the stuff bytes will be within the
383a61d7d19SMauro Carvalho Chehab * allowed range, decrementing the number of payload
384a61d7d19SMauro Carvalho Chehab * bytes to write if needed.
385a61d7d19SMauro Carvalho Chehab */
386a61d7d19SMauro Carvalho Chehab if (stuff_bytes > PES_TS_HEADER_MAX_STUFFING_BYTES) {
387a61d7d19SMauro Carvalho Chehab u32 tmp = stuff_bytes - PES_TS_HEADER_MAX_STUFFING_BYTES;
388a61d7d19SMauro Carvalho Chehab
389a61d7d19SMauro Carvalho Chehab stuff_bytes = PES_TS_HEADER_MAX_STUFFING_BYTES;
390a61d7d19SMauro Carvalho Chehab payload_size -= tmp;
391a61d7d19SMauro Carvalho Chehab }
392a61d7d19SMauro Carvalho Chehab }
393f90cf607SDaniel W. S. Almeida
394f90cf607SDaniel W. S. Almeida /* write ts header */
395163d72a2SMauro Carvalho Chehab ts_header_args.dest_offset = args->dest_offset + nbytes;
396f90cf607SDaniel W. S. Almeida ts_header_args.wrote_pes_header = wrote_pes_header;
397a61d7d19SMauro Carvalho Chehab ts_header_args.n_stuffing_bytes = stuff_bytes;
398f90cf607SDaniel W. S. Almeida
399a61d7d19SMauro Carvalho Chehab nbytes += vidtv_pes_write_ts_h(ts_header_args, need_pcr,
400a61d7d19SMauro Carvalho Chehab &last_pcr);
401a61d7d19SMauro Carvalho Chehab
402a61d7d19SMauro Carvalho Chehab need_pcr = false;
403f90cf607SDaniel W. S. Almeida
404f90cf607SDaniel W. S. Almeida if (!wrote_pes_header) {
405f90cf607SDaniel W. S. Almeida /* write the PES header only once */
406163d72a2SMauro Carvalho Chehab pes_header_args.dest_offset = args->dest_offset +
407f90cf607SDaniel W. S. Almeida nbytes;
408163d72a2SMauro Carvalho Chehab nbytes += vidtv_pes_write_h(&pes_header_args);
409f90cf607SDaniel W. S. Almeida wrote_pes_header = true;
410f90cf607SDaniel W. S. Almeida }
411f90cf607SDaniel W. S. Almeida
412f90cf607SDaniel W. S. Almeida /* write as much of the payload as we possibly can */
413163d72a2SMauro Carvalho Chehab nbytes += vidtv_memcpy(args->dest_buf,
414163d72a2SMauro Carvalho Chehab args->dest_offset + nbytes,
415163d72a2SMauro Carvalho Chehab args->dest_buf_sz,
416163d72a2SMauro Carvalho Chehab args->from,
417a61d7d19SMauro Carvalho Chehab payload_size);
418f90cf607SDaniel W. S. Almeida
419163d72a2SMauro Carvalho Chehab args->from += payload_size;
420f90cf607SDaniel W. S. Almeida
421a61d7d19SMauro Carvalho Chehab remaining_len -= payload_size;
422f90cf607SDaniel W. S. Almeida }
423f90cf607SDaniel W. S. Almeida
424f90cf607SDaniel W. S. Almeida return nbytes;
425f90cf607SDaniel W. S. Almeida }
426