LCOV - code coverage report
Current view: top level - filters - hevcmerge.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 630 760 82.9 %
Date: 2021-04-29 23:48:07 Functions: 12 12 100.0 %

          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

Generated by: LCOV version 1.13