LCOV - code coverage report
Current view: top level - filters - write_qcp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 138 175 78.9 %
Date: 2021-04-29 23:48:07 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / QCP 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/media_dev.h>
      31             : 
      32             : 
      33             : typedef struct
      34             : {
      35             :         //opts
      36             :         Bool exporter, mpeg2;
      37             : 
      38             :         //only one input pid declared
      39             :         GF_FilterPid *ipid;
      40             :         //only one output pid declared
      41             :         GF_FilterPid *opid;
      42             : 
      43             :         u32 codecid;
      44             :         Bool first;
      45             : 
      46             :         GF_Fraction64 duration;
      47             : 
      48             :         char GUID[16];
      49             :         u32 qcp_type, needs_rate_byte;
      50             :         QCPRateTable rtable[8];
      51             :         unsigned int *qcp_rates, rt_cnt;        /*contains constants*/
      52             :         Bool has_qcp_pad;
      53             :         Bool needs_final_pach;
      54             :         u32 data_size;
      55             :         u32 nb_frames;
      56             : 
      57             : } GF_QCPMxCtx;
      58             : 
      59             : 
      60             : /*QCP codec GUIDs*/
      61             : static const char *QCP_QCELP_GUID_1 = "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E";
      62             : static const char *QCP_EVRC_GUID = "\x8D\xD4\x89\xE6\x76\x90\xB5\x46\x91\xEF\x73\x6A\x51\x00\xCE\xB4";
      63             : static const char *QCP_SMV_GUID = "\x75\x2B\x7C\x8D\x97\xA7\x46\xED\x98\x5E\xD5\x3C\x8C\xC7\x5F\x84";
      64             : 
      65           1 : GF_Err qcpmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      66             : {
      67             :         u32 sr, chan, bps;
      68             :         const GF_PropertyValue *p;
      69           1 :         GF_QCPMxCtx *ctx = gf_filter_get_udta(filter);
      70             : 
      71           1 :         if (is_remove) {
      72           0 :                 ctx->ipid = NULL;
      73           0 :                 if (ctx->opid) {
      74           0 :                         gf_filter_pid_remove(ctx->opid);
      75           0 :                         ctx->opid = NULL;
      76             :                 }
      77             :                 return GF_OK;
      78             :         }
      79           1 :         if (! gf_filter_pid_check_caps(pid))
      80             :                 return GF_NOT_SUPPORTED;
      81             : 
      82           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
      83           1 :         if (!p) return GF_NOT_SUPPORTED;
      84           1 :         ctx->codecid = p->value.uint;
      85             : 
      86           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
      87           1 :         if (!p) return GF_NOT_SUPPORTED;
      88           1 :         sr = p->value.uint;
      89             : 
      90           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
      91           1 :         if (!p) return GF_NOT_SUPPORTED;
      92           1 :         chan = p->value.uint;
      93             : 
      94           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
      95           1 :         bps = p ? gf_audio_fmt_bit_depth(p->value.uint) : 16;
      96             : 
      97             : 
      98           1 :         if (!ctx->opid) {
      99           1 :                 ctx->opid = gf_filter_pid_new(filter);
     100           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
     101             :         }
     102             : 
     103           1 :         switch (ctx->codecid) {
     104           1 :         case GF_CODECID_QCELP:
     105           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("qcp") );
     106           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
     107           1 :                 ctx->qcp_type = 1;
     108           1 :                 memcpy(ctx->GUID, QCP_QCELP_GUID_1, sizeof(char)*16);
     109             :                 break;
     110           0 :         case GF_CODECID_EVRC_PV:
     111             :         case GF_CODECID_EVRC:
     112           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("evc") );
     113           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
     114           0 :                 memcpy(ctx->GUID, QCP_EVRC_GUID, sizeof(char)*16);
     115           0 :                 ctx->qcp_type = 3;
     116           0 :                 break;
     117           0 :         case GF_CODECID_SMV:
     118           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("smv") );
     119           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
     120           0 :                 ctx->qcp_type = 2;
     121           0 :                 memcpy(ctx->GUID, QCP_SMV_GUID, sizeof(char)*16);
     122             :                 break;
     123             :         }
     124             : 
     125           1 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("qcp") );
     126           1 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
     127           1 :         ctx->first = GF_TRUE;
     128             : 
     129           1 :         if (ctx->exporter) {
     130           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - SampleRate %d %d channels %d bits per sample\n", gf_codecid_name(ctx->codecid), sr, chan, bps));
     131             :         }
     132             : 
     133           1 :         ctx->ipid = pid;
     134           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DURATION);
     135           1 :         if (p) ctx->duration = p->value.lfrac;
     136             : 
     137           1 :         gf_filter_pid_set_framing_mode(pid, GF_TRUE);
     138             : 
     139           1 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_MEDIA_DATA_SIZE);
     140           1 :         if (!p) {
     141           1 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[QCP] Unknown total media size, cannot write QCP file right away\n"));
     142           1 :                 ctx->data_size = 0;
     143             :         } else {
     144           0 :                 ctx->data_size = (u32) p->value.longuint;
     145             :         }
     146           1 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_NB_FRAMES);
     147           1 :         if (!p) {
     148           1 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[QCP] Unknown total number of media frames, cannot write QCP file\n"));
     149           1 :                 ctx->nb_frames = 0;
     150             :         } else {
     151           0 :                 ctx->nb_frames = (u32) p->value.uint;
     152             :         }
     153             : 
     154           1 :         if (!ctx->data_size || !ctx->nb_frames) {
     155           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DISABLE_PROGRESSIVE, &PROP_UINT(GF_PID_FILE_PATCH_REPLACE) );
     156           1 :                 ctx->needs_final_pach = GF_TRUE;
     157             :         }
     158             : 
     159             :         return GF_OK;
     160             : }
     161             : 
     162           2 : static void qcpmx_send_header(GF_QCPMxCtx *ctx, u32 data_size, u32 frame_count)
     163             : {
     164             :         const GF_PropertyValue *p;
     165             :         Bool needs_rate_octet;
     166             :         char szName[80];
     167             :         u32 i, tot_size, size, sample_size, avg_rate;
     168             :         u32 block_size = 160;
     169             :         u32 sample_rate = 8000;
     170             :         GF_BitStream *bs;
     171             :         u8 *output;
     172             :         GF_FilterPacket *dst_pck;
     173             : 
     174           2 :         if (ctx->qcp_type==1) {
     175           2 :                 ctx->qcp_rates = (unsigned int*)GF_QCELP_RATE_TO_SIZE;
     176           2 :                 ctx->rt_cnt = GF_QCELP_RATE_TO_SIZE_NB;
     177             :         } else {
     178           0 :                 ctx->qcp_rates = (unsigned int*)GF_SMV_EVRC_RATE_TO_SIZE;
     179           0 :                 ctx->rt_cnt = GF_SMV_EVRC_RATE_TO_SIZE_NB;
     180             :         }
     181             : 
     182             :         /*dumps full table...*/
     183          14 :         for (i=0; i<ctx->rt_cnt; i++) {
     184          14 :                 ctx->rtable[i].rate_idx = ctx->qcp_rates[2*i];
     185          14 :                 ctx->rtable[i].pck_size = ctx->qcp_rates[2*i+1];
     186             :         }
     187             : 
     188           2 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FRAME_SIZE);
     189           2 :         sample_size = p ? p->value.uint : block_size;
     190             : 
     191             :         /*check sample format - packetvideo doesn't include rate octet...*/
     192           2 :         needs_rate_octet = (ctx->codecid==GF_CODECID_EVRC_PV) ? GF_TRUE : GF_FALSE;
     193             : 
     194           2 :         if (needs_rate_octet) data_size += frame_count;
     195           2 :         ctx->has_qcp_pad = (data_size % 2) ? GF_TRUE : GF_FALSE;
     196             : 
     197           2 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_BITRATE);
     198           2 :         if (p) avg_rate = p->value.uint;
     199           0 :         else avg_rate = frame_count ? 8*data_size*sample_rate/frame_count/block_size : 0;
     200             : 
     201             :         /*QLCM + fmt + vrat + data*/
     202           2 :         size = tot_size = 4+ 8+150 + 8+8 + 8 + data_size;
     203             :         /*pad is included in riff size*/
     204           2 :         if (ctx->has_qcp_pad) {
     205           1 :                 tot_size++;
     206             :         }
     207             :         size += 8;
     208             :         size -= data_size;
     209           2 :         dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
     210           2 :         if (!dst_pck) return;
     211             : 
     212           2 :         bs = gf_bs_new(output, size, GF_BITSTREAM_WRITE);
     213             : 
     214           2 :         gf_bs_write_data(bs, "RIFF", 4);
     215           2 :         gf_bs_write_u32_le(bs, tot_size);
     216           2 :         gf_bs_write_data(bs, "QLCM", 4);
     217           2 :         gf_bs_write_data(bs, "fmt ", 4);
     218           2 :         gf_bs_write_u32_le(bs, 150);/*fmt chunk size*/
     219           2 :         gf_bs_write_u8(bs, 1);
     220           2 :         gf_bs_write_u8(bs, 0);
     221           2 :         gf_bs_write_data(bs, ctx->GUID, 16);
     222           2 :         gf_bs_write_u16_le(bs, 1);
     223             :         memset(szName, 0, 80);
     224           2 :         strcpy(szName, (ctx->qcp_type==1) ? "QCELP-GPACExport" : ((ctx->qcp_type==2) ? "SMV-GPACExport" : "EVRC-GPACExport"));
     225           2 :         gf_bs_write_data(bs, szName, 80);
     226           2 :         gf_bs_write_u16_le(bs, avg_rate);
     227           2 :         gf_bs_write_u16_le(bs, sample_size);
     228           2 :         gf_bs_write_u16_le(bs, block_size);
     229           2 :         gf_bs_write_u16_le(bs, sample_rate);
     230           2 :         gf_bs_write_u16_le(bs, avg_rate);
     231           2 :         gf_bs_write_u32_le(bs, ctx->rt_cnt);
     232          18 :         for (i=0; i<8; i++) {
     233          16 :                 if (i<ctx->rt_cnt) {
     234             :                         /*frame size MINUS rate octet*/
     235          14 :                         gf_bs_write_u8(bs, ctx->rtable[i].pck_size - 1);
     236          14 :                         gf_bs_write_u8(bs, ctx->rtable[i].rate_idx);
     237             :                 } else {
     238           2 :                         gf_bs_write_u16(bs, 0);
     239             :                 }
     240             :         }
     241             :         memset(szName, 0, 80);
     242           2 :         gf_bs_write_data(bs, szName, 20);/*reserved*/
     243           2 :         gf_bs_write_data(bs, "vrat", 4);
     244           2 :         gf_bs_write_u32_le(bs, 8);/*vrat chunk size*/
     245           2 :         gf_bs_write_u32_le(bs, ctx->rt_cnt);
     246           2 :         gf_bs_write_u32_le(bs, frame_count);
     247           2 :         gf_bs_write_data(bs, "data", 4);
     248           2 :         gf_bs_write_u32_le(bs, data_size);/*data chunk size*/
     249             : 
     250           2 :         ctx->needs_rate_byte = needs_rate_octet ? 1 : 0;
     251             : 
     252           2 :         gf_bs_del(bs);
     253             : 
     254           2 :         if (!ctx->first) {
     255             :                 assert(ctx->needs_final_pach);
     256           1 :                 gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
     257           1 :                 gf_filter_pck_set_seek_flag(dst_pck, GF_TRUE);
     258           1 :                 gf_filter_pck_set_byte_offset(dst_pck, 0);
     259             :         } else {
     260           1 :                 gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
     261           1 :                 gf_filter_pck_set_byte_offset(dst_pck, GF_FILTER_NO_BO);
     262           1 :                 ctx->first = GF_FALSE;
     263             :         }
     264             : 
     265           2 :         gf_filter_pck_send(dst_pck);
     266             : }
     267             : 
     268         503 : GF_Err qcpmx_process(GF_Filter *filter)
     269             : {
     270         503 :         GF_QCPMxCtx *ctx = gf_filter_get_udta(filter);
     271             :         GF_FilterPacket *pck, *dst_pck;
     272             :         u8 *data, *output;
     273             :         u32 pck_size, size;
     274             : 
     275         503 :         pck = gf_filter_pid_get_packet(ctx->ipid);
     276         503 :         if (!pck) {
     277           2 :                 if (gf_filter_pid_is_eos(ctx->ipid)) {
     278           1 :                         if (ctx->needs_final_pach) {
     279           1 :                                 qcpmx_send_header(ctx, ctx->data_size, ctx->nb_frames);
     280           1 :                                 ctx->needs_final_pach = GF_FALSE;
     281             :                         }
     282           1 :                         if (ctx->has_qcp_pad) {
     283           1 :                                 dst_pck = gf_filter_pck_new_alloc(ctx->opid, 1, &output);
     284           1 :                                 if (dst_pck) {
     285           1 :                                         output[0] = 0;
     286           1 :                                         gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_TRUE);
     287           1 :                                         ctx->has_qcp_pad = GF_FALSE;
     288           1 :                                         gf_filter_pck_send(dst_pck);
     289             :                                 }
     290             :                         }
     291           1 :                         gf_filter_pid_set_eos(ctx->opid);
     292           1 :                         return GF_EOS;
     293             :                 }
     294             :                 return GF_OK;
     295             :         }
     296             : 
     297         501 :         if (ctx->first) {
     298           1 :                 qcpmx_send_header(ctx, ctx->data_size, ctx->nb_frames);
     299             :         }
     300             : 
     301         501 :         data = (char *) gf_filter_pck_get_data(pck, &pck_size);
     302             : 
     303         501 :         size = pck_size;
     304         501 :         ctx->data_size += pck_size;
     305         501 :         ctx->nb_frames ++;
     306             : 
     307             :         /*fix rate octet for QCP*/
     308         501 :         if (ctx->needs_rate_byte) {
     309             :                 u32 j;
     310             :                 u32 rate_found = 0;
     311           0 :                 size ++;
     312             : 
     313           0 :                 for (j=0; j<ctx->rt_cnt; j++) {
     314           0 :                         if (ctx->qcp_rates[2*j+1] == pck_size) {
     315           0 :                                 rate_found = ctx->qcp_rates[2*j];
     316           0 :                                 break;
     317             :                         }
     318             :                 }
     319           0 :                 if (!rate_found) {
     320           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[QCP] Frame size %d not in rate table, ignoring frame\n", pck_size));
     321           0 :                         gf_filter_pid_drop_packet(ctx->ipid);
     322           0 :                         return GF_NON_COMPLIANT_BITSTREAM;
     323             :                 }
     324             : 
     325           0 :                 dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
     326           0 :                 if (dst_pck) {
     327           0 :                         output[0] = rate_found;
     328           0 :                         memcpy(output+1, data, pck_size);
     329             :                 }
     330             :         } else {
     331             :                 //send the complete data
     332         501 :                 dst_pck = gf_filter_pck_new_ref(ctx->opid, 0, size, pck);
     333             :         }
     334         501 :         if (!dst_pck) return GF_OUT_OF_MEM;
     335             : 
     336         501 :         gf_filter_pck_merge_properties(pck, dst_pck);
     337         501 :         gf_filter_pck_set_byte_offset(dst_pck, GF_FILTER_NO_BO);
     338             : 
     339         501 :         gf_filter_pck_set_framing(dst_pck, ctx->first, GF_FALSE);
     340         501 :         ctx->first = GF_FALSE;
     341             : 
     342         501 :         gf_filter_pck_send(dst_pck);
     343             : 
     344         501 :         if (ctx->exporter) {
     345           0 :                 u32 timescale = gf_filter_pck_get_timescale(pck);
     346           0 :                 u64 ts = gf_filter_pck_get_cts(pck);
     347           0 :                 gf_set_progress("Exporting", ts*ctx->duration.den, ctx->duration.num*timescale);
     348             :         }
     349             : 
     350         501 :         gf_filter_pid_drop_packet(ctx->ipid);
     351             : 
     352         501 :         return GF_OK;
     353             : }
     354             : 
     355             : static const GF_FilterCapability QCPMxCaps[] =
     356             : {
     357             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     358             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_QCELP),
     359             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_EVRC),
     360             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_EVRC_PV),
     361             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_SMV),
     362             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     363             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     364             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, "qcp"),
     365             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, "audio/qcp"),
     366             : };
     367             : 
     368             : 
     369             : #define OFFS(_n)        #_n, offsetof(GF_QCPMxCtx, _n)
     370             : static const GF_FilterArgs QCPMxArgs[] =
     371             : {
     372             :         { OFFS(exporter), "compatibility with old exporter, displays export results", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     373             :         {0}
     374             : };
     375             : 
     376             : 
     377             : GF_FilterRegister QCPMxRegister = {
     378             :         .name = "writeqcp",
     379             :         GF_FS_SET_DESCRIPTION("QCP writer")
     380             :         GF_FS_SET_HELP("This filter converts a single stream to a QCP output file.")
     381             :         .private_size = sizeof(GF_QCPMxCtx),
     382             :         .args = QCPMxArgs,
     383             :         SETCAPS(QCPMxCaps),
     384             :         .configure_pid = qcpmx_configure_pid,
     385             :         .process = qcpmx_process
     386             : };
     387             : 
     388             : 
     389        2877 : const GF_FilterRegister *qcpmx_register(GF_FilterSession *session)
     390             : {
     391        2877 :         return &QCPMxRegister;
     392             : }

Generated by: LCOV version 1.13