LCOV - code coverage report
Current view: top level - filters - tileagg.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 145 161 90.1 %
Date: 2021-04-29 23:48:07 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.13