Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2020-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / tile splitting 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/internal/media_dev.h>
31 :
32 : #if !defined(GPAC_DISABLE_HEVC) && !defined(GPAC_DISABLE_AV_PARSERS)
33 :
34 : typedef struct
35 : {
36 : GF_FilterPid *opid;
37 : u32 x, y, w, h;
38 : GF_BitStream *pck_bs;
39 : u8 *pck_buf;
40 : u32 pck_buf_alloc;
41 : Bool all_intra;
42 : } TileSplitPid;
43 :
44 :
45 : typedef struct
46 : {
47 : //options
48 : GF_PropUIntList tiledrop;
49 :
50 : //internal
51 : GF_FilterPid *ipid;
52 : GF_FilterPid *base_opid;
53 : u32 base_id;
54 :
55 : u32 nb_tiles, nb_alloc_tiles;
56 : TileSplitPid *opids;
57 :
58 : HEVCState hevc;
59 : u32 nalu_size_length;
60 :
61 : Bool filter_disabled, passthrough;
62 : s32 cur_pps_idx;
63 :
64 : u32 width, height;
65 :
66 : GF_BitStream *pck_bs;
67 : u8 *pck_buf;
68 : u32 pck_buf_alloc;
69 : } GF_TileSplitCtx;
70 :
71 24 : static void tilesplit_update_pid_props(GF_TileSplitCtx *ctx, TileSplitPid *tinfo)
72 : {
73 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_WIDTH, &PROP_UINT(tinfo->w) );
74 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(tinfo->h) );
75 :
76 : #if 0
77 : GF_PropertyValue pval;
78 : pval.type = GF_PROP_VEC2I;
79 : pval.value.vec2i.x = ctx->width;
80 : pval.value.vec2i.y = ctx->height;
81 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_SRD_REF, &pval);
82 :
83 : pval.type = GF_PROP_VEC4I;
84 : pval.value.vec4i.x = tinfo->x;
85 : pval.value.vec4i.y = tinfo->y;
86 : pval.value.vec4i.z = tinfo->w;
87 : pval.value.vec4i.w = tinfo->h;
88 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_SRD, &pval);
89 : #else
90 :
91 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_CROP_POS, &PROP_VEC2I_INT(tinfo->x, tinfo->y) );
92 :
93 : #endif
94 24 : }
95 :
96 3 : static GF_Err tilesplit_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
97 : {
98 : GF_PropertyValue pval;
99 : const GF_PropertyValue *p;
100 : GF_HEVCConfig *hvcc;
101 : GF_HEVCConfig tile_cfg;
102 : HEVC_PPS *pps;
103 : HEVC_SPS *sps;
104 : u8 *dsi;
105 : u32 dsi_size, bitrate;
106 : u32 i, j, count, nb_tiles, PicWidthInCtbsY, PicHeightInCtbsY, tile_y, active_tiles;
107 : s32 pps_idx=-1, sps_idx=-1;
108 3 : GF_TileSplitCtx *ctx = (GF_TileSplitCtx *) gf_filter_get_udta(filter);
109 :
110 3 : if (is_remove) {
111 0 : if (ctx->ipid == pid) {
112 0 : for (i=0; i<ctx->nb_tiles; i++) {
113 0 : if (ctx->opids[i].opid) {
114 0 : gf_filter_pid_remove(ctx->opids[i].opid);
115 0 : ctx->opids[i].opid = NULL;
116 : }
117 : }
118 0 : ctx->nb_tiles = 0;
119 0 : ctx->ipid = NULL;
120 : }
121 : return GF_OK;
122 : }
123 :
124 3 : if (!ctx->ipid) {
125 3 : ctx->ipid = pid;
126 3 : ctx->base_opid = gf_filter_pid_new(filter);
127 3 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
128 : }
129 3 : gf_filter_pid_copy_properties(ctx->base_opid, pid);
130 : //set SABT to true by default for link resolution
131 3 : gf_filter_pid_set_property(ctx->base_opid, GF_PROP_PID_TILE_BASE, &PROP_BOOL(GF_TRUE) );
132 :
133 3 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
134 3 : if (!p) return GF_OK;
135 :
136 3 : hvcc = gf_odf_hevc_cfg_read(p->value.data.ptr, p->value.data.size, GF_FALSE);
137 3 : if (!hvcc) return GF_NON_COMPLIANT_BITSTREAM;
138 :
139 3 : ctx->nalu_size_length = hvcc->nal_unit_size;
140 3 : memset(&ctx->hevc, 0, sizeof(HEVCState));
141 :
142 3 : count = gf_list_count(hvcc->param_array);
143 12 : for (i=0; i<count; i++) {
144 9 : GF_NALUFFParamArray *ar = gf_list_get(hvcc->param_array, i);
145 18 : for (j=0; j < gf_list_count(ar->nalus); j++) {
146 9 : GF_NALUFFParam *sl = gf_list_get(ar->nalus, j);
147 9 : if (!sl) continue;
148 9 : switch (ar->type) {
149 3 : case GF_HEVC_NALU_PIC_PARAM:
150 3 : pps_idx = gf_hevc_read_pps(sl->data, sl->size, &ctx->hevc);
151 3 : break;
152 3 : case GF_HEVC_NALU_SEQ_PARAM:
153 3 : sps_idx = gf_hevc_read_sps(sl->data, sl->size, &ctx->hevc);
154 3 : break;
155 3 : case GF_HEVC_NALU_VID_PARAM:
156 3 : gf_hevc_read_vps(sl->data, sl->size, &ctx->hevc);
157 3 : break;
158 : }
159 : }
160 : }
161 3 : if (pps_idx==-1) return GF_NON_COMPLIANT_BITSTREAM;
162 3 : if (sps_idx==-1) return GF_NON_COMPLIANT_BITSTREAM;
163 :
164 3 : if (ctx->hevc.pps[pps_idx].loop_filter_across_tiles_enabled_flag)
165 0 : ctx->filter_disabled = GF_FALSE;
166 : else
167 3 : ctx->filter_disabled = GF_TRUE;
168 :
169 3 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
170 3 : if (p) ctx->base_id = p->value.uint;
171 :
172 3 : if (! ctx->hevc.pps[pps_idx].tiles_enabled_flag) {
173 0 : gf_odf_hevc_cfg_del(hvcc);
174 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[TileSplit] Tiles not enabled, using passthrough\n"));
175 0 : gf_filter_pid_set_property(ctx->base_opid, GF_PROP_PID_TILE_BASE, NULL);
176 0 : ctx->passthrough = GF_TRUE;
177 0 : return GF_OK;
178 : }
179 3 : ctx->passthrough = GF_FALSE;
180 3 : nb_tiles = ctx->hevc.pps[pps_idx].num_tile_columns * ctx->hevc.pps[pps_idx].num_tile_rows;
181 :
182 6 : while (nb_tiles < ctx->nb_tiles) {
183 0 : ctx->nb_tiles--;
184 0 : if (ctx->opids[ctx->nb_tiles].opid) {
185 0 : gf_filter_pid_remove(ctx->opids[ctx->nb_tiles].opid);
186 0 : ctx->opids[ctx->nb_tiles].opid = NULL;
187 : }
188 : }
189 :
190 3 : if (nb_tiles>ctx->nb_alloc_tiles) {
191 3 : ctx->opids = gf_realloc(ctx->opids, sizeof(TileSplitPid) * nb_tiles);
192 3 : memset(&ctx->opids[ctx->nb_alloc_tiles], 0, sizeof(TileSplitPid) * (nb_tiles-ctx->nb_alloc_tiles) );
193 3 : ctx->nb_alloc_tiles = nb_tiles;
194 : }
195 :
196 : /*setup tile info*/
197 3 : ctx->cur_pps_idx = pps_idx;
198 : pps = &ctx->hevc.pps[pps_idx];
199 : sps = &ctx->hevc.sps[sps_idx];
200 :
201 3 : ctx->width = sps->width;
202 3 : ctx->height = sps->height;
203 :
204 3 : PicWidthInCtbsY = sps->width / sps->max_CU_width;
205 3 : if (PicWidthInCtbsY * sps->max_CU_width < sps->width) PicWidthInCtbsY++;
206 3 : PicHeightInCtbsY = sps->height / sps->max_CU_width;
207 3 : if (PicHeightInCtbsY * sps->max_CU_width < sps->height) PicHeightInCtbsY++;
208 :
209 : //setup grid info before sending data
210 : //we fill the grid in order of the tiling
211 : tile_y = 0;
212 12 : for (i=0; i<pps->num_tile_rows; i++) {
213 : u32 tile_x = 0;
214 : u32 tile_height;
215 9 : if (pps->uniform_spacing_flag) {
216 9 : tile_height = (i+1)*PicHeightInCtbsY / pps->num_tile_rows - i * PicHeightInCtbsY / pps->num_tile_rows;
217 : } else {
218 0 : if (i < pps->num_tile_rows-1) {
219 0 : tile_height = pps->row_height[i];
220 0 : } else if (i) {
221 0 : tile_height = (PicHeightInCtbsY - pps->row_height[i-1]);
222 : } else {
223 : tile_height = PicHeightInCtbsY;
224 : }
225 : }
226 :
227 36 : for (j=0; j < pps->num_tile_columns; j++) {
228 : TileSplitPid *tinfo;
229 : u32 tile_width;
230 :
231 27 : if (pps->uniform_spacing_flag) {
232 27 : tile_width = (j+1) * PicWidthInCtbsY / pps->num_tile_columns - j * PicWidthInCtbsY / pps->num_tile_columns;
233 : } else {
234 0 : if (j<pps->num_tile_columns-1) {
235 0 : tile_width = pps->column_width[j];
236 : } else {
237 0 : tile_width = (PicWidthInCtbsY - pps->column_width[j-1]);
238 : }
239 : }
240 :
241 27 : tinfo = &ctx->opids[i * pps->num_tile_columns + j];
242 27 : tinfo->x = tile_x * sps->max_CU_width;
243 27 : tinfo->w = tile_width * sps->max_CU_width;
244 27 : tinfo->y = tile_y * sps->max_CU_width;
245 27 : tinfo->h = tile_height * sps->max_CU_width;
246 27 : tinfo->all_intra = GF_TRUE;
247 :
248 27 : if (tinfo->x + tinfo->w > sps->width)
249 0 : tinfo->w = sps->width - tinfo->x;
250 27 : if (tinfo->y + tinfo->h > sps->height)
251 9 : tinfo->h = sps->height - tinfo->y;
252 :
253 27 : tile_x += tile_width;
254 : }
255 9 : tile_y += tile_height;
256 : }
257 :
258 3 : pval.type = GF_PROP_VEC2I;
259 3 : pval.value.vec2i.x = ctx->width;
260 3 : pval.value.vec2i.y = ctx->height;
261 :
262 : memcpy(&tile_cfg, hvcc, sizeof(GF_HEVCConfig));
263 3 : tile_cfg.param_array = NULL;
264 3 : gf_odf_hevc_cfg_write(&tile_cfg, &dsi, &dsi_size);
265 :
266 3 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_BITRATE);
267 : bitrate = 0;
268 3 : if (p && (p->value.uint>10000)) {
269 1 : bitrate = p->value.uint - 10000;
270 1 : bitrate /= nb_tiles-ctx->tiledrop.nb_items;
271 : }
272 :
273 3 : const char *pname = gf_filter_pid_get_name(ctx->ipid);
274 3 : if (pname) pname = gf_file_basename(pname);
275 3 : if (!pname) pname = "video";
276 :
277 :
278 30 : for (i=ctx->nb_tiles; i<nb_tiles; i++) {
279 : char szName[GF_MAX_PATH];
280 27 : TileSplitPid *tinfo = &ctx->opids[i];
281 27 : if (!tinfo->opid) {
282 : Bool drop = GF_FALSE;
283 21 : for (j=0; j<ctx->tiledrop.nb_items; j++) {
284 24 : if (ctx->tiledrop.vals[j]==i) {
285 : drop = GF_TRUE;
286 : break;
287 : }
288 :
289 : }
290 27 : if (!drop)
291 24 : tinfo->opid = gf_filter_pid_new(filter);
292 : }
293 :
294 27 : if (!tinfo->opid) continue;
295 :
296 24 : gf_filter_pid_copy_properties(tinfo->opid, pid);
297 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_HEVC_TILES) );
298 24 : if (dsi)
299 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_DECODER_CONFIG, &PROP_DATA(dsi, dsi_size) );
300 : else
301 0 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_DECODER_CONFIG, NULL);
302 :
303 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_BITRATE, bitrate ? &PROP_UINT(bitrate) : NULL);
304 :
305 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_ID, &PROP_UINT(ctx->base_id + i + 1 ) );
306 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_TILE_ID, &PROP_UINT(i + 1 ) );
307 24 : gf_filter_pid_set_property(tinfo->opid, GF_PROP_PID_DEPENDENCY_ID, &PROP_UINT(ctx->base_id) );
308 24 : tilesplit_update_pid_props(ctx, tinfo);
309 :
310 : sprintf(szName, "%s_tile%d", pname, i+1);
311 24 : gf_filter_pid_set_name(tinfo->opid, szName);
312 : }
313 3 : if (dsi) gf_free(dsi);
314 3 : ctx->nb_tiles = nb_tiles;
315 : active_tiles = 0;
316 : //setup tbas track ref
317 30 : for (i=0; i<nb_tiles; i++) {
318 27 : if (!ctx->opids[i].opid)
319 3 : continue;
320 24 : pval.type = GF_PROP_4CC_LIST;
321 24 : pval.value.uint_list.nb_items = 1;
322 24 : pval.value.uint_list.vals = &ctx->base_id;
323 24 : active_tiles ++;
324 24 : gf_filter_pid_set_property_str(ctx->opids[i].opid, "isom:tbas", &pval);
325 : }
326 : //setup sabt track ref
327 3 : pval.type = GF_PROP_4CC_LIST;
328 3 : pval.value.uint_list.nb_items = active_tiles;
329 3 : pval.value.uint_list.vals = gf_malloc(sizeof(u32) * active_tiles);
330 : active_tiles = 0;
331 30 : for (i=0; i<nb_tiles; i++) {
332 27 : if (!ctx->opids[i].opid)
333 3 : continue;
334 24 : pval.value.uint_list.vals[active_tiles] = ctx->base_id + i + 1;
335 24 : active_tiles++;
336 : }
337 3 : gf_filter_pid_set_property_str(ctx->base_opid, "isom:sabt", &pval);
338 3 : gf_free(pval.value.uint_list.vals);
339 :
340 3 : gf_filter_pid_set_property(ctx->base_opid, GF_PROP_PID_ORIG_SIZE, &PROP_VEC2I_INT(ctx->width, ctx->height) );
341 3 : gf_filter_pid_set_property(ctx->base_opid, GF_PROP_PID_BITRATE, bitrate ? &PROP_UINT(10000) : NULL);
342 :
343 3 : gf_odf_hevc_cfg_del(hvcc);
344 3 : return GF_OK;
345 : }
346 :
347 3 : static GF_Err tilesplit_set_eos(GF_Filter *filter, GF_TileSplitCtx *ctx)
348 : {
349 : u32 i;
350 27 : for (i=0; i<ctx->nb_tiles; i++) {
351 27 : if (ctx->opids[i].opid)
352 24 : gf_filter_pid_set_eos(ctx->opids[i].opid);
353 : }
354 3 : gf_filter_pid_set_eos(ctx->base_opid);
355 3 : return GF_EOS;
356 : }
357 :
358 : u32 hevc_get_tile_id(HEVCState *hevc, u32 *tile_x, u32 *tile_y, u32 *tile_width, u32 *tile_height);
359 :
360 4575 : static GF_Err tilesplit_process(GF_Filter *filter)
361 : {
362 4575 : GF_TileSplitCtx *ctx = (GF_TileSplitCtx *) gf_filter_get_udta(filter);
363 : GF_FilterPacket *in_pck, *opck;
364 : u32 pck_size, i;
365 : GF_Err e = GF_OK;
366 : const u8 *in_data;
367 : u8 *output;
368 :
369 4575 : in_pck = gf_filter_pid_get_packet(ctx->ipid);
370 4575 : if (!in_pck) {
371 2325 : if (gf_filter_pid_is_eos(ctx->ipid)) {
372 3 : return tilesplit_set_eos(filter, ctx);
373 : }
374 : return GF_OK;
375 : }
376 :
377 2250 : if (ctx->passthrough) {
378 0 : gf_filter_pck_forward(in_pck, ctx->base_opid);
379 0 : gf_filter_pid_drop_packet(ctx->ipid);
380 0 : return GF_OK;
381 : }
382 :
383 2250 : in_data = gf_filter_pck_get_data(in_pck, &pck_size);
384 2250 : if (!in_data) {
385 0 : gf_filter_pid_drop_packet(ctx->ipid);
386 0 : return GF_OK;
387 : }
388 :
389 2250 : if (!ctx->pck_bs) ctx->pck_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
390 2247 : else gf_bs_reassign_buffer(ctx->pck_bs, ctx->pck_buf, ctx->pck_buf_alloc);
391 :
392 20250 : for (i=0; i<ctx->nb_tiles; i++) {
393 20250 : TileSplitPid *tinfo = &ctx->opids[i];
394 20250 : if (!tinfo->opid)
395 2250 : continue;
396 18000 : if (!tinfo->pck_bs) tinfo->pck_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
397 17976 : else gf_bs_reassign_buffer(tinfo->pck_bs, tinfo->pck_buf, tinfo->pck_buf_alloc);
398 : }
399 :
400 24753 : while (pck_size) {
401 : TileSplitPid *tinfo;
402 : u8 temporal_id, layer_id;
403 22503 : u8 nal_type = 0;
404 : u32 nalu_size = 0;
405 : s32 ret;
406 : u32 cur_tile;
407 : u32 tx, ty, tw, th;
408 112515 : for (i=0; i<ctx->nalu_size_length; i++) {
409 90012 : nalu_size = (nalu_size<<8) + in_data[i];
410 : }
411 :
412 22503 : if (pck_size < nalu_size + ctx->nalu_size_length) {
413 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[TileSplit] Corrupted NAL size, %d indicated but %d remaining\n", nalu_size + ctx->nalu_size_length, pck_size));
414 0 : break;
415 : }
416 :
417 22503 : ret = gf_hevc_parse_nalu((u8 *) in_data + ctx->nalu_size_length, nalu_size, &ctx->hevc, &nal_type, &temporal_id, &layer_id);
418 :
419 : //error parsing NAL, set nal to fallback to regular import
420 22503 : if (ret<0) nal_type = GF_HEVC_NALU_VID_PARAM;
421 :
422 22503 : switch (nal_type) {
423 20250 : case GF_HEVC_NALU_SLICE_TRAIL_N:
424 : case GF_HEVC_NALU_SLICE_TRAIL_R:
425 : case GF_HEVC_NALU_SLICE_TSA_N:
426 : case GF_HEVC_NALU_SLICE_TSA_R:
427 : case GF_HEVC_NALU_SLICE_STSA_N:
428 : case GF_HEVC_NALU_SLICE_STSA_R:
429 : case GF_HEVC_NALU_SLICE_BLA_W_LP:
430 : case GF_HEVC_NALU_SLICE_BLA_W_DLP:
431 : case GF_HEVC_NALU_SLICE_BLA_N_LP:
432 : case GF_HEVC_NALU_SLICE_IDR_W_DLP:
433 : case GF_HEVC_NALU_SLICE_IDR_N_LP:
434 : case GF_HEVC_NALU_SLICE_CRA:
435 : case GF_HEVC_NALU_SLICE_RADL_R:
436 : case GF_HEVC_NALU_SLICE_RADL_N:
437 : case GF_HEVC_NALU_SLICE_RASL_R:
438 : case GF_HEVC_NALU_SLICE_RASL_N:
439 20250 : tx = ty = tw = th = 0;
440 20250 : cur_tile = hevc_get_tile_id(&ctx->hevc, &tx, &ty, &tw, &th);
441 20250 : if (cur_tile >= ctx->nb_tiles) {
442 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[TileSplit] Tile index %d is greater than number of tiles %d in PPS\n", cur_tile, ctx->nb_tiles));
443 : e = GF_NON_COMPLIANT_BITSTREAM;
444 0 : goto err_exit;
445 : }
446 20250 : tinfo = &ctx->opids[cur_tile];
447 20250 : if (!tinfo->opid)
448 : break;
449 :
450 18000 : if ((tinfo->x != tx) || (tinfo->w != tw) || (tinfo->y != ty) || (tinfo->h != th)) {
451 0 : tinfo->x = tx;
452 0 : tinfo->y = ty;
453 0 : tinfo->w = tw;
454 0 : tinfo->h = th;
455 :
456 0 : tilesplit_update_pid_props(ctx, tinfo);
457 : }
458 :
459 18000 : if (ctx->hevc.s_info.slice_type != GF_HEVC_SLICE_TYPE_I) {
460 17280 : tinfo->all_intra = GF_FALSE;
461 : }
462 :
463 18000 : gf_bs_write_data(tinfo->pck_bs, (u8 *) in_data, nalu_size + ctx->nalu_size_length);
464 18000 : break;
465 : //note we don't need to care about sps or pps here, they are not in the bitstream
466 2253 : default:
467 2253 : gf_bs_write_data(ctx->pck_bs, (u8 *) in_data, nalu_size + ctx->nalu_size_length);
468 2253 : break;
469 : }
470 :
471 24753 : in_data += nalu_size + ctx->nalu_size_length;
472 22503 : pck_size -= nalu_size + ctx->nalu_size_length;
473 : }
474 :
475 2250 : err_exit:
476 : //and flush
477 :
478 2250 : gf_bs_get_content_no_truncate(ctx->pck_bs, &ctx->pck_buf, &pck_size, &ctx->pck_buf_alloc);
479 2250 : opck = gf_filter_pck_new_alloc(ctx->base_opid, pck_size, &output);
480 2250 : if (opck) {
481 2250 : memcpy(output, ctx->pck_buf, pck_size);
482 2250 : gf_filter_pck_merge_properties(in_pck, opck);
483 2250 : gf_filter_pck_send(opck);
484 : } else {
485 : e = GF_OUT_OF_MEM;
486 : }
487 :
488 20250 : for (i=0; i<ctx->nb_tiles; i++) {
489 20250 : TileSplitPid *tinfo = &ctx->opids[i];
490 20250 : if (!tinfo->opid) continue;
491 :
492 18000 : gf_bs_get_content_no_truncate(tinfo->pck_bs, &tinfo->pck_buf, &pck_size, &tinfo->pck_buf_alloc);
493 18000 : opck = gf_filter_pck_new_alloc(tinfo->opid, pck_size, &output);
494 18000 : if (opck) {
495 18000 : memcpy(output, tinfo->pck_buf, pck_size);
496 18000 : gf_filter_pck_merge_properties(in_pck, opck);
497 18000 : gf_filter_pck_send(opck);
498 : } else {
499 : e = GF_OUT_OF_MEM;
500 : }
501 : }
502 :
503 2250 : gf_filter_pid_drop_packet(ctx->ipid);
504 2250 : return e;
505 : }
506 :
507 3 : static GF_Err tilesplit_initialize(GF_Filter *filter)
508 : {
509 : // GF_TileSplitCtx *ctx = (GF_TileSplitCtx *) gf_filter_get_udta(filter);
510 :
511 3 : return GF_OK;
512 : }
513 :
514 3 : static void tilesplit_finalize(GF_Filter *filter)
515 : {
516 : u32 i;
517 3 : GF_TileSplitCtx *ctx = (GF_TileSplitCtx *) gf_filter_get_udta(filter);
518 :
519 30 : for (i=0; i<ctx->nb_alloc_tiles; i++) {
520 27 : TileSplitPid *tinfo = &ctx->opids[i];
521 27 : if (tinfo->pck_buf) gf_free(tinfo->pck_buf);
522 27 : if (tinfo->pck_bs) gf_bs_del(tinfo->pck_bs);
523 : }
524 3 : if (ctx->opids)
525 3 : gf_free(ctx->opids);
526 :
527 3 : if (ctx->pck_bs) gf_bs_del(ctx->pck_bs);
528 3 : if (ctx->pck_buf) gf_free(ctx->pck_buf);
529 3 : }
530 :
531 : #else
532 : static GF_Err tilesplit_process(GF_Filter *filter)
533 : {
534 : return GF_NOT_SUPPORTED;
535 : }
536 : #endif
537 :
538 : static const GF_FilterCapability TileSplitCaps[] =
539 : {
540 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
541 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
542 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_HEVC),
543 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_TILE_BASE, GF_TRUE),
544 :
545 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
546 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_HEVC),
547 : CAP_BOOL(GF_CAPS_OUTPUT, GF_PROP_PID_TILE_BASE, GF_TRUE),
548 : {0},
549 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
550 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
551 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_HEVC),
552 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_TILE_BASE, GF_TRUE),
553 :
554 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
555 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_HEVC_TILES)
556 : };
557 :
558 : #define OFFS(_n) #_n, offsetof(GF_TileSplitCtx, _n)
559 :
560 : static const GF_FilterArgs TileSplitArgs[] =
561 : {
562 : { OFFS(tiledrop), "specify indexes of tiles to drop (0-based, in tile raster scan order)", GF_PROP_UINT_LIST, "", NULL, GF_FS_ARG_UPDATE},
563 : {0}
564 : };
565 :
566 : GF_FilterRegister TileSplitRegister = {
567 : .name = "tilesplit",
568 : .flags = GF_FS_REG_EXPLICIT_ONLY,
569 : GF_FS_SET_DESCRIPTION("HEVC tile bitstream splitter")
570 : GF_FS_SET_HELP("This filter splits an HEVC tiled stream into tiled HEVC streams (`hvt1` or `hvt2` in isobmff)."
571 : "\n"
572 : "The filter will move to passthrough mode if the bitstream is not tiled.\n"
573 : "If the `Bitrate` property is set on the input PID, the output tile PIDs will have a bitrate set to `(Bitrate - 10k)/nb_opids`, 10 kbps being reserved for the base.\n"
574 : "\n"
575 : "Each tile PID will be assigned the following properties:\n"
576 : "- `ID`: equal to the base PID ID (same as input) plus the 1-based index of the tile in raster scan order.\n"
577 : "- `TileID`: equal to the 1-based index of the tile in raster scan order.\n"
578 : "\n"
579 : "Warning: The filter does not check if tiles are independently-coded (MCTS) !\n"
580 : "\n"
581 : "Warning: Support for dynamic changes of tiling grid has not been tested !\n"
582 : )
583 : .private_size = sizeof(GF_TileSplitCtx),
584 : SETCAPS(TileSplitCaps),
585 : #if !defined(GPAC_DISABLE_HEVC) && !defined(GPAC_DISABLE_AV_PARSERS)
586 : .initialize = tilesplit_initialize,
587 : .finalize = tilesplit_finalize,
588 : .args = TileSplitArgs,
589 : .configure_pid = tilesplit_configure_pid,
590 : #endif
591 : .process = tilesplit_process,
592 :
593 : };
594 :
595 :
596 2877 : const GF_FilterRegister *tilesplit_register(GF_FilterSession *session)
597 : {
598 : #if defined(GPAC_DISABLE_HEVC) || defined(GPAC_DISABLE_AV_PARSERS)
599 : if (!gf_opts_get_bool("temp", "gendoc"))
600 : return NULL;
601 : TileSplitRegister.version = "! Warning: NOT AVAILABLE IN THIS BUILD !";
602 : #endif
603 2877 : return &TileSplitRegister;
604 : }
605 :
606 :
|