Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Yacine Mathurin Boubacar Aziakou
6 : * Samir Mustapha
7 : * Copyright (c) Telecom ParisTech 2019-2021
8 : * All rights reserved
9 : *
10 : * This file is part of GPAC / HEVC tile merger filter
11 : *
12 : * GPAC is free software; you can redistribute it and/or modify
13 : * it under the terms of the GNU Lesser General Public License as published by
14 : * the Free Software Foundation; either version 2, or (at your option)
15 : * any later version.
16 : *
17 : * GPAC is distributed in the hope that it will be useful,
18 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : * GNU Lesser General Public License for more details.
21 : *
22 : * You should have received a copy of the GNU Lesser General Public
23 : * License along with this library; see the file COPYING. If not, write to
24 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 : *
26 : */
27 :
28 : #include <gpac/bitstream.h>
29 : #include <gpac/filters.h>
30 : #include <gpac/avparse.h>
31 : #include <gpac/constants.h>
32 : #include <gpac/internal/media_dev.h>
33 : #include <math.h>
34 :
35 : #if !defined(GPAC_DISABLE_HEVC) && !defined(GPAC_DISABLE_AV_PARSERS)
36 :
37 : typedef struct
38 : {
39 : GF_FilterPid *pid;
40 : u32 slice_segment_address, width, height;
41 : Bool in_error;
42 :
43 : u32 nalu_size_length;
44 : u32 dsi_crc;
45 : HEVCState hevc_state;
46 :
47 : //final position in grid - the row index is only used to push non multiple of CU height at the bottom of the grid
48 : u32 pos_row, pos_col;
49 :
50 : //timescale of source pid
51 : //number of packets processed
52 : u32 timescale;
53 : u32 nb_pck;
54 :
55 : //true if positioning in pixel is given
56 : Bool has_pos;
57 : // >=0: positioning in pixel in the Y plane as given by CropOrigin
58 : // <=0: positioning relative to top-left tile
59 : s32 pos_x, pos_y;
60 : } HEVCTilePidCtx;
61 :
62 :
63 : typedef struct {
64 : //width of column
65 : u32 width;
66 : //cumulated height of all slices in column
67 : u32 height;
68 : //only used while computing the grid, current position in rows in the column
69 : u32 row_pos, max_row_pos;
70 : u32 last_row_idx;
71 : u32 pos_x;
72 : } HEVCGridInfo;
73 :
74 : typedef struct
75 : {
76 : //options
77 : Bool strict, mrows;
78 :
79 : GF_FilterPid *opid;
80 : s32 base_pps_init_qp_delta_minus26;
81 : u32 nb_bits_per_address_dst;
82 : u32 out_width, out_height;
83 : u8 *buffer_nal, *buffer_nal_no_epb, *buffer_nal_in_no_epb;
84 : u32 buffer_nal_alloc, buffer_nal_no_epb_alloc, buffer_nal_in_no_epb_alloc;
85 : GF_BitStream *bs_au_in;
86 :
87 : GF_BitStream *bs_nal_in;
88 : GF_BitStream *bs_nal_out;
89 :
90 : HEVCGridInfo *grid;
91 : u32 nb_cols;
92 :
93 : u8 *sei_suffix_buf;
94 : u32 sei_suffix_len, sei_suffix_alloc;
95 : u32 hevc_nalu_size_length;
96 : u32 max_CU_width, max_CU_height;
97 :
98 : GF_List *pids, *ordered_pids;
99 : Bool in_error;
100 : Bool enable_multi_rows;
101 : u32 nb_rows;
102 : } GF_HEVCMergeCtx;
103 :
104 : //in src/filters/hevcsplit.c
105 : void hevc_rewrite_sps(char *in_SPS, u32 in_SPS_length, u32 width, u32 height, char **out_SPS, u32 *out_SPS_length);
106 :
107 : #if 0 //todo
108 : //rewrite the profile and level
109 : static void write_profile_tier_level(GF_BitStream *ctx->bs_nal_in, GF_BitStream *ctx->bs_nal_out, Bool ProfilePresentFlag, u8 MaxNumSubLayersMinus1)
110 : {
111 : u8 j;
112 : Bool sub_layer_profile_present_flag[8], sub_layer_level_present_flag[8];
113 : if (ProfilePresentFlag) {
114 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 8), 8);
115 : gf_bs_write_long_int(ctx->bs_nal_out, gf_bs_read_long_int(ctx->bs_nal_in, 32), 32);
116 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 4), 4);
117 : gf_bs_write_long_int(ctx->bs_nal_out, gf_bs_read_long_int(ctx->bs_nal_in, 44), 44);
118 : }
119 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 8), 8);
120 :
121 : for (j = 0; j < MaxNumSubLayersMinus1; j++) {
122 : sub_layer_profile_present_flag[j] = gf_bs_read_int(ctx->bs_nal_in, 1);
123 : gf_bs_write_int(ctx->bs_nal_out, sub_layer_profile_present_flag[j], 1);
124 : sub_layer_level_present_flag[j] = gf_bs_read_int(ctx->bs_nal_in, 1);
125 : gf_bs_write_int(ctx->bs_nal_out, sub_layer_level_present_flag[j], 1);
126 : }
127 : if (MaxNumSubLayersMinus1 > 0)
128 : for (j = MaxNumSubLayersMinus1; j < 8; j++)
129 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 2), 2);
130 :
131 :
132 : for (j = 0; j < MaxNumSubLayersMinus1; j++) {
133 : if (sub_layer_profile_present_flag[j]) {
134 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 8), 8);
135 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 32), 32);
136 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 4), 4);
137 : gf_bs_write_long_int(ctx->bs_nal_out, gf_bs_read_long_int(ctx->bs_nal_in, 44), 44);
138 : }
139 : if (sub_layer_level_present_flag[j])
140 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 8), 8);
141 : }
142 : }
143 : #endif
144 :
145 25 : static void hevcmerge_rewrite_pps(GF_HEVCMergeCtx *ctx, char *in_PPS, u32 in_PPS_length, char **out_PPS, u32 *out_PPS_length)
146 : {
147 : u8 cu_qp_delta_enabled_flag;
148 : u32 loop_filter_flag;
149 : u32 pps_size_no_epb;
150 :
151 25 : gf_bs_reassign_buffer(ctx->bs_nal_in, in_PPS, in_PPS_length);
152 25 : gf_bs_enable_emulation_byte_removal(ctx->bs_nal_in, GF_TRUE);
153 25 : if (!ctx->bs_nal_out) ctx->bs_nal_out = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
154 20 : else gf_bs_reassign_buffer(ctx->bs_nal_out, ctx->buffer_nal_no_epb, ctx->buffer_nal_no_epb_alloc);
155 :
156 : //Read and write NAL header bits
157 25 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 16), 16);
158 25 : gf_bs_write_ue(ctx->bs_nal_out, gf_bs_read_ue(ctx->bs_nal_in)); //pps_pic_parameter_set_id
159 25 : gf_bs_write_ue(ctx->bs_nal_out, gf_bs_read_ue(ctx->bs_nal_in)); //pps_seq_parameter_set_id
160 25 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 7), 7); //from dependent_slice_segments_enabled_flag to cabac_init_present_flag
161 25 : gf_bs_write_ue(ctx->bs_nal_out, gf_bs_read_ue(ctx->bs_nal_in)); //num_ref_idx_l0_default_active_minus1
162 25 : gf_bs_write_ue(ctx->bs_nal_out, gf_bs_read_ue(ctx->bs_nal_in)); //num_ref_idx_l1_default_active_minus1
163 25 : gf_bs_write_se(ctx->bs_nal_out, gf_bs_read_se(ctx->bs_nal_in)); //init_qp_minus26
164 25 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 2), 2); //from constrained_intra_pred_flag to transform_skip_enabled_flag
165 25 : cu_qp_delta_enabled_flag = gf_bs_read_int(ctx->bs_nal_in, 1); //cu_qp_delta_enabled_flag
166 25 : gf_bs_write_int(ctx->bs_nal_out, cu_qp_delta_enabled_flag, 1); //
167 25 : if (cu_qp_delta_enabled_flag)
168 0 : gf_bs_write_ue(ctx->bs_nal_out, gf_bs_read_ue(ctx->bs_nal_in)); // diff_cu_qp_delta_depth
169 25 : gf_bs_write_se(ctx->bs_nal_out, gf_bs_read_se(ctx->bs_nal_in)); // pps_cb_qp_offset
170 25 : gf_bs_write_se(ctx->bs_nal_out, gf_bs_read_se(ctx->bs_nal_in)); // pps_cr_qp_offset
171 25 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 4), 4); // from pps_slice_chroma_qp_offsets_present_flag to transquant_bypass_enabled_flag
172 :
173 :
174 25 : gf_bs_read_int(ctx->bs_nal_in, 1);
175 25 : gf_bs_write_int(ctx->bs_nal_out, 1, 1);
176 25 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 1), 1);//entropy_coding_sync_enabled_flag
177 25 : gf_bs_write_ue(ctx->bs_nal_out, ctx->nb_cols-1);//write num_tile_columns_minus1
178 : //num_tile_rows_minus1
179 25 : if (ctx->enable_multi_rows) {
180 0 : u32 nb_rows = ctx->nb_rows - 1;
181 : assert(ctx->nb_rows);
182 0 : gf_bs_write_ue(ctx->bs_nal_out, nb_rows);
183 : } else {
184 25 : gf_bs_write_ue(ctx->bs_nal_out, 0);//num_tile_rows_minus1
185 : }
186 25 : gf_bs_write_int(ctx->bs_nal_out, 0, 1); //uniform_spacing_flag
187 :
188 : // if (!uniform_spacing_flag) //always 0
189 : {
190 : u32 i;
191 49 : for (i = 0; i < ctx->nb_cols-1; i++)
192 24 : gf_bs_write_ue(ctx->bs_nal_out, (ctx->grid[i].width / ctx->max_CU_width - 1));
193 :
194 : //if multi row is possible, declare the row height
195 25 : if (ctx->enable_multi_rows && (ctx->nb_rows>1)) {
196 0 : u32 nb_rows = ctx->nb_rows - 1;
197 0 : u32 nb_pids = gf_list_count(ctx->pids);
198 :
199 0 : for (i=0; i<nb_pids; i++) {
200 : //get pid in their final order
201 0 : HEVCTilePidCtx *tile = gf_list_get(ctx->ordered_pids, i);
202 : //only check height in the first column
203 0 : if (tile->pos_col) continue;
204 0 : gf_bs_write_ue(ctx->bs_nal_out, (tile->height / ctx->max_CU_width - 1)); // row_height_minus1[i]
205 0 : nb_rows--;
206 0 : if (!nb_rows) break;
207 : }
208 : }
209 : //otherwise nothing to declare since we use a single row
210 : }
211 : loop_filter_flag = 1;
212 25 : gf_bs_write_int(ctx->bs_nal_out, loop_filter_flag, 1);
213 :
214 25 : loop_filter_flag = gf_bs_read_int(ctx->bs_nal_in, 1);
215 25 : gf_bs_write_int(ctx->bs_nal_out, loop_filter_flag, 1);
216 :
217 : // copy and write the rest of the bits in the byte
218 100 : while (gf_bs_get_bit_position(ctx->bs_nal_in) != 8) {
219 50 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 1), 1);
220 : }
221 :
222 : //copy and write the rest of the bytes
223 75 : while (gf_bs_get_size(ctx->bs_nal_in) != gf_bs_get_position(ctx->bs_nal_in)) {
224 50 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_u8(ctx->bs_nal_in), 8); //watchout, not aligned in destination bitstream
225 : }
226 :
227 25 : gf_bs_align(ctx->bs_nal_out);
228 :
229 25 : gf_bs_get_content_no_truncate(ctx->bs_nal_out, &ctx->buffer_nal_no_epb, &pps_size_no_epb, &ctx->buffer_nal_no_epb_alloc);
230 :
231 25 : *out_PPS_length = pps_size_no_epb + gf_media_nalu_emulation_bytes_add_count(ctx->buffer_nal_no_epb, pps_size_no_epb);
232 25 : *out_PPS = gf_malloc(*out_PPS_length);
233 25 : gf_media_nalu_add_emulation_bytes(ctx->buffer_nal_no_epb, *out_PPS, pps_size_no_epb);
234 25 : }
235 :
236 18750 : u32 hevcmerge_rewrite_slice(GF_HEVCMergeCtx *ctx, HEVCTilePidCtx *tile_pid, char *in_slice, u32 in_slice_length)
237 : {
238 : u64 header_end;
239 18750 : u32 out_slice_size_no_epb = 0, out_slice_length;
240 : u32 num_entry_point_start;
241 : u32 pps_id;
242 : Bool RapPicFlag = GF_FALSE;
243 : u32 slice_qp_delta_start;
244 : HEVC_PPS *pps;
245 : HEVC_SPS *sps;
246 : u32 al, slice_size, in_slice_size_no_epb, slice_offset_orig, slice_offset_dst;
247 : u32 first_slice_segment_in_pic_flag;
248 : u32 dependent_slice_segment_flag;
249 : u8 nal_unit_type;
250 : s32 new_slice_qp_delta;
251 :
252 : HEVCState *hevc = &tile_pid->hevc_state;
253 :
254 : //we remove EPB directly rather than from bs reader, since we will have to copy the entire payload without EPB
255 : //and gf_bs_read_data does not check for EPB
256 18750 : if (ctx->buffer_nal_in_no_epb_alloc<in_slice_length) {
257 26 : ctx->buffer_nal_in_no_epb_alloc = in_slice_length;
258 26 : ctx->buffer_nal_in_no_epb = gf_realloc(ctx->buffer_nal_in_no_epb, in_slice_length);
259 : }
260 18750 : in_slice_size_no_epb = gf_media_nalu_remove_emulation_bytes(in_slice, ctx->buffer_nal_in_no_epb, in_slice_length);
261 18750 : gf_bs_reassign_buffer(ctx->bs_nal_in, ctx->buffer_nal_in_no_epb, in_slice_size_no_epb);
262 : //disable EPB removal
263 18750 : gf_bs_enable_emulation_byte_removal(ctx->bs_nal_in, GF_FALSE);
264 18750 : if (!ctx->bs_nal_out) ctx->bs_nal_out = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
265 18750 : else gf_bs_reassign_buffer(ctx->bs_nal_out, ctx->buffer_nal_no_epb, ctx->buffer_nal_no_epb_alloc);
266 :
267 : assert(hevc->s_info.header_size_bits >= 0);
268 : assert(hevc->s_info.entry_point_start_bits >= 0);
269 18750 : header_end = (u64)hevc->s_info.header_size_bits;
270 :
271 18750 : num_entry_point_start = (u32)hevc->s_info.entry_point_start_bits;
272 18750 : slice_qp_delta_start = (u32)hevc->s_info.slice_qp_delta_start_bits;
273 :
274 : // nal_unit_header
275 18750 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 1), 1);
276 18750 : nal_unit_type = gf_bs_read_int(ctx->bs_nal_in, 6);
277 18750 : gf_bs_write_int(ctx->bs_nal_out, nal_unit_type, 6);
278 18750 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 9), 9);
279 :
280 18750 : first_slice_segment_in_pic_flag = gf_bs_read_int(ctx->bs_nal_in, 1); //first_slice_segment_in_pic_flag
281 18750 : if (tile_pid->slice_segment_address == 0)
282 3750 : gf_bs_write_int(ctx->bs_nal_out, 1, 1);
283 : else
284 15000 : gf_bs_write_int(ctx->bs_nal_out, 0, 1);
285 :
286 18750 : switch (nal_unit_type) {
287 : case GF_HEVC_NALU_SLICE_IDR_W_DLP:
288 : case GF_HEVC_NALU_SLICE_IDR_N_LP:
289 : RapPicFlag = GF_TRUE;
290 : break;
291 : case GF_HEVC_NALU_SLICE_BLA_W_LP:
292 : case GF_HEVC_NALU_SLICE_BLA_W_DLP:
293 : case GF_HEVC_NALU_SLICE_BLA_N_LP:
294 : case GF_HEVC_NALU_SLICE_CRA:
295 : RapPicFlag = GF_TRUE;
296 : break;
297 : }
298 :
299 : if (RapPicFlag) {
300 : //no_output_of_prior_pics_flag
301 750 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 1), 1);
302 : }
303 :
304 18750 : pps_id = gf_bs_read_ue(ctx->bs_nal_in);
305 18750 : gf_bs_write_ue(ctx->bs_nal_out, pps_id);
306 :
307 : pps = &hevc->pps[pps_id];
308 18750 : sps = &hevc->sps[pps->sps_id];
309 :
310 18750 : if (!first_slice_segment_in_pic_flag && pps->dependent_slice_segments_enabled_flag) {
311 0 : dependent_slice_segment_flag = gf_bs_read_int(ctx->bs_nal_in, 1);
312 : } else {
313 : dependent_slice_segment_flag = GF_FALSE;
314 : }
315 18750 : if (!first_slice_segment_in_pic_flag) {
316 0 : /*address_ori = */gf_bs_read_int(ctx->bs_nal_in, sps->bitsSliceSegmentAddress);
317 : }
318 : //else original slice segment address = 0
319 :
320 18750 : if (tile_pid->slice_segment_address > 0) {
321 15000 : if (pps->dependent_slice_segments_enabled_flag) {
322 0 : gf_bs_write_int(ctx->bs_nal_out, dependent_slice_segment_flag, 1);
323 : }
324 15000 : gf_bs_write_int(ctx->bs_nal_out, tile_pid->slice_segment_address, ctx->nb_bits_per_address_dst);
325 : }
326 : //else first slice in pic, no address
327 :
328 : //copy over bits until start of slice_qp_delta
329 364500 : while (slice_qp_delta_start != (gf_bs_get_position(ctx->bs_nal_in) - 1) * 8 + gf_bs_get_bit_position(ctx->bs_nal_in)) {
330 345750 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 1), 1);
331 : }
332 : //compute new qp delta
333 18750 : new_slice_qp_delta = hevc->s_info.pps->pic_init_qp_minus26 + hevc->s_info.slice_qp_delta - ctx->base_pps_init_qp_delta_minus26;
334 18750 : gf_bs_write_se(ctx->bs_nal_out, new_slice_qp_delta);
335 18750 : gf_bs_read_se(ctx->bs_nal_in);
336 :
337 : //copy over until num_entry_points
338 37500 : while (num_entry_point_start != (gf_bs_get_position(ctx->bs_nal_in) - 1) * 8 + gf_bs_get_bit_position(ctx->bs_nal_in)) {
339 0 : gf_bs_write_int(ctx->bs_nal_out, gf_bs_read_int(ctx->bs_nal_in, 1), 1);
340 : }
341 : //write num_entry_points to 0 (always present since we use tiling)
342 18750 : gf_bs_write_ue(ctx->bs_nal_out, 0);
343 :
344 : //write slice extension to 0
345 18750 : if (pps->slice_segment_header_extension_present_flag)
346 0 : gf_bs_write_int(ctx->bs_nal_out, 0, 1);
347 :
348 :
349 : //we may have unparsed data in the source bitstream (slice header) due to entry points or slice segment extensions
350 : //TODO: we might want to copy over the slice extension header bits
351 18750 : while (header_end != (gf_bs_get_position(ctx->bs_nal_in) - 1) * 8 + gf_bs_get_bit_position(ctx->bs_nal_in))
352 0 : gf_bs_read_int(ctx->bs_nal_in, 1);
353 :
354 : //read byte_alignment() is bit=1 + x bit=0
355 18750 : al = gf_bs_read_int(ctx->bs_nal_in, 1);
356 18750 : if (al != 1) {
357 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] source slice header not properly aligned\n"));
358 : }
359 18750 : gf_bs_align(ctx->bs_nal_in);
360 :
361 : //write byte_alignment() is bit=1 + x bit=0
362 18750 : gf_bs_write_int(ctx->bs_nal_out, 1, 1);
363 18750 : gf_bs_align(ctx->bs_nal_out); // align
364 :
365 : //get the final slice header
366 18750 : gf_bs_get_content_no_truncate(ctx->bs_nal_out, &ctx->buffer_nal_no_epb, &out_slice_size_no_epb, &ctx->buffer_nal_no_epb_alloc);
367 :
368 : //get the slice payload size (no EPB in it) and offset in payload
369 18750 : slice_size = (u32) gf_bs_available(ctx->bs_nal_in);
370 18750 : slice_offset_orig = (u32) gf_bs_get_position(ctx->bs_nal_in);
371 : //slice data offset in output slice
372 18750 : slice_offset_dst = out_slice_size_no_epb;
373 : //update output slice size
374 18750 : out_slice_size_no_epb += slice_size;
375 :
376 : //copy slice data
377 18750 : if (ctx->buffer_nal_no_epb_alloc < out_slice_size_no_epb) {
378 21 : ctx->buffer_nal_no_epb_alloc = out_slice_size_no_epb;
379 21 : ctx->buffer_nal_no_epb = gf_realloc(ctx->buffer_nal_no_epb, out_slice_size_no_epb);
380 : }
381 18750 : memcpy(ctx->buffer_nal_no_epb + slice_offset_dst, ctx->buffer_nal_in_no_epb + slice_offset_orig, sizeof(char) * slice_size);
382 :
383 : //insert epb
384 18750 : out_slice_length = out_slice_size_no_epb + gf_media_nalu_emulation_bytes_add_count(ctx->buffer_nal_no_epb, out_slice_size_no_epb);
385 18750 : if (ctx->buffer_nal_alloc < out_slice_length) {
386 26 : ctx->buffer_nal_alloc = out_slice_length;
387 26 : ctx->buffer_nal = gf_realloc(ctx->buffer_nal, out_slice_length);
388 : }
389 18750 : gf_media_nalu_add_emulation_bytes(ctx->buffer_nal_no_epb, ctx->buffer_nal, out_slice_size_no_epb);
390 18750 : return out_slice_length;
391 : }
392 :
393 25 : static GF_Err hevcmerge_rewrite_config(GF_HEVCMergeCtx *ctx, GF_FilterPid *opid, char *data, u32 size)
394 : {
395 : u32 i, j;
396 : u8 *new_dsi;
397 : u32 new_size;
398 25 : GF_HEVCConfig *hvcc = gf_odf_hevc_cfg_read(data, size, GF_FALSE);
399 25 : if (!hvcc) return GF_NON_COMPLIANT_BITSTREAM;
400 :
401 : // for all the list objects in param_array
402 75 : for (i = 0; i < gf_list_count(hvcc->param_array); i++) { // hvcc->param_array:list object
403 75 : GF_NALUFFParamArray *ar = (GF_NALUFFParamArray *) gf_list_get(hvcc->param_array, i); // ar contains the i-th item in param_array
404 150 : for (j = 0; j < gf_list_count(ar->nalus); j++) { // for all the nalus the i-th param got
405 : /*! used for storing AVC sequenceParameterSetNALUnit and pictureParameterSetNALUnit*/
406 75 : GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(ar->nalus, j); // store j-th nalus in *sl
407 :
408 75 : if (ar->type == GF_HEVC_NALU_SEQ_PARAM) {
409 25 : char *outSPS=NULL;
410 25 : u32 outSize=0;
411 25 : hevc_rewrite_sps(sl->data, sl->size, ctx->out_width, ctx->out_height, &outSPS, &outSize);
412 25 : gf_free(sl->data);
413 25 : sl->data = outSPS;
414 25 : sl->size = outSize;
415 : }
416 50 : else if (ar->type == GF_HEVC_NALU_VID_PARAM) {
417 : }
418 25 : else if (ar->type == GF_HEVC_NALU_PIC_PARAM) {
419 25 : char *outPPS=NULL;
420 25 : u32 outSize=0;
421 25 : hevcmerge_rewrite_pps(ctx, sl->data, sl->size, &outPPS, &outSize);
422 25 : gf_free(sl->data);
423 25 : sl->data = outPPS;
424 25 : sl->size = outSize;
425 : }
426 : }
427 : }
428 25 : gf_odf_hevc_cfg_write(hvcc, &new_dsi, &new_size);
429 25 : gf_odf_hevc_cfg_del(hvcc);
430 :
431 25 : gf_filter_pid_set_property(opid, GF_PROP_PID_DECODER_CONFIG, &PROP_DATA_NO_COPY(new_dsi, new_size));
432 25 : return GF_OK;
433 : }
434 :
435 : static void hevcmerge_write_nal(GF_HEVCMergeCtx *ctx, char *output_nal, char *rewritten_nal, u32 out_nal_size)
436 : {
437 22505 : u32 n = 8*(ctx->hevc_nalu_size_length);
438 112525 : while (n) {
439 90020 : u32 v = (out_nal_size >> (n-8)) & 0xFF;
440 90020 : *output_nal = v;
441 90020 : output_nal++;
442 : n-=8;
443 : }
444 22505 : memcpy(output_nal, rewritten_nal, out_nal_size);
445 : }
446 :
447 102 : static u32 hevcmerge_compute_address(GF_HEVCMergeCtx *ctx, HEVCTilePidCtx *tile_pid, Bool use_y_coord)
448 : {
449 : u32 i, nb_pids, sum_height = 0, sum_width = 0;
450 163 : for (i=0; i<tile_pid->pos_col; i++) {
451 61 : sum_width += ctx->grid[i].width / ctx->max_CU_width;
452 : }
453 :
454 102 : if (use_y_coord) {
455 96 : sum_height = tile_pid->pos_y / ctx->max_CU_height;
456 : } else {
457 6 : nb_pids = gf_list_count(ctx->pids);
458 14 : for (i=0; i<nb_pids; i++) {
459 8 : HEVCTilePidCtx *actx = gf_list_get(ctx->pids, i);
460 8 : if (actx->pos_col == tile_pid->pos_col) {
461 6 : if (actx->pos_row<tile_pid->pos_row) {
462 0 : sum_height += actx->height / ctx->max_CU_height;
463 : }
464 : }
465 : }
466 : }
467 102 : return sum_height * (ctx->out_width / ctx->max_CU_width) + sum_width;
468 : }
469 :
470 25 : void hevcmerge_build_srdmap(GF_HEVCMergeCtx *ctx, Bool use_abs_pos)
471 : {
472 : GF_PropertyValue srdmap;
473 : u32 i, nb_pids;
474 : Bool srd_map_valid = GF_TRUE;
475 :
476 25 : if (!ctx->out_width || !ctx->out_height) {
477 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD_MAP, NULL);
478 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD_REF, NULL);
479 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD, NULL);
480 1 : return;
481 : }
482 :
483 24 : nb_pids = gf_list_count(ctx->ordered_pids);
484 :
485 :
486 : memset(&srdmap, 0, sizeof(GF_PropertyValue));
487 24 : srdmap.type = GF_PROP_UINT_LIST;
488 : //8 integers per PID
489 24 : srdmap.value.uint_list.nb_items = nb_pids*8;
490 24 : srdmap.value.uint_list.vals = gf_malloc(sizeof(s32)*nb_pids*8);
491 :
492 : s32 *vals = srdmap.value.uint_list.vals;
493 :
494 125 : for (i=0; i<nb_pids; i++) {
495 : s32 tile_x, tile_y;
496 : u32 width_in_CU, nb_cols, nb_rows;
497 : s32 x=0, y=0, w=0, h=0, srd_x, srd_y, srd_w, srd_h;
498 101 : HEVCTilePidCtx *tile = gf_list_get(ctx->ordered_pids, i);
499 :
500 101 : const GF_PropertyValue *srd = gf_filter_pid_get_property(tile->pid, GF_PROP_PID_SRD);
501 101 : const GF_PropertyValue *srd_ref = gf_filter_pid_get_property(tile->pid, GF_PROP_PID_SRD_REF);
502 101 : const GF_PropertyValue *crop_pos = gf_filter_pid_get_property(tile->pid, GF_PROP_PID_CROP_POS);
503 :
504 : //get original SRD of the PID, translate into output referential {out_width,out_height}
505 101 : if (srd && srd_ref) {
506 96 : srd_x = srd->value.vec4i.x;
507 96 : srd_y = srd->value.vec4i.y;
508 96 : srd_w = srd->value.vec4i.z;
509 96 : srd_h = srd->value.vec4i.w;
510 :
511 96 : if (srd_ref->value.vec2i.x != 0) {
512 96 : x = (srd_x * ctx->out_width) / srd_ref->value.vec2i.x;
513 96 : w = (srd_w * ctx->out_width) / srd_ref->value.vec2i.x;
514 : } else {
515 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("[HEVCMerge] width=0 in source pid SRD referential, cannot output SRD map\n" ));
516 : srd_map_valid = GF_FALSE;
517 : break;
518 : }
519 96 : if (srd_ref->value.vec2i.y != 0) {
520 96 : y = (srd_y * ctx->out_height) / srd_ref->value.vec2i.y;
521 96 : h = (srd_h * ctx->out_height) / srd_ref->value.vec2i.y;
522 : } else {
523 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("[HEVCMerge] height=0 in source pid SRD referential, undefined results\n" ));
524 : srd_map_valid = GF_FALSE;
525 : break;
526 : }
527 5 : } else if (crop_pos) {
528 5 : x = crop_pos->value.vec2i.x;
529 5 : y = crop_pos->value.vec2i.y;
530 5 : w = (s32)(tile->width);
531 5 : h = (s32)(tile->height);
532 : } else {
533 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("[HEVCMerge] SRD, SRD_REF and CROP_POS are not defined for source pid, undefined results\n" ));
534 : srd_map_valid = GF_FALSE;
535 : break;
536 : }
537 101 : vals[8*i] = x;
538 101 : vals[8*i+1] = y;
539 101 : vals[8*i+2] = w;
540 101 : vals[8*i+3] = h;
541 :
542 : //get final position in the recomputed video, expressed in output referential {out_width,out_height}
543 : //we recompute the position directly from the slice segment address
544 101 : width_in_CU = ctx->out_width / ctx->max_CU_width;
545 101 : nb_cols = tile->slice_segment_address % width_in_CU;
546 101 : nb_rows = (tile->slice_segment_address - nb_cols) / width_in_CU;
547 :
548 : assert(tile->slice_segment_address == (nb_rows * width_in_CU + nb_cols));
549 :
550 : //tile_x and tile_y should start at zero
551 101 : tile_x = use_abs_pos ? nb_cols * ctx->max_CU_width : nb_cols;
552 101 : tile_y = use_abs_pos ? nb_rows * ctx->max_CU_height : nb_rows;
553 :
554 101 : vals[8*i+4] = (tile_x * ctx->out_width) / ctx->out_width;
555 101 : vals[8*i+5] = (tile_y * ctx->out_height) / ctx->out_height;
556 101 : vals[8*i+6] = (tile->width * ctx->out_width) / ctx->out_width;
557 101 : vals[8*i+7] = (tile->height * ctx->out_height) / ctx->out_height;
558 : }
559 : if (srd_map_valid)
560 24 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD_MAP, &srdmap);
561 :
562 24 : gf_free(srdmap.value.uint_list.vals);
563 :
564 : //assign reference space
565 24 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD_REF, &PROP_VEC2I_INT(ctx->out_width, ctx->out_height));
566 : //remove any SRD copied from inputs - we could also create an SRD {0,0,out_width,out_height}
567 24 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD, NULL);
568 : }
569 :
570 25 : static GF_Err hevcmerge_rebuild_grid(GF_HEVCMergeCtx *ctx, GF_FilterPid *pid)
571 : {
572 : u32 nb_cols=0;
573 : u32 nb_rows=0;
574 : Bool reorder_rows=GF_FALSE;
575 : u32 force_last_col_plus_one=0;
576 : u32 nb_has_pos=0, nb_no_pos=0, nb_rel_pos=0, nb_abs_pos=0;
577 : u32 min_rel_pos_x = (u32) -1;
578 : u32 min_rel_pos_y = (u32) -1;
579 25 : u32 i, j, max_cols, nb_pids = gf_list_count(ctx->pids);
580 :
581 102 : for (i=0; i<nb_pids; i++) {
582 102 : HEVCTilePidCtx *apidctx = gf_list_get(ctx->pids, i);
583 102 : if (apidctx->width % ctx->max_CU_width) nb_cols++;
584 102 : if (apidctx->height % ctx->max_CU_height) nb_rows++;
585 102 : if (apidctx->has_pos) {
586 102 : nb_has_pos++;
587 102 : if ((apidctx->pos_x<0) || (apidctx->pos_y<0)) {
588 2 : nb_rel_pos++;
589 : }
590 100 : else if ((apidctx->pos_x>0) || (apidctx->pos_y>0)) nb_abs_pos++;
591 :
592 102 : if ((apidctx->pos_x<=0) && ((u32) (-apidctx->pos_x) < min_rel_pos_x))
593 : min_rel_pos_x = -apidctx->pos_x;
594 102 : if ((apidctx->pos_y<=0) && ((u32) (-apidctx->pos_y) < min_rel_pos_y))
595 : min_rel_pos_y = -apidctx->pos_y;
596 : } else {
597 0 : nb_no_pos++;
598 : }
599 : }
600 25 : if ((nb_cols>1) && (nb_rows>1)) {
601 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Cannot merge more than one tile not a multiple of CTUs in both width and height, not possible in standard\n"));
602 : return GF_BAD_PARAM;
603 : }
604 25 : if (nb_has_pos && nb_no_pos) {
605 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Cannot merge tiles with explicit positioning and tiles with implicit positioning, not supported\n"));
606 : return GF_BAD_PARAM;
607 : }
608 25 : if (nb_rel_pos && nb_abs_pos) {
609 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Cannot merge tiles with both relative explicit positioning and absolute explicit positioning, not supported\n"));
610 : return GF_BAD_PARAM;
611 : }
612 25 : if (ctx->grid) gf_free(ctx->grid);
613 25 : ctx->grid = NULL;
614 :
615 25 : if (min_rel_pos_x == (u32)-1) min_rel_pos_x=0;
616 :
617 25 : if (nb_has_pos) {
618 : max_cols = 0;
619 102 : for (i=0; i<nb_pids; i++) {
620 102 : HEVCTilePidCtx *tile1 = gf_list_get(ctx->pids, i);
621 102 : if (!tile1->has_pos) continue;
622 :
623 594 : for (j=0; j<nb_pids; j++) {
624 : Bool overlap = GF_FALSE;
625 594 : HEVCTilePidCtx *tile2 = gf_list_get(ctx->pids, j);
626 594 : if (tile2 == tile1) continue;
627 492 : if (!tile2->has_pos) continue;
628 :
629 : //for relative positioning, only check we don't have the same indexes
630 492 : if (nb_rel_pos) {
631 2 : continue;
632 : }
633 :
634 : //make sure we are aligned
635 490 : if ((tile1->pos_x<tile2->pos_x) && (tile1->pos_x + (s32) tile1->width > tile2->pos_x)) {
636 : overlap = GF_TRUE;
637 : }
638 490 : else if ((tile1->pos_x==tile2->pos_x) && (tile1->width != tile2->width)) {
639 : overlap = GF_TRUE;
640 : }
641 490 : if (tile1->pos_x==tile2->pos_x) {
642 156 : if ((tile1->pos_y<tile2->pos_y) && (tile1->pos_y + (s32) tile1->height > tile2->pos_y)) {
643 : overlap = GF_TRUE;
644 : }
645 156 : else if ((tile1->pos_y==tile2->pos_y) && (tile1->height != tile2->height)) {
646 : overlap = GF_TRUE;
647 : }
648 : }
649 :
650 490 : if (overlap) {
651 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Overlapping tiles detected, cannot merge\n"));
652 : return GF_BAD_PARAM;
653 : }
654 : }
655 :
656 102 : if (!max_cols) {
657 25 : ctx->grid = gf_malloc(sizeof(HEVCGridInfo));
658 : memset(&ctx->grid[0], 0, sizeof(HEVCGridInfo));
659 :
660 25 : ctx->grid[0].width = tile1->width;
661 25 : if (nb_rel_pos) {
662 2 : ctx->grid[0].pos_x = -tile1->pos_x - min_rel_pos_x;
663 : } else {
664 23 : ctx->grid[0].pos_x = tile1->pos_x;
665 : }
666 : max_cols=1;
667 77 : } else if (ctx->grid[max_cols-1].pos_x != tile1->pos_x) {
668 23 : if (nb_rel_pos) {
669 : //append
670 1 : if (ctx->grid[max_cols-1].pos_x <(u32) -tile1->pos_x) {
671 0 : ctx->grid = gf_realloc(ctx->grid, sizeof(HEVCGridInfo) * (max_cols+1) );
672 0 : memset(&ctx->grid[max_cols], 0, sizeof(HEVCGridInfo));
673 0 : ctx->grid[max_cols].width = tile1->width;
674 0 : ctx->grid[max_cols].pos_x = -tile1->pos_x;
675 : max_cols+=1;
676 : }
677 : //insert
678 : else {
679 : Bool found=GF_FALSE;
680 1 : for (j=0; j<max_cols; j++) {
681 1 : if (ctx->grid[j].pos_x == -tile1->pos_x) {
682 : found=GF_TRUE;
683 : break;
684 : }
685 : }
686 1 : if (!found) {
687 0 : for (j=0; j<max_cols; j++) {
688 1 : if (ctx->grid[j].pos_x > (u32) -tile1->pos_x) {
689 1 : ctx->grid = gf_realloc(ctx->grid, sizeof(HEVCGridInfo) * (max_cols+1) );
690 1 : memmove(&ctx->grid[j+1], &ctx->grid[j], sizeof(HEVCGridInfo) * (max_cols-j));
691 1 : memset(&ctx->grid[j], 0, sizeof(HEVCGridInfo));
692 1 : ctx->grid[j].width = tile1->width;
693 1 : ctx->grid[j].pos_x = -tile1->pos_x;
694 : max_cols+=1;
695 : break;
696 : }
697 : }
698 : }
699 : }
700 : } else {
701 : //append
702 22 : if (ctx->grid[max_cols-1].pos_x + ctx->grid[max_cols-1].width <= (u32) tile1->pos_x) {
703 21 : ctx->grid = gf_realloc(ctx->grid, sizeof(HEVCGridInfo) * (max_cols+1) );
704 21 : memset(&ctx->grid[max_cols], 0, sizeof(HEVCGridInfo));
705 21 : ctx->grid[max_cols].width = tile1->width;
706 21 : ctx->grid[max_cols].pos_x = tile1->pos_x;
707 : max_cols+=1;
708 : }
709 : //insert
710 : else {
711 0 : for (j=0; j<max_cols; j++) {
712 1 : if (ctx->grid[j].pos_x == (u32) tile1->pos_x) {
713 : break;
714 : }
715 1 : if (ctx->grid[j].pos_x > (u32) tile1->pos_x) {
716 1 : ctx->grid = gf_realloc(ctx->grid, sizeof(HEVCGridInfo) * (max_cols+1) );
717 1 : memmove(&ctx->grid[j+1], &ctx->grid[j], sizeof(HEVCGridInfo) * (max_cols-j));
718 1 : memset(&ctx->grid[j], 0, sizeof(HEVCGridInfo));
719 1 : ctx->grid[j].width = tile1->width;
720 1 : ctx->grid[j].pos_x = tile1->pos_x;
721 : max_cols+=1;
722 : break;
723 : }
724 : }
725 : }
726 : }
727 : }
728 : }
729 25 : if (!ctx->grid) {
730 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Failed to create grid\n"));
731 : return GF_OUT_OF_MEM;
732 : }
733 : // pass on the grid to insert empty columns
734 25 : if (!nb_rel_pos && ctx->grid) {
735 22 : for (j=0; j<max_cols-1; j++) {
736 : assert(ctx->grid[j].pos_x + ctx->grid[j].width <= ctx->grid[j+1].pos_x);
737 22 : if (ctx->grid[j].pos_x + ctx->grid[j].width != ctx->grid[j+1].pos_x) {
738 1 : u32 new_width = ctx->grid[j+1].pos_x - ctx->grid[j].pos_x - ctx->grid[j].width;
739 : u32 new_x = ctx->grid[j].pos_x + ctx->grid[j].width;
740 :
741 1 : ctx->grid = gf_realloc(ctx->grid, sizeof(HEVCGridInfo) * (max_cols+1) );
742 1 : memmove(&ctx->grid[j+2], &ctx->grid[j+1], sizeof(HEVCGridInfo) * (max_cols-j-1));
743 :
744 1 : memset(&ctx->grid[j+1], 0, sizeof(HEVCGridInfo));
745 1 : ctx->grid[j+1].width = new_width;
746 1 : ctx->grid[j+1].pos_x = new_x;
747 : max_cols+=1;
748 : j++;
749 : }
750 : }
751 : }
752 :
753 : //assign cols and rows
754 102 : for (i=0; i<nb_pids; i++) {
755 102 : HEVCTilePidCtx *tile = gf_list_get(ctx->pids, i);
756 102 : tile->pos_col=0;
757 102 : tile->pos_row=0;
758 : }
759 :
760 74 : for (j=0; j<max_cols; j++) {
761 49 : if (nb_rel_pos) {
762 3 : ctx->grid[j].height = 0;
763 : }
764 : //check non-last columns are multiple of max CU width
765 49 : if ((j+1<max_cols) && (ctx->grid[j].width % ctx->max_CU_height) ) {
766 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Invalid grid specification, column %d width %d not a multiple of max CU width and not the last one\n", j+1, ctx->grid[j].width));
767 : return GF_BAD_PARAM;
768 : }
769 243 : for (i=0; i<nb_pids; i++) {
770 243 : HEVCTilePidCtx *tile = gf_list_get(ctx->pids, i);
771 243 : if (nb_rel_pos) {
772 5 : if (-tile->pos_x != ctx->grid[j].pos_x) continue;
773 2 : if (ctx->grid[j].width != tile->width) {
774 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Invalid relative positioning in the same column of tiles with different width %d vs %d\n", tile->width, ctx->grid[j].width));
775 : return GF_BAD_PARAM;
776 : }
777 2 : tile->pos_col = j;
778 2 : tile->pos_row = -tile->pos_y;
779 2 : ctx->grid[j].height += tile->height;
780 : } else {
781 238 : if (tile->pos_x != ctx->grid[j].pos_x) continue;
782 99 : tile->pos_col = j;
783 99 : tile->pos_row = tile->pos_y / ctx->max_CU_height;
784 99 : if (tile->pos_row * ctx->max_CU_height != tile->pos_y) {
785 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] Tile y %d not a multiple of CU height %d, adjusting to next boundary\n", tile->pos_y, ctx->max_CU_height));
786 0 : tile->pos_row++;
787 : }
788 99 : if (tile->pos_y + tile->height > ctx->grid[j].height)
789 99 : ctx->grid[j].height = tile->pos_y + tile->height;
790 : }
791 :
792 101 : if (tile->pos_row > ctx->grid[j].max_row_pos)
793 59 : ctx->grid[j].max_row_pos = tile->pos_row;
794 : }
795 : }
796 : //check non-last rows are multiple of max CU height
797 49 : for (j=0; j<max_cols; j++) {
798 243 : for (i=0; i<nb_pids; i++) {
799 243 : HEVCTilePidCtx *tile = gf_list_get(ctx->pids, i);
800 243 : if (tile->pos_col != j) continue;
801 102 : if ((tile->pos_row < ctx->grid[j].max_row_pos) && (tile->height % ctx->max_CU_height)) {
802 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Invalid grid specification, row %d in column %d height %d not a multiple of max CU height and not the last one\n", tile->pos_row, j+1, tile->height));
803 : return GF_BAD_PARAM;
804 : }
805 : }
806 : }
807 : nb_cols = max_cols;
808 :
809 : } else {
810 : //gather tiles per columns
811 0 : ctx->grid = gf_malloc(sizeof(HEVCGridInfo)*nb_pids);
812 : memset(ctx->grid, 0, sizeof(HEVCGridInfo)*nb_pids);
813 :
814 : nb_cols=0;
815 0 : for (i=0; i<nb_pids; i++) {
816 : Bool found = GF_FALSE;
817 0 : HEVCTilePidCtx *apidctx = gf_list_get(ctx->pids, i);
818 : //do we fit on a col
819 0 : for (j=0; j<nb_cols; j++) {
820 0 : if (ctx->grid[j].width == apidctx->width) {
821 : found = GF_TRUE;
822 : break;
823 : }
824 : }
825 : //force new column
826 0 : if ((apidctx->height % ctx->max_CU_height) && found && ctx->grid[j].last_row_idx)
827 : found = GF_FALSE;
828 :
829 0 : if (!found) {
830 0 : ctx->grid[nb_cols].width = apidctx->width;
831 0 : ctx->grid[nb_cols].height = apidctx->height;
832 0 : if (apidctx->width % ctx->max_CU_width) {
833 : assert(!force_last_col_plus_one);
834 0 : force_last_col_plus_one = nb_cols+1;
835 : }
836 0 : if (apidctx->height % ctx->max_CU_height) {
837 0 : ctx->grid[nb_cols].last_row_idx = ctx->grid[nb_cols].row_pos +1 ;
838 : reorder_rows=GF_TRUE;
839 : }
840 :
841 0 : ctx->grid[nb_cols].row_pos = 1;
842 0 : nb_cols++;
843 : j=nb_cols-1;
844 : } else {
845 0 : ctx->grid[j].height += apidctx->height;
846 0 : if (apidctx->height % ctx->max_CU_height) {
847 0 : ctx->grid[j].last_row_idx = ctx->grid[j].row_pos+1;
848 : reorder_rows=GF_TRUE;
849 : }
850 0 : ctx->grid[j].row_pos ++;
851 : }
852 : assert(ctx->grid[j].row_pos);
853 0 : apidctx->pos_row = ctx->grid[j].row_pos - 1;
854 0 : apidctx->pos_col = j;
855 : }
856 : //move last column at end if not a multiple of CTU
857 0 : if (force_last_col_plus_one) {
858 0 : for (i=0; i<nb_pids; i++) {
859 0 : HEVCTilePidCtx *apidctx = gf_list_get(ctx->pids, i);
860 : //do we fit on a col
861 0 : if (apidctx->pos_col == force_last_col_plus_one - 1) {
862 0 : apidctx->pos_col = nb_cols-1;
863 0 : } else if (apidctx->pos_col > force_last_col_plus_one - 1) {
864 0 : apidctx->pos_col -= 1;
865 : }
866 : }
867 : }
868 : //in each column, push the last_row_idx if any to the bottom and move up other tiles accordingly
869 : //this ensures that slices with non multiple of maxCTU height are always at the bottom of the grid
870 0 : if (reorder_rows) {
871 0 : for (i=0; i<nb_cols; i++) {
872 0 : if (!ctx->grid[i].last_row_idx) continue;
873 :
874 0 : for (j=0; j<nb_pids; j++) {
875 0 : HEVCTilePidCtx *apidctx = gf_list_get(ctx->pids, j);
876 :
877 0 : if (apidctx->pos_col != i) continue;
878 :
879 0 : if (apidctx->pos_row==ctx->grid[i].last_row_idx-1) {
880 0 : apidctx->pos_row = ctx->grid[i].row_pos;
881 0 : } else if (apidctx->pos_row > ctx->grid[i].last_row_idx-1) {
882 0 : apidctx->pos_row -= 1;
883 : }
884 : }
885 : }
886 : }
887 : }
888 :
889 : //update final pos
890 25 : ctx->out_width = 0;
891 25 : ctx->out_height = 0;
892 49 : for (i=0; i<nb_cols; i++) {
893 49 : ctx->out_width += ctx->grid[i].width;
894 49 : if (ctx->grid[i].height > ctx->out_height)
895 29 : ctx->out_height = ctx->grid[i].height;
896 : }
897 25 : ctx->nb_cols = nb_cols;
898 :
899 : //recompute slice addresses
900 25 : GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("[HEVCMerge] Grid reconfigured, output size %dx%d, %d input pids:\n", ctx->out_width, ctx->out_height, nb_pids));
901 102 : for (i=0; i<nb_pids; i++) {
902 102 : HEVCTilePidCtx *pidctx = gf_list_get(ctx->pids, i);
903 102 : pidctx->slice_segment_address = hevcmerge_compute_address(ctx, pidctx, nb_abs_pos ? GF_TRUE : GF_FALSE);
904 :
905 102 : GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("- pid %s (pos %dx%d) size %dx%d new address %d\n",
906 : gf_filter_pid_get_name(pidctx->pid),
907 : nb_has_pos ? pidctx->pos_x : pidctx->pos_col,
908 : nb_has_pos ? pidctx->pos_y : pidctx->pos_row,
909 : pidctx->width, pidctx->height, pidctx->slice_segment_address));
910 : }
911 :
912 : //check if we can use a multi-row grid
913 25 : ctx->enable_multi_rows = GF_FALSE;
914 25 : if (ctx->mrows) {
915 0 : ctx->enable_multi_rows = GF_TRUE;
916 0 : ctx->nb_rows=0;
917 0 : for (i=0; i<nb_pids; i++) {
918 0 : HEVCTilePidCtx *tile = gf_list_get(ctx->pids, i);
919 0 : s32 pos_y = nb_has_pos ? tile->pos_y : tile->pos_row;
920 :
921 0 : if (!tile->pos_col)
922 0 : ctx->nb_rows++;
923 :
924 0 : for (j=0; j<nb_pids; j++) {
925 0 : if (i==j) continue;
926 0 : HEVCTilePidCtx *atile = gf_list_get(ctx->pids, j);
927 0 : s32 apos_y = nb_has_pos ? atile->pos_y : atile->pos_row;
928 0 : if (apos_y != pos_y) continue;
929 0 : if (atile->height != tile->height) {
930 0 : ctx->enable_multi_rows = GF_FALSE;
931 : break;
932 : }
933 : }
934 0 : if (!ctx->enable_multi_rows) {
935 0 : ctx->nb_rows = 0;
936 : break;
937 : }
938 : }
939 : }
940 :
941 : //sort pids according to columns (tiles) and slice addresses to be conformant with spec
942 25 : gf_list_reset(ctx->ordered_pids);
943 49 : for (i=0; i<nb_cols; i++) {
944 243 : for (j=0; j<nb_pids; j++) {
945 : u32 k;
946 : Bool inserted = GF_FALSE;
947 243 : HEVCTilePidCtx *tile = gf_list_get(ctx->pids, j);
948 : //
949 243 : if (!ctx->enable_multi_rows && (tile->pos_col != i))
950 141 : continue;
951 246 : for (k=0; k<gf_list_count(ctx->ordered_pids); k++) {
952 246 : HEVCTilePidCtx *tile2 = gf_list_get(ctx->ordered_pids, k);
953 246 : if (!ctx->enable_multi_rows && (tile2->pos_col != i))
954 168 : continue;
955 78 : if (tile2->slice_segment_address > tile->slice_segment_address) {
956 : inserted = GF_TRUE;
957 0 : gf_list_insert(ctx->ordered_pids, tile, k);
958 : break;
959 : }
960 : }
961 : if (!inserted) {
962 246 : for (k=0; k<gf_list_count(ctx->ordered_pids); k++) {
963 246 : HEVCTilePidCtx *tile2 = gf_list_get(ctx->ordered_pids, k);
964 246 : if (!ctx->enable_multi_rows) {
965 246 : if (tile2->pos_col > tile->pos_col) {
966 : inserted = GF_TRUE;
967 0 : gf_list_insert(ctx->ordered_pids, tile, k);
968 : break;
969 : }
970 : } else {
971 0 : if (tile2->slice_segment_address > tile->slice_segment_address) {
972 : inserted = GF_TRUE;
973 0 : gf_list_insert(ctx->ordered_pids, tile, k);
974 : break;
975 : }
976 : }
977 : }
978 : if (!inserted)
979 102 : gf_list_add(ctx->ordered_pids, tile);
980 : }
981 : }
982 49 : if (ctx->enable_multi_rows)
983 : break;
984 : }
985 :
986 : assert(gf_list_count(ctx->ordered_pids) == gf_list_count(ctx->pids));
987 :
988 : //build SRD map
989 25 : hevcmerge_build_srdmap(ctx, nb_abs_pos ? GF_TRUE : GF_FALSE);
990 :
991 : return GF_OK;
992 : }
993 :
994 77 : static GF_Err hevcmerge_check_sps_pps(GF_HEVCMergeCtx *ctx, HEVCTilePidCtx *pid_base, HEVCTilePidCtx *pid_o)
995 : {
996 : Bool all_ok = GF_TRUE;
997 : u32 i;
998 : HEVCState *state_base = &pid_base->hevc_state;
999 : HEVCState *state_o = &pid_o->hevc_state;
1000 77 : char *src_base = gf_filter_pid_get_source(pid_base->pid);
1001 77 : char *src_o = gf_filter_pid_get_source(pid_o->pid);
1002 :
1003 1232 : for (i=0; i<16; i++) {
1004 : HEVC_SPS *sps_base = &state_base->sps[i];
1005 : HEVC_SPS *sps_o = &state_o->sps[i];
1006 :
1007 : #define CHECK_SPS_VAL(__name) \
1008 : if (sps_base->__name != sps_o->__name ) { \
1009 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] "#__name" differs in SPS between %s and %s, undefined results\n", src_base, src_o));\
1010 : all_ok = GF_FALSE;\
1011 : }\
1012 :
1013 1232 : if (!sps_base->state && !sps_o->state) continue;
1014 77 : if (!sps_base->state && sps_o->state) {
1015 : all_ok = GF_FALSE;
1016 : break;
1017 : }
1018 77 : if (sps_base->state && !sps_o->state) {
1019 : all_ok = GF_FALSE;
1020 : break;
1021 : }
1022 77 : CHECK_SPS_VAL(aspect_ratio_info_present_flag)
1023 77 : CHECK_SPS_VAL(chroma_format_idc)
1024 77 : CHECK_SPS_VAL(cw_flag)
1025 77 : CHECK_SPS_VAL(cw_flag)
1026 77 : CHECK_SPS_VAL(cw_left)
1027 77 : CHECK_SPS_VAL(cw_right)
1028 77 : CHECK_SPS_VAL(cw_top)
1029 77 : CHECK_SPS_VAL(cw_bottom)
1030 77 : CHECK_SPS_VAL(bit_depth_luma)
1031 77 : CHECK_SPS_VAL(bit_depth_chroma)
1032 77 : CHECK_SPS_VAL(log2_max_pic_order_cnt_lsb)
1033 77 : CHECK_SPS_VAL(separate_colour_plane_flag)
1034 77 : CHECK_SPS_VAL(max_CU_depth)
1035 77 : CHECK_SPS_VAL(num_short_term_ref_pic_sets)
1036 77 : CHECK_SPS_VAL(num_long_term_ref_pic_sps)
1037 : //HEVC_ReferencePictureSets rps[64];
1038 77 : CHECK_SPS_VAL(aspect_ratio_info_present_flag)
1039 77 : CHECK_SPS_VAL(long_term_ref_pics_present_flag)
1040 77 : CHECK_SPS_VAL(temporal_mvp_enable_flag)
1041 77 : CHECK_SPS_VAL(sample_adaptive_offset_enabled_flag)
1042 77 : CHECK_SPS_VAL(sar_idc)
1043 77 : CHECK_SPS_VAL(sar_width)
1044 77 : CHECK_SPS_VAL(sar_height)
1045 : // CHECK_SPS_VAL(has_timing_info)
1046 : // CHECK_SPS_VAL(num_units_in_tick)
1047 : // CHECK_SPS_VAL(time_scale)
1048 : // CHECK_SPS_VAL(poc_proportional_to_timing_flag)
1049 77 : CHECK_SPS_VAL(num_ticks_poc_diff_one_minus1)
1050 77 : CHECK_SPS_VAL(video_full_range_flag)
1051 77 : CHECK_SPS_VAL(colour_description_present_flag)
1052 77 : CHECK_SPS_VAL(colour_primaries)
1053 77 : CHECK_SPS_VAL(transfer_characteristic)
1054 77 : CHECK_SPS_VAL(matrix_coeffs)
1055 77 : CHECK_SPS_VAL(rep_format_idx);
1056 77 : CHECK_SPS_VAL(sps_ext_or_max_sub_layers_minus1)
1057 77 : CHECK_SPS_VAL(max_sub_layers_minus1)
1058 77 : CHECK_SPS_VAL(update_rep_format_flag)
1059 77 : CHECK_SPS_VAL(sub_layer_ordering_info_present_flag)
1060 77 : CHECK_SPS_VAL(scaling_list_enable_flag)
1061 77 : CHECK_SPS_VAL(infer_scaling_list_flag)
1062 77 : CHECK_SPS_VAL(scaling_list_ref_layer_id)
1063 77 : CHECK_SPS_VAL(scaling_list_data_present_flag)
1064 77 : CHECK_SPS_VAL(asymmetric_motion_partitions_enabled_flag)
1065 77 : CHECK_SPS_VAL(pcm_enabled_flag)
1066 77 : CHECK_SPS_VAL(strong_intra_smoothing_enable_flag)
1067 77 : CHECK_SPS_VAL(vui_parameters_present_flag)
1068 77 : CHECK_SPS_VAL(log2_diff_max_min_luma_coding_block_size)
1069 77 : CHECK_SPS_VAL(log2_min_transform_block_size)
1070 77 : CHECK_SPS_VAL(log2_min_luma_coding_block_size)
1071 77 : CHECK_SPS_VAL(log2_max_transform_block_size)
1072 77 : CHECK_SPS_VAL(max_transform_hierarchy_depth_inter)
1073 77 : CHECK_SPS_VAL(max_transform_hierarchy_depth_intra)
1074 77 : CHECK_SPS_VAL(pcm_sample_bit_depth_luma_minus1)
1075 77 : CHECK_SPS_VAL(pcm_sample_bit_depth_chroma_minus1)
1076 77 : CHECK_SPS_VAL(pcm_loop_filter_disable_flag)
1077 77 : CHECK_SPS_VAL(log2_min_pcm_luma_coding_block_size_minus3)
1078 77 : CHECK_SPS_VAL(log2_diff_max_min_pcm_luma_coding_block_size)
1079 77 : CHECK_SPS_VAL(overscan_info_present)
1080 77 : CHECK_SPS_VAL(overscan_appropriate)
1081 77 : CHECK_SPS_VAL(video_signal_type_present_flag)
1082 77 : CHECK_SPS_VAL(video_format)
1083 77 : CHECK_SPS_VAL(chroma_loc_info_present_flag)
1084 77 : CHECK_SPS_VAL(chroma_sample_loc_type_top_field)
1085 77 : CHECK_SPS_VAL(chroma_sample_loc_type_bottom_field)
1086 77 : CHECK_SPS_VAL(neutra_chroma_indication_flag)
1087 77 : CHECK_SPS_VAL(field_seq_flag)
1088 77 : CHECK_SPS_VAL(frame_field_info_present_flag)
1089 77 : CHECK_SPS_VAL(default_display_window_flag)
1090 77 : CHECK_SPS_VAL(left_offset)
1091 77 : CHECK_SPS_VAL(right_offset)
1092 77 : CHECK_SPS_VAL(top_offset)
1093 77 : CHECK_SPS_VAL(bottom_offset)
1094 : }
1095 :
1096 4928 : for (i=0; i<64; i++) {
1097 : HEVC_PPS *pps_base = &state_base->pps[i];
1098 : HEVC_PPS *pps_o = &state_o->pps[i];
1099 :
1100 : #define CHECK_PPS_VAL(__name) \
1101 : if (pps_base->__name != pps_o->__name ) { \
1102 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] "#__name" differs in PPS, undefined results\n"));\
1103 : all_ok = GF_FALSE;\
1104 : }\
1105 :
1106 4928 : if (!pps_base->state && !pps_o->state) continue;
1107 77 : if (!pps_base->state && pps_o->state) {
1108 : all_ok = GF_FALSE;
1109 : break;
1110 : }
1111 77 : if (pps_base->state && !pps_o->state) {
1112 : all_ok = GF_FALSE;
1113 : break;
1114 : }
1115 : //we don't check tile config nor init qp since we rewrite these
1116 : // CHECK_PPS_VAL(loop_filter_across_tiles_enabled_flag)
1117 : // CHECK_PPS_VAL(pic_init_qp_minus26)
1118 :
1119 77 : CHECK_PPS_VAL(dependent_slice_segments_enabled_flag)
1120 77 : CHECK_PPS_VAL(num_extra_slice_header_bits)
1121 77 : CHECK_PPS_VAL(num_ref_idx_l0_default_active)
1122 77 : CHECK_PPS_VAL(num_ref_idx_l1_default_active)
1123 77 : CHECK_PPS_VAL(slice_segment_header_extension_present_flag)
1124 77 : CHECK_PPS_VAL(output_flag_present_flag)
1125 77 : CHECK_PPS_VAL(lists_modification_present_flag)
1126 77 : CHECK_PPS_VAL(cabac_init_present_flag)
1127 77 : CHECK_PPS_VAL(weighted_pred_flag)
1128 77 : CHECK_PPS_VAL(weighted_bipred_flag)
1129 77 : CHECK_PPS_VAL(slice_chroma_qp_offsets_present_flag)
1130 77 : CHECK_PPS_VAL(deblocking_filter_override_enabled_flag)
1131 77 : CHECK_PPS_VAL(loop_filter_across_slices_enabled_flag)
1132 77 : CHECK_PPS_VAL(entropy_coding_sync_enabled_flag)
1133 77 : CHECK_PPS_VAL(sign_data_hiding_flag)
1134 77 : CHECK_PPS_VAL(constrained_intra_pred_flag)
1135 77 : CHECK_PPS_VAL(transform_skip_enabled_flag)
1136 77 : CHECK_PPS_VAL(cu_qp_delta_enabled_flag)
1137 77 : CHECK_PPS_VAL(transquant_bypass_enable_flag)
1138 77 : CHECK_PPS_VAL(diff_cu_qp_delta_depth)
1139 77 : CHECK_PPS_VAL(pic_cb_qp_offset)
1140 77 : CHECK_PPS_VAL(pic_cr_qp_offset)
1141 77 : CHECK_PPS_VAL(deblocking_filter_control_present_flag)
1142 77 : CHECK_PPS_VAL(pic_disable_deblocking_filter_flag)
1143 77 : CHECK_PPS_VAL(pic_scaling_list_data_present_flag)
1144 77 : CHECK_PPS_VAL(beta_offset_div2)
1145 77 : CHECK_PPS_VAL(tc_offset_div2)
1146 77 : CHECK_PPS_VAL(log2_parallel_merge_level_minus2)
1147 : }
1148 :
1149 77 : if (src_base) gf_free(src_base);
1150 77 : if (src_o) gf_free(src_o);
1151 77 : if (all_ok || !ctx->strict) return GF_OK;
1152 : return GF_BAD_PARAM;
1153 : }
1154 :
1155 :
1156 25 : static GF_Err hevcmerge_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
1157 : {
1158 : Bool grid_config_changed = GF_FALSE;
1159 : u32 cfg_crc = 0, pid_width, pid_height;
1160 : const GF_PropertyValue *p, *dsi;
1161 : GF_Err e;
1162 : u8 j, i;
1163 25 : GF_HEVCMergeCtx *ctx = (GF_HEVCMergeCtx*)gf_filter_get_udta(filter);
1164 : HEVCTilePidCtx *tile_pid;
1165 :
1166 25 : if (ctx->in_error)
1167 : return GF_BAD_PARAM;
1168 :
1169 25 : if (is_remove) {
1170 0 : tile_pid = gf_filter_pid_get_udta(pid);
1171 0 : gf_list_del_item(ctx->pids, tile_pid);
1172 0 : gf_free(tile_pid);
1173 0 : if (!gf_list_count(ctx->pids)) {
1174 0 : if (ctx->opid) {
1175 0 : gf_filter_pid_remove(ctx->opid);
1176 0 : ctx->opid = NULL;
1177 : }
1178 : return GF_OK;
1179 : }
1180 : grid_config_changed = GF_TRUE;
1181 : goto reconfig_grid;
1182 : }
1183 :
1184 25 : if (!gf_filter_pid_check_caps(pid))
1185 : return GF_NOT_SUPPORTED;
1186 :
1187 25 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
1188 25 : if (!p) return GF_OK;
1189 25 : pid_width = p->value.uint;
1190 25 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
1191 25 : if (!p) return GF_OK;
1192 25 : pid_height = p->value.uint;
1193 :
1194 25 : dsi = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
1195 25 : if (!dsi) return GF_OK;
1196 :
1197 25 : tile_pid = gf_filter_pid_get_udta(pid);
1198 : //not set, first time we see this pid
1199 25 : if (!tile_pid) {
1200 25 : GF_SAFEALLOC(tile_pid, HEVCTilePidCtx);
1201 25 : if (!tile_pid) return GF_OUT_OF_MEM;
1202 :
1203 25 : gf_filter_pid_set_udta(pid, tile_pid);
1204 25 : tile_pid->pid = pid;
1205 25 : tile_pid->hevc_state.full_slice_header_parse = GF_TRUE;
1206 25 : gf_list_add(ctx->pids, tile_pid);
1207 :
1208 25 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
1209 : }
1210 : assert(tile_pid);
1211 :
1212 25 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
1213 25 : if (p) tile_pid->timescale = p->value.uint;
1214 :
1215 : //check if config (vps/sps/pps) has changed - if not, we ignore the reconfig
1216 : //note that we don't copy all properties of input pids to the output in this case
1217 : cfg_crc = 0;
1218 25 : if (dsi->value.data.ptr && dsi->value.data.size) {
1219 25 : cfg_crc = gf_crc_32(dsi->value.data.ptr, dsi->value.data.size);
1220 : }
1221 25 : if (cfg_crc == tile_pid->dsi_crc)
1222 : return GF_OK;
1223 25 : tile_pid->dsi_crc = cfg_crc;
1224 :
1225 : //update this pid's config by parsing sps/vps/pps and check if we need to change anything
1226 25 : GF_HEVCConfig *hvcc = gf_odf_hevc_cfg_read(dsi->value.data.ptr, dsi->value.data.size, GF_FALSE);
1227 25 : if (!hvcc) return GF_NON_COMPLIANT_BITSTREAM;
1228 25 : tile_pid->nalu_size_length = hvcc->nal_unit_size;
1229 25 : ctx->hevc_nalu_size_length = 4;
1230 : e = GF_OK;
1231 100 : for (i = 0; i < gf_list_count(hvcc->param_array); i++) {
1232 75 : GF_NALUFFParamArray *ar = (GF_NALUFFParamArray *) gf_list_get(hvcc->param_array, i);
1233 150 : for (j = 0; j < gf_list_count(ar->nalus); j++) {
1234 75 : GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(ar->nalus, j);
1235 : s32 idx = 0;
1236 :
1237 75 : if (ar->type == GF_HEVC_NALU_SEQ_PARAM) {
1238 25 : idx = gf_hevc_read_sps(sl->data, sl->size, &tile_pid->hevc_state);
1239 25 : if (idx>=0) {
1240 25 : if (!ctx->max_CU_width) {
1241 5 : ctx->max_CU_width = tile_pid->hevc_state.sps[idx].max_CU_width;
1242 20 : } else if (ctx->max_CU_width != tile_pid->hevc_state.sps[idx].max_CU_width) {
1243 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Cannot merge tiles not using the same max CU width (%d vs %d)\n", ctx->max_CU_width, tile_pid->hevc_state.sps[idx].max_CU_width));
1244 : e = GF_BAD_PARAM;
1245 : break;
1246 : }
1247 25 : if (!ctx->max_CU_height) {
1248 5 : ctx->max_CU_height = tile_pid->hevc_state.sps[idx].max_CU_height;
1249 20 : } else if (ctx->max_CU_height != tile_pid->hevc_state.sps[idx].max_CU_height) {
1250 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[HEVCMerge] Cannot merge tiles not using the same max CU height (%d vs %d)\n", ctx->max_CU_height, tile_pid->hevc_state.sps[idx].max_CU_height));
1251 : e = GF_BAD_PARAM;
1252 : break;
1253 : }
1254 : }
1255 : }
1256 50 : else if (ar->type == GF_HEVC_NALU_VID_PARAM) {
1257 25 : idx = gf_hevc_read_vps(sl->data, sl->size, &tile_pid->hevc_state);
1258 : }
1259 25 : else if (ar->type == GF_HEVC_NALU_PIC_PARAM) {
1260 25 : idx = gf_hevc_read_pps(sl->data, sl->size, &tile_pid->hevc_state);
1261 : }
1262 75 : if (idx < 0) {
1263 : // WARNING
1264 : e = GF_NON_COMPLIANT_BITSTREAM;
1265 : break;
1266 : }
1267 : }
1268 75 : if (e) break;
1269 : }
1270 25 : gf_odf_hevc_cfg_del(hvcc);
1271 25 : if (e) return e;
1272 25 : if (!ctx->opid) {
1273 5 : ctx->opid = gf_filter_pid_new(filter);
1274 5 : gf_filter_pid_copy_properties(ctx->opid, pid);
1275 : //remove all SRD related properties
1276 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD, NULL);
1277 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD_REF, NULL);
1278 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SRD_MAP, NULL);
1279 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CROP_POS, NULL);
1280 : //TODO, we might want to compute a cumulate of these properties on the output ?
1281 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_ORIG_SIZE, NULL);
1282 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DOWN_SIZE, NULL);
1283 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DOWN_RATE, NULL);
1284 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DOWN_BYTES, NULL);
1285 : }
1286 :
1287 25 : if ((pid_width != tile_pid->width) || (pid_height != tile_pid->height)) {
1288 25 : tile_pid->width = pid_width;
1289 25 : tile_pid->height = pid_height;
1290 : grid_config_changed = GF_TRUE;
1291 : }
1292 : //todo further testing we might want to force a grid reconfig even if same width / height
1293 :
1294 25 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CROP_POS);
1295 25 : if (p) {
1296 : s32 pos_x, pos_y;
1297 25 : if (!tile_pid->has_pos)
1298 : grid_config_changed = GF_TRUE;
1299 :
1300 25 : tile_pid->has_pos = GF_TRUE;
1301 25 : if (p->value.vec2i.x>0) {
1302 15 : pos_x = p->value.vec2i.x / ctx->max_CU_width;
1303 15 : if (pos_x * ctx->max_CU_width != p->value.vec2i.x) {
1304 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] CropOrigin X %d is not a multiple of max CU width %d, adjusting to next boundary\n", p->value.vec2i.x, ctx->max_CU_width));
1305 0 : pos_x++;
1306 : }
1307 15 : pos_x *= ctx->max_CU_width;
1308 : } else {
1309 : pos_x = p->value.vec2i.x;
1310 : }
1311 :
1312 25 : if (p->value.vec2i.y>0) {
1313 15 : pos_y = p->value.vec2i.y / ctx->max_CU_height;
1314 15 : if (pos_y * ctx->max_CU_height != p->value.vec2i.y) {
1315 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] CropOrigin Y %d is not a multiple of max CU height %d, adjusting to next boundary\n", p->value.vec2i.y, ctx->max_CU_height));
1316 0 : pos_y++;
1317 : }
1318 15 : pos_y *= ctx->max_CU_height;
1319 : } else {
1320 : pos_y = p->value.vec2i.y;
1321 : }
1322 25 : if ((pos_x != tile_pid->pos_x) || (pos_y != tile_pid->pos_y))
1323 : grid_config_changed = GF_TRUE;
1324 25 : tile_pid->pos_x = pos_x;
1325 25 : tile_pid->pos_y = pos_y;
1326 : } else {
1327 0 : if (tile_pid->has_pos)
1328 : grid_config_changed = GF_TRUE;
1329 0 : tile_pid->has_pos = GF_FALSE;
1330 0 : tile_pid->pos_x = tile_pid->pos_y = 0;
1331 : }
1332 :
1333 25 : reconfig_grid:
1334 : // Update grid
1335 25 : if (grid_config_changed) {
1336 25 : e = hevcmerge_rebuild_grid(ctx, pid);
1337 25 : if (e) {
1338 0 : ctx->in_error = GF_TRUE;
1339 0 : return e;
1340 : }
1341 : }
1342 :
1343 : //check SPS/PPS are compatible - for now we only warn but still process
1344 25 : tile_pid = gf_list_get(ctx->pids, 0);
1345 102 : for (i=1; i<gf_list_count(ctx->pids); i++) {
1346 77 : HEVCTilePidCtx *apidctx = gf_list_get(ctx->pids, i);
1347 77 : e = hevcmerge_check_sps_pps(ctx, tile_pid, apidctx);
1348 77 : if (e) {
1349 0 : apidctx->in_error = GF_TRUE;
1350 0 : return e;
1351 : }
1352 : }
1353 :
1354 25 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->out_width));
1355 25 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->out_height));
1356 :
1357 : //recreate DSI based on first in the ordered set of pid we have
1358 : //this avoids cases where the input pid order changes due to scheduling, and they don't have exactly the same xPS
1359 25 : tile_pid = gf_list_get(ctx->ordered_pids, 0);
1360 25 : ctx->base_pps_init_qp_delta_minus26 = tile_pid->hevc_state.pps->pic_init_qp_minus26;
1361 :
1362 25 : u32 nb_CTUs = ((ctx->out_width + ctx->max_CU_width - 1) / ctx->max_CU_width) * ((ctx->out_height + ctx->max_CU_height - 1) / ctx->max_CU_height);
1363 25 : ctx->nb_bits_per_address_dst = 0;
1364 181 : while (nb_CTUs > (u32)(1 << ctx->nb_bits_per_address_dst)) {
1365 131 : ctx->nb_bits_per_address_dst++;
1366 : }
1367 :
1368 25 : dsi = gf_filter_pid_get_property(tile_pid->pid, GF_PROP_PID_DECODER_CONFIG);
1369 : assert(dsi);
1370 25 : return hevcmerge_rewrite_config(ctx, ctx->opid, dsi->value.data.ptr, dsi->value.data.size);
1371 : }
1372 :
1373 3872 : static GF_Err hevcmerge_process(GF_Filter *filter)
1374 : {
1375 : char *data;
1376 : u32 pos, nal_length, data_size, i;
1377 : s32 current_poc=0;
1378 : u8 temporal_id, layer_id, nal_unit_type;
1379 : u32 nb_eos, nb_ipid;
1380 : Bool found_sei_prefix=GF_FALSE, found_sei_suffix=GF_FALSE;
1381 : u64 min_dts = GF_FILTER_NO_TS;
1382 : u32 min_dts_timescale=0;
1383 : GF_FilterPacket *output_pck = NULL;
1384 3872 : GF_HEVCMergeCtx *ctx = (GF_HEVCMergeCtx*) gf_filter_get_udta (filter);
1385 :
1386 3872 : if (ctx->in_error)
1387 : return GF_BAD_PARAM;
1388 : nb_eos = 0;
1389 3872 : nb_ipid = gf_list_count(ctx->pids);
1390 :
1391 : //probe input for at least one packet on each pid
1392 22977 : for (i = 0; i < nb_ipid; i++) {
1393 : u64 dts;
1394 : GF_FilterPacket *pck_src;
1395 19222 : HEVCTilePidCtx *tile_pid = gf_list_get(ctx->pids, i);
1396 19222 : if (tile_pid->in_error) {
1397 0 : nb_eos++;
1398 0 : continue;
1399 : }
1400 :
1401 19222 : pck_src = gf_filter_pid_get_packet(tile_pid->pid);
1402 19222 : if (!pck_src) {
1403 142 : if (gf_filter_pid_is_eos(tile_pid->pid)) {
1404 25 : nb_eos++;
1405 25 : continue;
1406 : }
1407 : return GF_OK;
1408 : }
1409 19080 : dts = gf_filter_pck_get_dts(pck_src);
1410 :
1411 19080 : if (dts * min_dts_timescale < min_dts * tile_pid->timescale) {
1412 : min_dts = dts;
1413 : min_dts_timescale = tile_pid->timescale;
1414 : }
1415 : }
1416 3755 : if (nb_eos == nb_ipid) {
1417 5 : gf_filter_pid_set_eos(ctx->opid);
1418 5 : return GF_EOS;
1419 : }
1420 :
1421 : //reassemble based on the ordered list of pids
1422 18750 : for (i = 0; i < nb_ipid; i++) {
1423 : u64 dts;
1424 : GF_FilterPacket *pck_src;
1425 18750 : HEVCTilePidCtx *tile_pid = gf_list_get(ctx->ordered_pids, i);
1426 : assert(tile_pid);
1427 18750 : if (tile_pid->in_error)
1428 0 : continue;
1429 18750 : pck_src = gf_filter_pid_get_packet(tile_pid->pid);
1430 18750 : if (nb_eos) {
1431 0 : if (pck_src) {
1432 0 : tile_pid->nb_pck++;
1433 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] pids of unequal duration, skipping packet %d on pid %d\n", tile_pid->nb_pck, i+1));
1434 0 : gf_filter_pid_drop_packet(tile_pid->pid);
1435 : }
1436 0 : continue;
1437 : }
1438 18750 : if (!pck_src) {
1439 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] no data on pid %d while merging, eos detected %d\n", i+1, gf_filter_pid_is_eos(tile_pid->pid) ));
1440 0 : continue;
1441 : }
1442 :
1443 18750 : dts = gf_filter_pck_get_dts(pck_src);
1444 18750 : if (dts * min_dts_timescale != min_dts * tile_pid->timescale) continue;
1445 18750 : data = (char *)gf_filter_pck_get_data(pck_src, &data_size); // data contains only a packet
1446 : // TODO: this is a clock signaling, for now just trash ..
1447 18750 : if (!data) {
1448 0 : gf_filter_pid_drop_packet(tile_pid->pid);
1449 0 : continue;
1450 : }
1451 18750 : tile_pid->nb_pck++;
1452 :
1453 : //parse the access unit for this pid
1454 18750 : gf_bs_reassign_buffer(ctx->bs_au_in, data, data_size);
1455 :
1456 75025 : while (gf_bs_available(ctx->bs_au_in)) {
1457 : u8 *output_nal;
1458 : u8 *nal_pck;
1459 : u32 nal_pck_size;
1460 :
1461 37525 : nal_length = gf_bs_read_int(ctx->bs_au_in, tile_pid->nalu_size_length * 8);
1462 37525 : pos = (u32) gf_bs_get_position(ctx->bs_au_in);
1463 37525 : gf_hevc_parse_nalu(data + pos, nal_length, &tile_pid->hevc_state, &nal_unit_type, &temporal_id, &layer_id);
1464 37525 : gf_bs_skip_bytes(ctx->bs_au_in, nal_length); //skip nal in bs
1465 :
1466 : //VCL nal, rewrite slice header
1467 37525 : if (nal_unit_type < 32) {
1468 18750 : if (!i) current_poc = tile_pid->hevc_state.s_info.poc;
1469 15000 : else if (current_poc != tile_pid->hevc_state.s_info.poc) {
1470 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[HEVCMerge] merging AU %u with different POC (%d vs %d), undefined results.\n", tile_pid->nb_pck, current_poc, tile_pid->hevc_state.s_info.poc));
1471 : }
1472 :
1473 18750 : nal_pck_size = hevcmerge_rewrite_slice(ctx, tile_pid, data + pos, nal_length);
1474 18750 : nal_pck = ctx->buffer_nal;
1475 : }
1476 : //NON-vcl, copy for SEI or drop (we should not have any SPS/PPS/VPS in the bitstream, they are in the decoder config prop)
1477 : else {
1478 18775 : gf_hevc_parse_nalu(data + pos, nal_length, &tile_pid->hevc_state, &nal_unit_type, &temporal_id, &layer_id);
1479 : // Copy SEI_PREFIX only for the first sample.
1480 18775 : if (nal_unit_type == GF_HEVC_NALU_SEI_PREFIX && !found_sei_prefix) {
1481 : found_sei_prefix = GF_TRUE;
1482 : nal_pck = data + pos;
1483 : nal_pck_size = nal_length;
1484 : }
1485 : // Copy SEI_SUFFIX only as last nalu of the sample.
1486 18770 : else if (nal_unit_type == GF_HEVC_NALU_SEI_SUFFIX && ((i+1 == nb_ipid) || !found_sei_suffix) ) {
1487 : found_sei_suffix = GF_TRUE;
1488 7500 : if (ctx->sei_suffix_alloc<nal_length) {
1489 5 : ctx->sei_suffix_alloc = nal_length;
1490 5 : ctx->sei_suffix_buf = gf_realloc(ctx->sei_suffix_buf, nal_length);
1491 : }
1492 7500 : ctx->sei_suffix_len = nal_length;
1493 7500 : memcpy(ctx->sei_suffix_buf, data+pos, nal_length);
1494 26270 : continue;
1495 : }
1496 11270 : else continue;
1497 : }
1498 18755 : if (!output_pck) {
1499 3750 : output_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->hevc_nalu_size_length + nal_pck_size, &output_nal);
1500 3750 : if (!output_pck) return GF_OUT_OF_MEM;
1501 :
1502 : // todo: might need to rewrite crypto info
1503 3750 : gf_filter_pck_merge_properties(pck_src, output_pck);
1504 : }
1505 : else {
1506 : u8 *data_start;
1507 : u32 new_size;
1508 15005 : gf_filter_pck_expand(output_pck, ctx->hevc_nalu_size_length + nal_pck_size, &data_start, &output_nal, &new_size);
1509 : }
1510 18755 : hevcmerge_write_nal(ctx, output_nal, nal_pck, nal_pck_size);
1511 : }
1512 18750 : gf_filter_pid_drop_packet(tile_pid->pid);
1513 : }
1514 : // end of loop on inputs
1515 :
1516 : //if we had a SEI suffix, append it
1517 3750 : if (ctx->sei_suffix_len) {
1518 3750 : if (output_pck) {
1519 : u8 *output_nal;
1520 : u8 *data_start;
1521 : u32 new_size;
1522 3750 : gf_filter_pck_expand(output_pck, ctx->hevc_nalu_size_length + ctx->sei_suffix_len, &data_start, &output_nal, &new_size);
1523 3750 : hevcmerge_write_nal(ctx, output_nal, ctx->sei_suffix_buf, ctx->sei_suffix_len);
1524 : }
1525 3750 : ctx->sei_suffix_len = 0;
1526 : }
1527 :
1528 3750 : if (output_pck)
1529 3750 : gf_filter_pck_send(output_pck);
1530 :
1531 : return GF_OK;
1532 : }
1533 :
1534 5 : static GF_Err hevcmerge_initialize(GF_Filter *filter)
1535 : {
1536 5 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[HEVCMerge] hevcmerge_initialize started.\n"));
1537 5 : GF_HEVCMergeCtx *ctx = (GF_HEVCMergeCtx *)gf_filter_get_udta(filter);
1538 5 : ctx->bs_au_in = gf_bs_new((char *)ctx, 1, GF_BITSTREAM_READ);
1539 5 : ctx->bs_nal_in = gf_bs_new((char *)ctx, 1, GF_BITSTREAM_READ);
1540 5 : ctx->pids = gf_list_new();
1541 5 : ctx->ordered_pids = gf_list_new();
1542 5 : return GF_OK;
1543 : }
1544 :
1545 5 : static void hevcmerge_finalize(GF_Filter *filter)
1546 : {
1547 5 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[HEVCMerge] hevcmerge_finalize.\n"));
1548 5 : GF_HEVCMergeCtx *ctx = (GF_HEVCMergeCtx *)gf_filter_get_udta(filter);
1549 5 : if (ctx->buffer_nal) gf_free(ctx->buffer_nal);
1550 5 : if (ctx->buffer_nal_no_epb) gf_free(ctx->buffer_nal_no_epb);
1551 5 : if (ctx->buffer_nal_in_no_epb) gf_free(ctx->buffer_nal_in_no_epb);
1552 5 : gf_bs_del(ctx->bs_au_in);
1553 5 : gf_bs_del(ctx->bs_nal_in);
1554 5 : if (ctx->bs_nal_out)
1555 5 : gf_bs_del(ctx->bs_nal_out);
1556 :
1557 5 : if (ctx->grid) gf_free(ctx->grid);
1558 30 : while (gf_list_count(ctx->pids)) {
1559 25 : HEVCTilePidCtx *pctx = gf_list_pop_back(ctx->pids);
1560 25 : gf_free(pctx);
1561 : }
1562 5 : gf_list_del(ctx->pids);
1563 5 : gf_list_del(ctx->ordered_pids);
1564 :
1565 5 : if (ctx->sei_suffix_buf) gf_free(ctx->sei_suffix_buf);
1566 :
1567 5 : }
1568 :
1569 : static const GF_FilterCapability HEVCMergeCaps[] =
1570 : {
1571 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1572 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_HEVC),
1573 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
1574 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1575 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_HEVC)
1576 : };
1577 :
1578 : #define OFFS(_n) #_n, offsetof(GF_HEVCMergeCtx, _n)
1579 :
1580 : static const GF_FilterArgs HEVCMergeArgs[] =
1581 : {
1582 : { OFFS(strict), "strict comparison of SPS and PPS of input pids - see filter help", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1583 : { OFFS(mrows), "signal multiple rows in tile grid when possible", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1584 : {0}
1585 : };
1586 :
1587 : GF_FilterRegister HEVCMergeRegister = {
1588 : .name = "hevcmerge",
1589 : GF_FS_SET_DESCRIPTION("HEVC Tile merger")
1590 : GF_FS_SET_HELP("This filter merges a set of HEVC PIDs into a single motion-constrained tiled HEVC PID.\n"
1591 : "The filter creates a tiling grid with a single row and as many columns as needed.\n"
1592 : "If [-mrows]() is set and tiles properly align on the final grid, multiple rows will be declared in the PPS.\n"
1593 : "Positioning of tiles can be automatic (implicit) or explicit.\n"
1594 : "The filter will check the SPS and PPS configurations of input PID and warn if they are not aligned but will still process them unless [-strict]() is set.\n"
1595 : "The filter assumes that all input PIDs are synchronized (frames share the same timestamp) and will reassemble frames with the same dts. If pids are of unequal duration, the filter will drop frames as soon as one pid is over.\n"
1596 : "## Implicit Positioning\n"
1597 : "In implicit positioning, results may vary based on the order of input pids declaration.\n"
1598 : "In this mode the filter will automatically allocate new columns for tiles with height not a multiple of max CU height.\n"
1599 : "## Explicit Positioning\n"
1600 : "In explicit positioning, the `CropOrigin` property on input PIDs is used to setup the tile grid. In this case, tiles shall not overlap in the final output.\n"
1601 : "If `CropOrigin` is used, it shall be set on all input sources.\n"
1602 : "If positive coordinates are used, they specify absolute positioning in pixels of the tiles. The coordinates are automatically adjusted to the next multiple of max CU width and height.\n"
1603 : "If negative coordinates are used, they specify relative positioning (eg `0x-1` indicates to place the tile below the tile 0x0).\n"
1604 : "In this mode, it is the caller responsibility to set coordinates so that all tiles in a column have the same width and only the last row/column uses non-multiple of max CU width/height values. The filter will complain and abort if this is not respected.\n"
1605 : "- If an horizontal blank is detected in the layout, an empty column in the tiling grid will be inserted.\n"
1606 : "- If a vertical blank is detected in the layout, it is ignored.\n"
1607 : " \n"
1608 : "## Spatial Relationship Description (SRD)\n"
1609 : "\n"
1610 : "The filter will create an `SRDMap` property in the output PID if `SRDRef` and `SRD` or `CropOrigin` are set on all input PIDs.\n"
1611 : "The `SRDMap` allows forwarding the logical sources `SRD` in the merged PID.\n"
1612 : "The output pid `SRDRef` is set to the output video size.\n"
1613 : "The input `SRDRef` and `SRD` are usually specified in DASH MPD, but can be manually assigned to inputs.\n"
1614 : "- `SRDRef` gives the size of the referential used for the input `SRD` (usually matches the original video size, but not always)\n"
1615 : "- `SRD` gives the size and position of the input in the original video, expressed in `SRDRef` referential of the input.\n"
1616 : "The inputs do not need to have matching `SRDRef`."
1617 : "EX src1:SRD=0x0x640x480:SRDRef=1280x720\n"
1618 : "This indicates that `src1` contains a video located at 0,0, with a size of 640x480 pixels in a virtual source of 1280x720 pixels.\n"
1619 : "EX src2:SRD=640x0x640x480:SRDRef=1280x720\n"
1620 : "This indicates that `src1` contains a video located at 640,0, with a size of 640x480 pixels in a virtual source of 1280x720 pixels.\n"
1621 : " \n"
1622 : "Each merged input is described by 8 intergers in the output `SRDMap`:\n"
1623 : "- the source `SRD` is rescaled in the output `SRDRef` to form the first part (4 integers) of the `SRDMap` (i.e. __where was the input ?__)\n"
1624 : "- the source location in the reconstructed video forms the second part (4 integers) of the `SRDMap` (i.e. __where are the input pixels in the output ?__)\n"
1625 : "Assuming the two sources are encoded at 320x240 and merged as src2 above src1, the output will be a 320x480 video with a `SRDMap` of {0,160,160,240,0,0,320,240,0,0,160,240,0,240,320,240}\n"
1626 : "Note: merged inputs are always listed in `SRDMap` in their tile order in the output bitstream.\n"
1627 : "\n"
1628 : "Alternatively to using `SRD` and `SRDRef`, it is possible to specify `CropOrigin` property on the inputs, in which case:\n"
1629 : "- the `CropOrigin` gives the location in the source\n"
1630 : "- the input size gives the size in the source, and no rescaling of referential is done\n"
1631 : "EX src1:CropOrigin=0x0 src1:CropOrigin=640x0 \n"
1632 : "Assuming the two sources are encoded at 320x240 and merged as src1 above src2, the output will be a 320x480 video with a `SRDMap` of {0,0,320,240,0,0,320,240,640,0,320,240,0,240,320,240}\n"
1633 : )
1634 : .private_size = sizeof(GF_HEVCMergeCtx),
1635 : SETCAPS(HEVCMergeCaps),
1636 : .flags = GF_FS_REG_EXPLICIT_ONLY,
1637 : .initialize = hevcmerge_initialize,
1638 : .finalize = hevcmerge_finalize,
1639 : .args = HEVCMergeArgs,
1640 : .configure_pid = hevcmerge_configure_pid,
1641 : .process = hevcmerge_process,
1642 : .max_extra_pids = -1,
1643 : };
1644 :
1645 2877 : const GF_FilterRegister *hevcmerge_register(GF_FilterSession *session)
1646 : {
1647 2877 : return &HEVCMergeRegister;
1648 : }
1649 :
1650 : #else
1651 : const GF_FilterRegister *hevcmerge_register(GF_FilterSession *session)
1652 : {
1653 : return NULL;
1654 : }
1655 : #endif // GPAC_DISABLE_AV_PARSERS
|