1 /*
2 * camutils.c -
3 * Copyright (C) 2007 Alessandro Decina
4 *
5 * Authors:
6 * Alessandro Decina <alessandro.d@gmail.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include <gst/gst.h>
25 #include <string.h>
26
27 #include "cam.h"
28 #include "camutils.h"
29
30 #define GST_CAT_DEFAULT cam_debug_cat
31
32 /* From the spec:
33 * length_field() {
34 * size_indicator
35 * if (size_indicator == 0)
36 * length_value
37 * else if (size_indicator == 1) {
38 * length_field_size
39 * for (i=0; i<length_field_size; i++) {
40 * length_value_byte
41 * }
42 * }
43 * }
44 */
45
46 guint8
cam_calc_length_field_size(guint length)47 cam_calc_length_field_size (guint length)
48 {
49 guint field_len;
50
51 if (length < G_MAXUINT8)
52 field_len = 1;
53 else if (length <= G_MAXUINT16)
54 field_len = 3;
55 else if (length <= (1 << 24) - 1)
56 field_len = 4;
57 else
58 field_len = 5;
59
60 return field_len;
61 }
62
63 /* write a length_field */
64 guint8
cam_write_length_field(guint8 * buff,guint length)65 cam_write_length_field (guint8 * buff, guint length)
66 {
67 guint8 field_len = cam_calc_length_field_size (length);
68
69 if (buff) {
70 switch (field_len) {
71 case 1:
72 buff[0] = length;
73 break;
74 case 2:
75 g_return_val_if_reached (0);
76 break;
77 case 3:
78 buff[0] = TPDU_HEADER_SIZE_INDICATOR | (field_len - 1);
79 buff[1] = length >> 8;
80 buff[2] = length & 0xFF;
81 break;
82 case 4:
83 buff[0] = TPDU_HEADER_SIZE_INDICATOR | (field_len - 1);
84 buff[1] = length >> 16;
85 buff[2] = (length >> 8) & 0xFF;
86 buff[3] = length & 0xFF;
87 break;
88 case 5:
89 buff[0] = TPDU_HEADER_SIZE_INDICATOR | (field_len - 1);
90 buff[1] = length >> 24;
91 buff[2] = (length >> 16) & 0xFF;
92 buff[3] = (length >> 8) & 0xFF;
93 buff[4] = length & 0xFF;
94 break;
95 default:
96 g_return_val_if_reached (0);
97 }
98 }
99
100 return field_len;
101 }
102
103 /* read a length_field */
104 guint8
cam_read_length_field(guint8 * buff,guint * length)105 cam_read_length_field (guint8 * buff, guint * length)
106 {
107 guint i;
108 guint field_len;
109 guint8 len;
110
111 if (buff[0] <= G_MAXINT8) {
112 field_len = 1;
113 len = buff[0];
114 } else {
115 field_len = buff[0] & ~TPDU_HEADER_SIZE_INDICATOR;
116 if (field_len > 4) {
117 GST_ERROR ("length_field length exceeds 4 bytes: %d", field_len);
118 field_len = 0;
119 len = 0;
120 } else {
121 len = 0;
122 for (i = 0; i < field_len; ++i)
123 len = (len << 8) | *++buff;
124
125 /* count the size indicator byte */
126 field_len += 1;
127 }
128 }
129
130 if (length)
131 *length = len;
132
133 return field_len;
134 }
135
136 /*
137 * ca_pmt () {
138 * ca_pmt_tag 24 uimsbf
139 * length_field()
140 * ca_pmt_list_management 8 uimsbf
141 * program_number 16 uimsbf
142 * reserved 2 bslbf
143 * version_number 5 uimsbf
144 * current_next_indicator 1 bslbf
145 * reserved 4 bslbf
146 * program_info_length 12 uimsbf
147 * if (program_info_length != 0) {
148 * ca_pmt_cmd_id at program level 8 uimsbf
149 * for (i=0; i<n; i++) {
150 * CA_descriptor() programme level
151 * }
152 * }
153 * for (i=0; i<n; i++) {
154 * stream_type 8 uimsbf
155 * reserved 3 bslbf
156 * elementary_PID elementary stream PID 13 uimsbf
157 * reserved 4 bslbf
158 * ES_info_length 12 uimsbf
159 * if (ES_info_length != 0) {
160 * ca_pmt_cmd_id at ES level 8 uimsbf
161 * for (i=0; i<n; i++) {
162 * CA_descriptor() elementary stream level
163 * }
164 * }
165 * }
166 * }
167 */
168
169 static guint
get_ca_descriptors_length(GPtrArray * descriptors)170 get_ca_descriptors_length (GPtrArray * descriptors)
171 {
172 guint i;
173 guint nb_desc = descriptors->len;
174 guint len = 0;
175
176 for (i = 0; i < nb_desc; i++) {
177 GstMpegtsDescriptor *desc = g_ptr_array_index (descriptors, i);
178 if (desc->tag == 0x09)
179 len += desc->length;
180 }
181
182 return len;
183 }
184
185 static guint8 *
write_ca_descriptors(guint8 * body,GPtrArray * descriptors)186 write_ca_descriptors (guint8 * body, GPtrArray * descriptors)
187 {
188 guint i, nb_desc;
189
190 nb_desc = descriptors->len;
191 for (i = 0; i < nb_desc; i++) {
192 GstMpegtsDescriptor *desc = g_ptr_array_index (descriptors, i);
193 if (desc->tag == 0x09) {
194 memcpy (body, desc->data, desc->length);
195 body += desc->length;
196 }
197 }
198
199 return body;
200 }
201
202 guint8 *
cam_build_ca_pmt(GstMpegtsPMT * pmt,guint8 list_management,guint8 cmd_id,guint * size)203 cam_build_ca_pmt (GstMpegtsPMT * pmt, guint8 list_management, guint8 cmd_id,
204 guint * size)
205 {
206 GstMpegtsSection *section = (GstMpegtsSection *) pmt;
207 guint body_size = 0;
208 guint8 *buffer;
209 guint8 *body;
210 GList *lengths = NULL;
211 guint len = 0;
212 guint i;
213
214 /* get the length of program level CA_descriptor()s */
215 len = get_ca_descriptors_length (pmt->descriptors);
216 if (len > 0)
217 /* add one byte for the program level cmd_id */
218 len += 1;
219
220 lengths = g_list_append (lengths, GINT_TO_POINTER (len));
221 body_size += 6 + len;
222
223 for (i = 0; i < pmt->streams->len; i++) {
224 GstMpegtsPMTStream *pmtstream = g_ptr_array_index (pmt->streams, i);
225
226 len = get_ca_descriptors_length (pmtstream->descriptors);
227 if (len > 0)
228 /* one byte for the stream level cmd_id */
229 len += 1;
230
231 lengths = g_list_append (lengths, GINT_TO_POINTER (len));
232 body_size += 5 + len;
233 }
234
235 GST_DEBUG ("Body Size %d", body_size);
236
237 buffer = g_malloc0 (body_size);
238 body = buffer;
239
240 /* ca_pmt_list_management 8 uimsbf */
241 *body++ = list_management;
242
243 /* program_number 16 uimsbf */
244 GST_WRITE_UINT16_BE (body, section->subtable_extension);
245 body += 2;
246
247 /* reserved 2
248 * version_number 5
249 * current_next_indicator 1
250 */
251 *body++ = (section->version_number << 1) | 0x01;
252
253 /* Reserved 4
254 * program_info_length 12
255 */
256 len = GPOINTER_TO_INT (lengths->data);
257 lengths = g_list_delete_link (lengths, lengths);
258 GST_WRITE_UINT16_BE (body, len);
259 body += 2;
260
261 if (len != 0) {
262 *body++ = cmd_id;
263
264 body = write_ca_descriptors (body, pmt->descriptors);
265 }
266
267 for (i = 0; i < pmt->streams->len; i++) {
268 GstMpegtsPMTStream *pmtstream = g_ptr_array_index (pmt->streams, i);
269
270 *body++ = pmtstream->stream_type;
271 GST_WRITE_UINT16_BE (body, pmtstream->pid);
272 body += 2;
273 len = GPOINTER_TO_INT (lengths->data);
274 lengths = g_list_delete_link (lengths, lengths);
275 GST_WRITE_UINT16_BE (body, len);
276 body += 2;
277
278 if (len != 0) {
279 *body++ = cmd_id;
280 body = write_ca_descriptors (body, pmtstream->descriptors);
281 }
282 }
283
284 *size = body_size;
285 return buffer;
286 }
287