Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2017-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / tile aggregrator filter
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 :
27 : #include <gpac/filters.h>
28 : #include <gpac/avparse.h>
29 : #include <gpac/constants.h>
30 : #include <gpac/media_tools.h>
31 :
32 :
33 : typedef struct
34 : {
35 : //options
36 : GF_PropUIntList tiledrop;
37 :
38 : //internal
39 : GF_FilterPid *opid;
40 : GF_FilterPid *base_ipid;
41 : u32 nalu_size_length;
42 : u32 base_id;
43 :
44 : GF_BitStream *bs_r;
45 :
46 : u32 flush_packets;
47 : const GF_PropertyValue *sabt;
48 :
49 : Bool check_connections;
50 :
51 : } GF_TileAggCtx;
52 :
53 :
54 137 : static GF_Err tileagg_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
55 : {
56 : u32 codec_id=0;
57 : const GF_PropertyValue *p;
58 : GF_HEVCConfig *hvcc;
59 :
60 137 : GF_TileAggCtx *ctx = (GF_TileAggCtx *) gf_filter_get_udta(filter);
61 :
62 137 : if (is_remove) {
63 20 : if (ctx->base_ipid == pid) {
64 2 : if (ctx->opid) {
65 2 : gf_filter_pid_remove(ctx->opid);
66 2 : ctx->opid = NULL;
67 : }
68 : }
69 : return GF_OK;
70 : }
71 117 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
72 117 : if (!p)
73 : return GF_NOT_SUPPORTED;
74 117 : codec_id = p->value.uint;
75 :
76 : //a single HEVC base is allowed per instance
77 117 : if ((codec_id==GF_CODECID_HEVC) && ctx->base_ipid && (ctx->base_ipid != pid))
78 : return GF_REQUIRES_NEW_INSTANCE;
79 :
80 : //a tile pid connected before our base, check we have the same base ID, otherwise we need a new instance
81 117 : if ((codec_id==GF_CODECID_HEVC) && !ctx->base_ipid && ctx->base_id) {
82 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
83 0 : if (!p)
84 : return GF_NOT_SUPPORTED;
85 :
86 0 : if (ctx->base_id != p->value.uint)
87 : return GF_REQUIRES_NEW_INSTANCE;
88 :
89 0 : ctx->base_ipid = pid;
90 : }
91 : //tile pid connecting after another tile pid, we share the same base
92 117 : if ((codec_id==GF_CODECID_HEVC_TILES) && ctx->base_id) {
93 105 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DEPENDENCY_ID);
94 105 : if (!p) return GF_NOT_SUPPORTED;
95 105 : if (ctx->base_id != p->value.uint)
96 : return GF_REQUIRES_NEW_INSTANCE;
97 : }
98 :
99 :
100 117 : if (!ctx->base_ipid && (codec_id==GF_CODECID_HEVC) ) {
101 7 : ctx->base_ipid = pid;
102 7 : if (!ctx->opid) {
103 7 : ctx->opid = gf_filter_pid_new(filter);
104 : }
105 : }
106 117 : ctx->check_connections = GF_TRUE;
107 :
108 117 : if (ctx->base_ipid == pid) {
109 12 : gf_filter_pid_copy_properties(ctx->opid, ctx->base_ipid);
110 12 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TILE_BASE, NULL);
111 12 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD, NULL);
112 : //we keep the SRDREF property to indicate this was a tiled HEVC reassembled
113 12 : gf_filter_pid_set_property_str(ctx->opid, "isom:sabt", NULL);
114 :
115 12 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
116 : //not ready yet
117 12 : if (!p) return GF_OK;
118 12 : hvcc = gf_odf_hevc_cfg_read(p->value.data.ptr, p->value.data.size, GF_FALSE);
119 12 : ctx->nalu_size_length = hvcc ? hvcc->nal_unit_size : 4;
120 12 : if (hvcc) gf_odf_hevc_cfg_del(hvcc);
121 :
122 12 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
123 12 : if (!p)
124 : return GF_NOT_SUPPORTED;
125 12 : ctx->base_id = p->value.uint;
126 :
127 12 : ctx->sabt = gf_filter_pid_get_property_str(pid, "isom:sabt");
128 : } else {
129 105 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DEPENDENCY_ID);
130 105 : if (!p) return GF_NOT_SUPPORTED;
131 105 : if (!ctx->base_ipid) {
132 0 : ctx->base_id = p->value.uint;
133 : }
134 : //we already checked the same base ID is used
135 : }
136 :
137 : return GF_OK;
138 : }
139 :
140 8 : static GF_Err tileagg_set_eos(GF_Filter *filter, GF_TileAggCtx *ctx)
141 : {
142 8 : u32 i, count = gf_filter_get_ipid_count(filter);
143 77 : for (i=0; i<count; i++) {
144 77 : GF_FilterPid *pid = gf_filter_get_ipid(filter, i);
145 77 : gf_filter_pid_set_discard(pid, GF_TRUE);
146 : }
147 :
148 8 : gf_filter_pid_set_eos(ctx->opid);
149 8 : return GF_EOS;
150 : }
151 :
152 11562 : static GF_Err tileagg_process(GF_Filter *filter)
153 : {
154 11562 : GF_TileAggCtx *ctx = (GF_TileAggCtx *) gf_filter_get_udta(filter);
155 11562 : u32 i, j, count = gf_filter_get_ipid_count(filter);
156 : GF_FilterPacket *dst_pck, *base_pck;
157 : u64 min_cts = GF_FILTER_NO_TS;
158 : u32 pck_size, final_size, size = 0;
159 : u32 pos, nb_ready=0;
160 : u32 sabt_idx;
161 : Bool has_sei_suffix = GF_FALSE;
162 : const char *data;
163 : u8 *output;
164 11562 : if (!ctx->base_ipid) return GF_EOS;
165 :
166 11562 : if (ctx->check_connections) {
167 57 : if (gf_filter_connections_pending(filter))
168 : return GF_OK;
169 57 : ctx->check_connections = GF_FALSE;
170 : }
171 :
172 11562 : base_pck = gf_filter_pid_get_packet(ctx->base_ipid);
173 11562 : if (!base_pck) {
174 310 : ctx->flush_packets = 0;
175 310 : if (gf_filter_pid_is_eos(ctx->base_ipid)) {
176 8 : return tileagg_set_eos(filter, ctx);
177 : }
178 : return GF_OK;
179 : }
180 11252 : min_cts = gf_filter_pck_get_cts(base_pck);
181 11252 : gf_filter_pck_get_data(base_pck, &pck_size);
182 11252 : size = pck_size;
183 :
184 86084 : for (i=0; i<count; i++) {
185 : GF_FilterPacket *pck;
186 : u64 cts;
187 : Bool do_drop=GF_FALSE;
188 82095 : GF_FilterPid *pid = gf_filter_get_ipid(filter, i);
189 82095 : if (pid==ctx->base_ipid) continue;
190 : while (1) {
191 0 : pck = gf_filter_pid_get_packet(pid);
192 70843 : if (!pck) {
193 7263 : if (gf_filter_pid_is_eos(pid)) {
194 0 : return tileagg_set_eos(filter, ctx);
195 : }
196 : //if we are flushing a segment, consider the PID discarded if no packet
197 : //otherwise wait for packet
198 7263 : if (! ctx->flush_packets)
199 : return GF_OK;
200 : break;
201 : }
202 :
203 63580 : cts = gf_filter_pck_get_cts(pck);
204 63580 : if (cts < min_cts) {
205 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[TileAgg] Tiled pid %s with cts "LLU" less than base tile pid cts "LLU" - discarding packet\n", gf_filter_pid_get_name(pid), cts, min_cts ));
206 0 : gf_filter_pid_drop_packet(pid);
207 : } else {
208 : break;
209 : }
210 : }
211 :
212 63580 : if (!pck) continue;
213 :
214 63580 : if (cts > min_cts) continue;
215 :
216 0 : for (j=0; j<ctx->tiledrop.nb_items; j++) {
217 0 : if (ctx->tiledrop.vals[j] == i)
218 : do_drop=GF_TRUE;
219 : }
220 63580 : if (do_drop) {
221 0 : gf_filter_pid_drop_packet(pid);
222 0 : continue;
223 : }
224 :
225 63580 : gf_filter_pck_get_data(pck, &pck_size);
226 63580 : size += pck_size;
227 63580 : nb_ready++;
228 : }
229 :
230 3989 : GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[TileAgg] reaggregating CTS "LLU" %d ready %d pids (nb flush pck %d)\n", min_cts, nb_ready+1, count, ctx->flush_packets));
231 3989 : if (ctx->flush_packets)
232 0 : ctx->flush_packets--;
233 :
234 3989 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
235 3989 : if (!dst_pck) return GF_OUT_OF_MEM;
236 :
237 : final_size = size;
238 :
239 3989 : gf_filter_pck_merge_properties(base_pck, dst_pck);
240 3989 : data = gf_filter_pck_get_data(base_pck, &pck_size);
241 :
242 : //copy all NAL from base except SEI suffixes
243 3989 : gf_bs_reassign_buffer(ctx->bs_r, data, pck_size);
244 : pos = 0;
245 : size = 0;
246 11974 : while (pos<pck_size) {
247 3996 : u32 nal_size = gf_bs_read_int(ctx->bs_r, 8*ctx->nalu_size_length);
248 : u8 nal_type;
249 3996 : gf_bs_read_int(ctx->bs_r, 1);
250 3996 : nal_type = gf_bs_read_int(ctx->bs_r, 6);
251 3996 : gf_bs_read_int(ctx->bs_r, 1);
252 3996 : gf_bs_skip_bytes(ctx->bs_r, nal_size-1);
253 3996 : if (nal_type == GF_HEVC_NALU_SEI_SUFFIX) {
254 : has_sei_suffix = GF_TRUE;
255 : } else {
256 7 : memcpy(output+size, data+pos, nal_size + ctx->nalu_size_length);
257 7 : size += nal_size + ctx->nalu_size_length;
258 : }
259 3996 : pos += nal_size + ctx->nalu_size_length;
260 : }
261 :
262 : sabt_idx = 0;
263 : while (1) {
264 : u32 pid_id = 0;
265 37640 : if (ctx->sabt) {
266 37640 : if (sabt_idx >= ctx->sabt->value.uint_list.nb_items)
267 : break;
268 33651 : pid_id = ctx->sabt->value.uint_list.vals[sabt_idx];
269 33651 : sabt_idx++;
270 : }
271 195156 : for (i=0; i<count; i++) {
272 : u64 cts;
273 : GF_FilterPacket *pck;
274 195156 : GF_FilterPid *pid = gf_filter_get_ipid(filter, i);
275 195156 : if (pid==ctx->base_ipid) continue;
276 161505 : if (pid_id) {
277 161505 : const GF_PropertyValue *p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
278 161505 : if (!p || (p->value.uint != pid_id))
279 127854 : continue;
280 : }
281 33651 : pck = gf_filter_pid_get_packet(pid);
282 : //can happen if we drop one tile
283 33651 : if (!pck) continue;
284 :
285 33651 : cts = gf_filter_pck_get_cts(pck);
286 33651 : if (cts != min_cts) continue;
287 :
288 33651 : data = gf_filter_pck_get_data(pck, &pck_size);
289 33651 : memcpy(output+size, data, pck_size);
290 33651 : size += pck_size;
291 :
292 33651 : gf_filter_pid_drop_packet(pid);
293 33651 : if (pid_id)
294 : break;
295 : }
296 33651 : if (!pid_id)
297 : break;
298 : }
299 :
300 : //append all SEI suffixes
301 3989 : if (has_sei_suffix) {
302 3989 : data = gf_filter_pck_get_data(base_pck, &pck_size);
303 3989 : gf_bs_reassign_buffer(ctx->bs_r, data, pck_size);
304 :
305 : pos = 0;
306 11974 : while (pos<pck_size) {
307 3996 : u32 nal_size = gf_bs_read_int(ctx->bs_r, 8*ctx->nalu_size_length);
308 : u8 nal_type;
309 3996 : gf_bs_read_int(ctx->bs_r, 1);
310 3996 : nal_type = gf_bs_read_int(ctx->bs_r, 6);
311 3996 : gf_bs_read_int(ctx->bs_r, 1);
312 3996 : gf_bs_skip_bytes(ctx->bs_r, nal_size-1);
313 3996 : if (nal_type == GF_HEVC_NALU_SEI_SUFFIX) {
314 3989 : memcpy(output+size, data+pos, nal_size + ctx->nalu_size_length);
315 3989 : size += nal_size + ctx->nalu_size_length;
316 : }
317 3996 : pos += nal_size + ctx->nalu_size_length;
318 : }
319 : }
320 :
321 3989 : gf_filter_pid_drop_packet(ctx->base_ipid);
322 :
323 3989 : if (size < final_size)
324 0 : gf_filter_pck_truncate(dst_pck, size);
325 :
326 3989 : gf_filter_pck_send(dst_pck);
327 3989 : return GF_OK;
328 : }
329 :
330 7 : static GF_Err tileagg_initialize(GF_Filter *filter)
331 : {
332 7 : GF_TileAggCtx *ctx = (GF_TileAggCtx *) gf_filter_get_udta(filter);
333 7 : ctx->bs_r = gf_bs_new((char *)ctx, 1, GF_BITSTREAM_READ);
334 :
335 7 : return GF_OK;
336 : }
337 :
338 7 : static void tileagg_finalize(GF_Filter *filter)
339 : {
340 7 : GF_TileAggCtx *ctx = (GF_TileAggCtx *) gf_filter_get_udta(filter);
341 7 : gf_bs_del(ctx->bs_r);
342 7 : }
343 :
344 33221 : static Bool tileagg_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
345 : {
346 33221 : GF_TileAggCtx *ctx = (GF_TileAggCtx *) gf_filter_get_udta(filter);
347 33221 : if (evt->base.type != GF_FEVT_PLAY_HINT) return GF_FALSE;
348 99 : if (evt->play.forced_dash_segment_switch) {
349 : //this assumes the dashin module performs regulation of output in case of losses
350 : //otherwise it may dispatch more than one segment in the input buffer
351 99 : if (!ctx->flush_packets)
352 99 : gf_filter_pid_get_buffer_occupancy(ctx->base_ipid, NULL, &ctx->flush_packets, NULL, NULL);
353 : else {
354 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[TileAgg] Something is wrong in demuxer, received segment flush event but previous segment is not yet flushed !\n" ));
355 : }
356 : }
357 : return GF_TRUE;
358 : }
359 :
360 : static const GF_FilterCapability TileAggCaps[] =
361 : {
362 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
363 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
364 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_HEVC),
365 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_TILE_BASE, GF_TRUE),
366 :
367 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
368 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_CODECID, GF_CODECID_HEVC),
369 : {0},
370 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
371 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
372 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_HEVC_TILES),
373 : };
374 :
375 : #define OFFS(_n) #_n, offsetof(GF_TileAggCtx, _n)
376 :
377 : static const GF_FilterArgs TileAggArgs[] =
378 : {
379 : { OFFS(tiledrop), "specify indexes of tiles to drop", GF_PROP_UINT_LIST, "", NULL, GF_FS_ARG_UPDATE},
380 : {0}
381 : };
382 :
383 : GF_FilterRegister TileAggRegister = {
384 : .name = "tileagg",
385 : GF_FS_SET_DESCRIPTION("HEVC tile aggregator")
386 : GF_FS_SET_HELP("This filter reaggregates a set of split tiled HEVC streams (`hvt1` or `hvt2` in isobmff) into a single HEVC stream.")
387 : .private_size = sizeof(GF_TileAggCtx),
388 : SETCAPS(TileAggCaps),
389 : .initialize = tileagg_initialize,
390 : .finalize = tileagg_finalize,
391 : .args = TileAggArgs,
392 : .configure_pid = tileagg_configure_pid,
393 : .process = tileagg_process,
394 : .process_event = tileagg_process_event,
395 : .max_extra_pids = (u32) (-1),
396 : };
397 :
398 2877 : const GF_FilterRegister *tileagg_register(GF_FilterSession *session)
399 : {
400 2877 : return &TileAggRegister;
401 : }
402 :
403 :
|