LCOV - code coverage report
Current view: top level - filters - write_nhnt.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 134 176 76.1 %
Date: 2021-04-29 23:48:07 Functions: 5 5 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 / NHNT stream to file 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             : #include <gpac/filters.h>
      27             : #include <gpac/constants.h>
      28             : #include <gpac/bitstream.h>
      29             : 
      30             : #include <gpac/internal/isomedia_dev.h>
      31             : 
      32             : 
      33             : typedef struct
      34             : {
      35             :         //opts
      36             :         Bool exporter, large;
      37             : 
      38             :         //only one input pid declared
      39             :         GF_FilterPid *ipid;
      40             :         //only one output pid declared
      41             :         GF_FilterPid *opid_nhnt, *opid_mdia, *opid_info;
      42             : 
      43             :         u32 codecid;
      44             :         u32 streamtype;
      45             :         u32 oti;
      46             : 
      47             :         const char *dcfg;
      48             :         u32 dcfg_size;
      49             : 
      50             :         GF_Fraction64 duration;
      51             :         Bool first;
      52             : 
      53             :         GF_BitStream *bs;
      54             :         u64 mdia_pos;
      55             :         Bool configure_side_streams;
      56             : } GF_NHNTDumpCtx;
      57             : 
      58           1 : GF_Err nhntdump_config_side_streams(GF_Filter *filter, GF_NHNTDumpCtx *ctx)
      59             : {
      60             :         const GF_PropertyValue *p;
      61             :         char *name, *url, *res_name;
      62             :         char fileName[GF_MAX_PATH+1];
      63             :         GF_FileIO *gfio = NULL;
      64             :         GF_Err e;
      65             : 
      66           1 :         url = gf_filter_pid_get_destination(ctx->opid_nhnt);
      67           1 :         if (url) {
      68           0 :                 if (!strncmp(url, "gfio://", 7)) {
      69           0 :                         gfio = gf_fileio_from_url(url);
      70           0 :                         strncpy(fileName, gf_fileio_translate_url(url), GF_MAX_PATH);
      71             :                 } else {
      72             :                         strncpy(fileName, url, GF_MAX_PATH);
      73             :                 }
      74           0 :                 fileName[GF_MAX_PATH] = 0;
      75           0 :                 gf_free(url);
      76             :         } else {
      77             :                 strcpy(fileName, "dump");
      78             :         }
      79             : 
      80           1 :         name = gf_file_ext_start(fileName);
      81           1 :         if (name) {
      82           0 :                 name[0] = 0;
      83             :         }
      84           1 :         if (!ctx->opid_mdia)
      85           1 :                 ctx->opid_mdia = gf_filter_pid_new(filter);
      86             : 
      87           1 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_DECODER_CONFIG);
      88           1 :         if (p) {
      89           1 :                 ctx->dcfg = p->value.data.ptr;
      90           1 :                 ctx->dcfg_size = p->value.data.size;
      91             : 
      92           1 :                 if (!ctx->opid_info)
      93           1 :                         ctx->opid_info = gf_filter_pid_new(filter);
      94             : 
      95           0 :         } else if (ctx->opid_info) {
      96           0 :                 gf_filter_pid_remove(ctx->opid_info);
      97           0 :                 ctx->opid_info = NULL;
      98             :         }
      99             : 
     100           1 :         gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
     101           1 :         gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_FILE_EXT, &PROP_STRING("media") );
     102           1 :         gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_MIME, &PROP_STRING("application/x-nhnt") );
     103             : 
     104           1 :         if (!ctx->exporter) {
     105           0 :                 name = gf_file_ext_start(fileName);
     106           0 :                 if (name) name[0] = 0;
     107             :                 strcat(fileName, ".media");
     108           0 :                 if (gfio) {
     109           0 :                         res_name = (char *) gf_fileio_factory(gfio, gf_file_basename(fileName) );
     110             :                 } else {
     111             :                         res_name = fileName;
     112             :                 }
     113           0 :                 gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_OUTPATH, &PROP_STRING(res_name) );
     114             : 
     115           0 :                 GF_Filter *o_media = gf_filter_connect_destination(filter, res_name, &e);
     116           0 :                 if (o_media) gf_filter_set_source(o_media, filter, NULL);
     117             :         }
     118             : 
     119             : 
     120           1 :         if (ctx->opid_info) {
     121           1 :                 gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
     122           1 :                 gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_FILE_EXT, &PROP_STRING("info") );
     123           1 :                 gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_MIME, &PROP_STRING("application/x-nhnt") );
     124             : 
     125           1 :                 if (!ctx->exporter) {
     126           0 :                         name = gf_file_ext_start(fileName);
     127           0 :                         if (name) name[0] = 0;
     128             :                         strcat(fileName, ".info");
     129           0 :                         if (gfio) {
     130           0 :                                 res_name = (char *) gf_fileio_factory(gfio, gf_file_basename(fileName) );
     131             :                         } else {
     132             :                                 res_name = fileName;
     133             :                         }
     134           0 :                         gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_OUTPATH, &PROP_STRING(res_name) );
     135             : 
     136           0 :                         GF_Filter *o_info = gf_filter_connect_destination(filter, res_name, &e);
     137           0 :                         if (o_info) gf_filter_set_source(o_info, filter, NULL);
     138             :                 }
     139             : 
     140             :         }
     141           1 :         ctx->configure_side_streams = GF_FALSE;
     142           1 :         return GF_OK;
     143             : }
     144             : 
     145           1 : GF_Err nhntdump_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     146             : {
     147             :         u32 cid, chan, sr, bps, w, h;
     148             :         const char *name;
     149             :         const GF_PropertyValue *p;
     150           1 :         GF_NHNTDumpCtx *ctx = gf_filter_get_udta(filter);
     151             : 
     152           1 :         if (is_remove) {
     153           0 :                 ctx->ipid = NULL;
     154           0 :                 if (ctx->opid_nhnt) {
     155           0 :                         gf_filter_pid_remove(ctx->opid_nhnt);
     156           0 :                         ctx->opid_nhnt = NULL;
     157             :                 }
     158           0 :                 if (ctx->opid_mdia) {
     159           0 :                         gf_filter_pid_remove(ctx->opid_mdia);
     160           0 :                         ctx->opid_mdia = NULL;
     161             :                 }
     162           0 :                 if (ctx->opid_info) {
     163           0 :                         gf_filter_pid_remove(ctx->opid_info);
     164           0 :                         ctx->opid_info = NULL;
     165             :                 }
     166             :                 return GF_OK;
     167             :         }
     168           1 :         if (! gf_filter_pid_check_caps(pid))
     169             :                 return GF_NOT_SUPPORTED;
     170             : 
     171           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
     172           1 :         if (!p) return GF_NOT_SUPPORTED;
     173           1 :         cid = p->value.uint;
     174             : 
     175           1 :         if (ctx->codecid == cid) {
     176             :                 return GF_OK;
     177             :         }
     178           1 :         ctx->codecid = cid;
     179           1 :         ctx->configure_side_streams = GF_TRUE;
     180             : 
     181           1 :         if (ctx->codecid<GF_CODECID_LAST_MPEG4_MAPPING) ctx->oti = ctx->codecid;
     182             :         else {
     183           0 :                 ctx->oti = gf_codecid_oti(ctx->codecid);
     184             :         }
     185           1 :         if (!ctx->oti) {
     186           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("CodecID %s has no mapping to MPEG-4 systems, cannot use NHNT. Use NHML instead\n", gf_4cc_to_str(cid) ));
     187             :                 return GF_NOT_SUPPORTED;
     188             :         }
     189             : 
     190           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
     191           1 :         ctx->streamtype = p ? p->value.uint : GF_STREAM_UNKNOWN;
     192             : 
     193           1 :         if (!ctx->opid_nhnt)
     194           1 :                 ctx->opid_nhnt = gf_filter_pid_new(filter);
     195             : 
     196           1 :         ctx->ipid = pid;
     197             : 
     198             : 
     199           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
     200           1 :         sr = p ? p->value.uint : 0;
     201           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
     202           1 :         chan = p ? p->value.uint : 0;
     203           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
     204           1 :         bps = p ? gf_audio_fmt_bit_depth(p->value.uint) : 16;
     205           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
     206           1 :         w = p ? p->value.uint : 0;
     207           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
     208           1 :         h = p ? p->value.uint : 0;
     209             : 
     210             : 
     211           1 :         name = gf_codecid_name(ctx->codecid);
     212           1 :         if (ctx->exporter) {
     213           1 :                 if (w && h) {
     214           1 :                         GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - Size %dx%d\n", name, w, h));
     215           0 :                 } else if (sr && chan) {
     216           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - SampleRate %d %d channels %d bits per sample\n", name, sr, chan, bps));
     217             :                 } else {
     218           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s\n", name));
     219             :                 }
     220             :         }
     221             : 
     222           1 :         gf_filter_pid_set_property(ctx->opid_nhnt, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
     223           1 :         gf_filter_pid_set_property(ctx->opid_nhnt, GF_PROP_PID_FILE_EXT, &PROP_STRING("nhnt") );
     224           1 :         gf_filter_pid_set_property(ctx->opid_nhnt, GF_PROP_PID_MIME, &PROP_STRING("application/x-nhnt") );
     225             : 
     226             : 
     227           1 :         ctx->first = GF_TRUE;
     228             : 
     229           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DURATION);
     230           1 :         if (p) ctx->duration = p->value.lfrac;
     231           1 :         gf_filter_pid_set_framing_mode(pid, GF_TRUE);
     232           1 :         return GF_OK;
     233             : }
     234             : 
     235         175 : GF_Err nhntdump_process(GF_Filter *filter)
     236             : {
     237         175 :         GF_NHNTDumpCtx *ctx = gf_filter_get_udta(filter);
     238             :         GF_FilterPacket *pck, *dst_pck;
     239             :         u8 *output;
     240             :         u32 size, pck_size;
     241             :         u64 dts, cts;
     242             : 
     243         175 :         if (ctx->configure_side_streams) {
     244           1 :                 return nhntdump_config_side_streams(filter, ctx);
     245             :         }
     246             : 
     247         174 :         pck = gf_filter_pid_get_packet(ctx->ipid);
     248         174 :         if (!pck) {
     249           1 :                 if (gf_filter_pid_is_eos(ctx->ipid)) {
     250           1 :                         gf_filter_pid_set_eos(ctx->opid_nhnt);
     251           1 :                         gf_filter_pid_set_eos(ctx->opid_mdia);
     252           1 :                         if (ctx->opid_info) gf_filter_pid_set_eos(ctx->opid_info);
     253             :                         return GF_EOS;
     254             :                 }
     255             :                 return GF_OK;
     256             :         }
     257             : 
     258         173 :         if (ctx->first) {
     259             :                 u32 nhnt_hdr_size = 4+1+1+1+2+3+4+4+4;
     260             :                 const GF_PropertyValue *p;
     261             : 
     262           1 :                 dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhnt, nhnt_hdr_size, &output);
     263           1 :                 if (!dst_pck) return GF_OUT_OF_MEM;
     264             : 
     265           1 :                 if (!ctx->bs) ctx->bs = gf_bs_new(output, nhnt_hdr_size, GF_BITSTREAM_WRITE);
     266           0 :                 else gf_bs_reassign_buffer(ctx->bs, output, nhnt_hdr_size);
     267             : 
     268             :                 /*write header*/
     269             :                 /*'NHnt' format*/
     270           1 :                 gf_bs_write_data(ctx->bs, ctx->large ? "NHnl" : "NHnt", 4);
     271             :                 /*version 1*/
     272           1 :                 gf_bs_write_u8(ctx->bs, 0);
     273             :                 /*streamType*/
     274           1 :                 gf_bs_write_u8(ctx->bs, ctx->streamtype);
     275             :                 /*OTI*/
     276           1 :                 gf_bs_write_u8(ctx->bs, ctx->oti);
     277             :                 /*reserved*/
     278           1 :                 gf_bs_write_u16(ctx->bs, 0);
     279             :                 /*bufferDB*/
     280             :                 //p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_DB_SIZE);
     281           1 :                 gf_bs_write_u24(ctx->bs, 0);
     282             :                 /*avg BitRate*/
     283           1 :                 p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_BITRATE);
     284           1 :                 gf_bs_write_u32(ctx->bs, p ? p->value.uint : 0);
     285             :                 /*max bitrate*/
     286             :                 //p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_MAX_RATE);
     287           1 :                 gf_bs_write_u32(ctx->bs, 0);
     288             :                 /*timescale*/
     289           1 :                 p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_TIMESCALE);
     290           1 :                 gf_bs_write_u32(ctx->bs, p ? p->value.uint : 1000);
     291             : 
     292           1 :                 gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
     293           1 :                 gf_filter_pck_send(dst_pck);
     294             : 
     295           1 :                 if (ctx->opid_info) {
     296           1 :                         dst_pck = gf_filter_pck_new_shared(ctx->opid_info, ctx->dcfg, ctx->dcfg_size, NULL);
     297           1 :                         if (dst_pck) {
     298           1 :                                 gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
     299           1 :                                 gf_filter_pck_set_readonly(dst_pck);
     300           1 :                                 gf_filter_pck_send(dst_pck);
     301             :                         }
     302             :                 }
     303             :         }
     304             : 
     305             :         //get media data
     306         173 :         gf_filter_pck_get_data(pck, &pck_size);
     307             : 
     308             :         //nhnt data size
     309         173 :         size = 3 + 1 + 3*(ctx->large ? 8 : 4);
     310         173 :         dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhnt, size, &output);
     311         173 :         if (!dst_pck) return GF_OUT_OF_MEM;
     312             : 
     313             :         //send nhnt data
     314         173 :         gf_bs_reassign_buffer(ctx->bs, output, size);
     315             : 
     316             :         /*dump nhnt info*/
     317         173 :         gf_bs_write_u24(ctx->bs, pck_size);
     318         173 :         gf_bs_write_int(ctx->bs, gf_filter_pck_get_sap(pck) ? 1 : 0, 1);
     319             :         /*AU start & end flag always true*/
     320         173 :         gf_bs_write_int(ctx->bs, 1, 1);
     321         173 :         gf_bs_write_int(ctx->bs, 1, 1);
     322             :         /*reserved*/
     323         173 :         gf_bs_write_int(ctx->bs, 0, 3);
     324         173 :         gf_bs_write_int(ctx->bs, gf_filter_pck_get_sap(pck) ? 0 : 1, 2);
     325             : 
     326         173 :         dts = gf_filter_pck_get_dts(pck);
     327         173 :         cts = gf_filter_pck_get_cts(pck);
     328         173 :         if (ctx->large) {
     329           0 :                 gf_bs_write_u64(ctx->bs, ctx->mdia_pos);
     330           0 :                 gf_bs_write_u64(ctx->bs, cts);
     331           0 :                 gf_bs_write_u64(ctx->bs, dts);
     332             :         } else {
     333         173 :                 gf_bs_write_u32(ctx->bs, (u32) ctx->mdia_pos);
     334         173 :                 gf_bs_write_u32(ctx->bs, (u32) cts);
     335         173 :                 gf_bs_write_u32(ctx->bs, (u32) dts);
     336             :         }
     337         173 :         ctx->mdia_pos += pck_size;
     338         173 :         gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
     339         173 :         gf_filter_pck_send(dst_pck);
     340             : 
     341             :         //send the complete data packet
     342         173 :         dst_pck = gf_filter_pck_new_ref(ctx->opid_mdia, 0, pck_size, pck);
     343         173 :         if (!dst_pck) return GF_OUT_OF_MEM;
     344             :         
     345         173 :         gf_filter_pck_merge_properties(pck, dst_pck);
     346             :         //keep byte offset ?
     347             : //      gf_filter_pck_set_byte_offset(dst_pck, GF_FILTER_NO_BO);
     348             : 
     349         173 :         gf_filter_pck_set_framing(dst_pck, ctx->first, GF_FALSE);
     350         173 :         gf_filter_pck_send(dst_pck);
     351             : 
     352         173 :         ctx->first = GF_FALSE;
     353             : 
     354         173 :         if (ctx->exporter) {
     355         173 :                 u32 timescale = gf_filter_pck_get_timescale(pck);
     356         173 :                 u64 ts = gf_filter_pck_get_cts(pck);
     357         173 :                 gf_set_progress("Exporting", ts*ctx->duration.den, ctx->duration.num*timescale);
     358             :         }
     359             : 
     360         173 :         gf_filter_pid_drop_packet(ctx->ipid);
     361             : 
     362         173 :         return GF_OK;
     363             : }
     364             : 
     365           1 : static void nhntdump_finalize(GF_Filter *filter)
     366             : {
     367           1 :         GF_NHNTDumpCtx *ctx = gf_filter_get_udta(filter);
     368           1 :         if (ctx->bs) gf_bs_del(ctx->bs);
     369           1 : }
     370             : 
     371             : static const GF_FilterCapability NHNTDumpCaps[] =
     372             : {
     373             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     374             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
     375             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     376             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     377             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, "nhnt"),
     378             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, "application/x-nhnt"),
     379             : };
     380             : 
     381             : #define OFFS(_n)        #_n, offsetof(GF_NHNTDumpCtx, _n)
     382             : static const GF_FilterArgs NHNTDumpArgs[] =
     383             : {
     384             :         { OFFS(exporter), "compatibility with old exporter, displays export results", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     385             :         { OFFS(large), "use large file mode", GF_PROP_BOOL, "false", NULL, 0},
     386             :         {0}
     387             : };
     388             : 
     389             : 
     390             : GF_FilterRegister NHNTDumpRegister = {
     391             :         .name = "nhntw",
     392             :         GF_FS_SET_DESCRIPTION("NHNT writer")
     393             :         GF_FS_SET_HELP("This filter converts a single stream to an NHNT output file.\n"
     394             :         "NHNT documentation is available at https://wiki.gpac.io/NHNT-Format\n")
     395             :         .private_size = sizeof(GF_NHNTDumpCtx),
     396             :         .args = NHNTDumpArgs,
     397             :         .finalize = nhntdump_finalize,
     398             :         SETCAPS(NHNTDumpCaps),
     399             :         .configure_pid = nhntdump_configure_pid,
     400             :         .process = nhntdump_process
     401             : };
     402             : 
     403        2877 : const GF_FilterRegister *nhntdump_register(GF_FilterSession *session)
     404             : {
     405        2877 :         return &NHNTDumpRegister;
     406             : }
     407             : 

Generated by: LCOV version 1.13