LCOV - code coverage report
Current view: top level - filters - reframe_rawpcm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 127 146 87.0 %
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 2018-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / RAW PCM reframer 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/avparse.h>
      27             : #include <gpac/constants.h>
      28             : #include <gpac/filters.h>
      29             : 
      30             : typedef struct
      31             : {
      32             :         //opts
      33             :         u32 framelen, safmt, sr, ch;
      34             : 
      35             :         //only one input pid declared
      36             :         GF_FilterPid *ipid;
      37             :         //only one output pid declared
      38             :         GF_FilterPid *opid;
      39             : 
      40             :         Bool file_loaded, is_playing, initial_play_done;
      41             :         u64 cts;
      42             :         u32 frame_size, nb_bytes_in_frame, Bps;
      43             :         u64 filepos, total_frames;
      44             :         GF_FilterPacket *out_pck;
      45             :         u8 *out_data;
      46             :         Bool reverse_play, done;
      47             : } GF_PCMReframeCtx;
      48             : 
      49             : 
      50             : 
      51             : 
      52          34 : GF_Err pcmreframe_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      53             : {
      54             :         const GF_PropertyValue *p;
      55          34 :         GF_PCMReframeCtx *ctx = gf_filter_get_udta(filter);
      56             : 
      57          34 :         if (is_remove) {
      58           0 :                 ctx->ipid = NULL;
      59           0 :                 if (ctx->opid) {
      60           0 :                         gf_filter_pid_remove(ctx->opid);
      61           0 :                         ctx->opid = NULL;
      62             :                 }
      63             :                 return GF_OK;
      64             :         }
      65          34 :         if (! gf_filter_pid_check_caps(pid))
      66             :                 return GF_NOT_SUPPORTED;
      67             : 
      68          34 :         ctx->ipid = pid;
      69          34 :         if (!ctx->safmt) {
      70          34 :                 p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_EXT);
      71          34 :                 if (p && p->value.string) ctx->safmt = gf_audio_fmt_parse(p->value.string);
      72             :         }
      73          34 :         if (!ctx->safmt) {
      74           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[PCMReframe] Missing audio format, cannot parse\n"));
      75             :                 return GF_BAD_PARAM;
      76             :         }
      77          34 :         if (!ctx->sr) {
      78           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[PCMReframe] Missing audio sample rate, cannot parse\n"));
      79             :                 return GF_BAD_PARAM;
      80             :         }
      81          34 :         if (!ctx->ch) {
      82           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[PCMReframe] Missing audio ch, cannot parse\n"));
      83             :                 return GF_BAD_PARAM;
      84             :         }
      85          34 :         if (!ctx->framelen) {
      86           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[PCMReframe] Missing audio framelen, using 1024\n"));
      87           0 :                 ctx->framelen = 1024;
      88             :         }
      89             : 
      90          34 :         ctx->Bps = gf_audio_fmt_bit_depth(ctx->safmt) / 8;
      91          34 :         ctx->frame_size = ctx->framelen * ctx->Bps * ctx->ch;
      92             : 
      93          34 :         if (!ctx->opid)
      94          34 :                 ctx->opid = gf_filter_pid_new(filter);
      95             : 
      96          34 :         gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
      97          34 :         gf_filter_pid_set_framing_mode(ctx->ipid, GF_FALSE);
      98          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_AUDIO));
      99          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW));
     100          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(ctx->sr));
     101          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(ctx->ch));
     102          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, &PROP_UINT(ctx->framelen));
     103          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(ctx->safmt));
     104          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->sr));
     105          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_REWIND));
     106          34 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, &PROP_BOOL(GF_TRUE));
     107             : 
     108          34 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
     109          34 :         if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
     110             : 
     111          34 :         if (!gf_sys_is_test_mode() ) {
     112           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->sr * ctx->Bps * ctx->ch));
     113             :         }
     114             : 
     115          34 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_DOWN_SIZE);
     116          34 :         if (p && p->value.longuint) {
     117             :                 u64 nb_frames = p->value.longuint;
     118          34 :                 nb_frames /= ctx->Bps * ctx->ch;
     119             :                 ctx->total_frames = p->value.longuint;
     120          34 :                 ctx->total_frames /= ctx->frame_size;
     121             : 
     122          34 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(nb_frames, ctx->sr));
     123             :         }
     124             : 
     125             :         return GF_OK;
     126             : }
     127             : 
     128        2336 : static Bool pcmreframe_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     129             : {
     130             :         u32 nb_frames;
     131             :         GF_FilterEvent fevt;
     132        2336 :         GF_PCMReframeCtx *ctx = gf_filter_get_udta(filter);
     133             : 
     134        2336 :         switch (evt->base.type) {
     135          34 :         case GF_FEVT_PLAY:
     136          34 :                 if (!ctx->is_playing) {
     137          34 :                         ctx->is_playing = GF_TRUE;
     138          34 :                         ctx->cts = 0;
     139             :                 }
     140          34 :                 ctx->done = GF_FALSE;
     141             : 
     142          34 :                 if (!ctx->total_frames)
     143             :                         return GF_TRUE;
     144             : 
     145          34 :                 if (evt->play.start_range>=0) {
     146          34 :                         ctx->cts = (u64) (evt->play.start_range * ctx->sr);
     147             :                 } else {
     148           0 :                         ctx->cts = (ctx->total_frames-1) * ctx->framelen;
     149             :                 }
     150          34 :                 nb_frames = (u32) (ctx->cts / ctx->framelen);
     151          34 :                 if (nb_frames==ctx->total_frames) {
     152           2 :                         if (evt->play.speed>=0) {
     153           0 :                                 ctx->done = GF_TRUE;
     154           0 :                                 return GF_TRUE;
     155             :                         }
     156           2 :                         nb_frames--;
     157           2 :                         ctx->cts = nb_frames * ctx->framelen;
     158             :                 }
     159             : 
     160          34 :                 ctx->filepos = nb_frames * ctx->frame_size;
     161          34 :                 ctx->reverse_play =  (evt->play.speed<0) ? GF_TRUE : GF_FALSE;
     162             : 
     163          34 :                 if (!ctx->initial_play_done) {
     164          34 :                         ctx->initial_play_done = GF_TRUE;
     165             :                         //seek will not change the current source state, don't send a seek
     166          34 :                         if (!ctx->filepos)
     167             :                                 return GF_TRUE;
     168             :                 }
     169             :                 //post a seek
     170           2 :                 GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
     171           2 :                 fevt.seek.start_offset = ctx->filepos;
     172           2 :                 gf_filter_pid_send_event(ctx->ipid, &fevt);
     173             : 
     174             :                 //cancel event
     175           2 :                 return GF_TRUE;
     176             : 
     177           0 :         case GF_FEVT_STOP:
     178             :                 //don't cancel event
     179           0 :                 ctx->is_playing = GF_FALSE;
     180           0 :                 return GF_FALSE;
     181             : 
     182             :         case GF_FEVT_SET_SPEED:
     183             :                 //cancel event
     184             :                 return GF_TRUE;
     185             :         default:
     186             :                 break;
     187             :         }
     188             :         //by default don't cancel event - to rework once we have downloading in place
     189        2302 :         return GF_FALSE;
     190             : }
     191             : 
     192        2326 : void pcmreframe_flush_packet(GF_PCMReframeCtx *ctx)
     193             : {
     194        2326 :         if (ctx->reverse_play) {
     195          36 :                 u32 i, nb_bytes_in_sample, nb_samples = ctx->nb_bytes_in_frame / ctx->Bps / ctx->ch;
     196          36 :                 nb_bytes_in_sample = ctx->Bps * ctx->ch;
     197       18468 :                 for (i=0; i<nb_samples/2; i++) {
     198             :                         char store[100];
     199       18432 :                         memcpy(store, ctx->out_data + i*nb_bytes_in_sample, nb_bytes_in_sample);
     200       18432 :                         memcpy(ctx->out_data + i*nb_bytes_in_sample, ctx->out_data + (nb_samples - i - 1)*nb_bytes_in_sample, nb_bytes_in_sample);
     201       18432 :                         memcpy(ctx->out_data + (nb_samples-i-1)*nb_bytes_in_sample, store, nb_bytes_in_sample);
     202             :                 }
     203             :         }
     204        2326 :         gf_filter_pck_send(ctx->out_pck);
     205        2326 :         ctx->out_pck = NULL;
     206        2326 : }
     207        2430 : GF_Err pcmreframe_process(GF_Filter *filter)
     208             : {
     209        2430 :         GF_PCMReframeCtx *ctx = gf_filter_get_udta(filter);
     210             :         GF_FilterPacket *pck;
     211             :         u64 byte_offset;
     212             :         u8 *data;
     213             :         u32 pck_size;
     214             : 
     215        2430 :         if (ctx->done) return GF_EOS;
     216             : 
     217        2428 :         if (!ctx->is_playing && ctx->opid) return GF_OK;
     218             : 
     219        2362 :         pck = gf_filter_pid_get_packet(ctx->ipid);
     220        2362 :         if (!pck) {
     221          60 :                 if (gf_filter_pid_is_eos(ctx->ipid) && !ctx->reverse_play) {
     222          35 :                         if (ctx->out_pck) {
     223           0 :                                 gf_filter_pck_truncate(ctx->out_pck, ctx->nb_bytes_in_frame);
     224           0 :                                 gf_filter_pck_set_duration(ctx->out_pck, ctx->nb_bytes_in_frame/ctx->Bps/ctx->ch);
     225           0 :                                 pcmreframe_flush_packet(ctx);
     226             :                         }
     227          35 :                         if (ctx->opid)
     228          35 :                                 gf_filter_pid_set_eos(ctx->opid);
     229             :                         return GF_EOS;
     230             :                 }
     231             :                 return GF_OK;
     232             :         }
     233             : 
     234        2302 :         data = (char *) gf_filter_pck_get_data(pck, &pck_size);
     235        2302 :         byte_offset = gf_filter_pck_get_byte_offset(pck);
     236             : 
     237        9126 :         while (pck_size) {
     238        4558 :                 if (!ctx->out_pck) {
     239        2326 :                         ctx->out_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->frame_size, &ctx->out_data);
     240        2326 :                         if (!ctx->out_pck) return GF_OUT_OF_MEM;
     241             : 
     242        2326 :                         gf_filter_pck_set_cts(ctx->out_pck, ctx->cts);
     243        2326 :                         gf_filter_pck_set_sap(ctx->out_pck, GF_FILTER_SAP_1);
     244        2326 :                         gf_filter_pck_set_duration(ctx->out_pck, ctx->framelen);
     245        2326 :                         gf_filter_pck_set_byte_offset(ctx->out_pck, byte_offset);
     246             :                 }
     247             : 
     248        4558 :                 if (pck_size + ctx->nb_bytes_in_frame < ctx->frame_size) {
     249        2232 :                         memcpy(ctx->out_data + ctx->nb_bytes_in_frame, data, pck_size);
     250        2232 :                         ctx->nb_bytes_in_frame += pck_size;
     251        2232 :                         pck_size = 0;
     252             :                 } else {
     253        2326 :                         u32 remain = ctx->frame_size - ctx->nb_bytes_in_frame;
     254        2326 :                         memcpy(ctx->out_data + ctx->nb_bytes_in_frame, data, remain);
     255        2326 :                         ctx->nb_bytes_in_frame = ctx->frame_size;
     256        2326 :                         pcmreframe_flush_packet(ctx);
     257             : 
     258        2326 :                         pck_size -= remain;
     259        2326 :                         data += remain;
     260        2326 :                         byte_offset += remain;
     261        2326 :                         ctx->out_pck = NULL;
     262        2326 :                         ctx->nb_bytes_in_frame = 0;
     263             : 
     264             :                         //reverse playback, the remaining data is for the next frame, we want the previous one.
     265             :                         //Trash packet and seek to previous frame
     266        2326 :                         if (ctx->reverse_play) {
     267             :                                 GF_FilterEvent fevt;
     268          36 :                                 if (!ctx->cts) {
     269           2 :                                         if (ctx->opid)
     270           2 :                                                 gf_filter_pid_set_eos(ctx->opid);
     271           2 :                                         GF_FEVT_INIT(fevt, GF_FEVT_STOP, ctx->ipid);
     272           2 :                                         gf_filter_pid_send_event(ctx->ipid, &fevt);
     273           2 :                                         ctx->done = GF_TRUE;
     274           2 :                                         return GF_EOS;
     275             :                                 }
     276          34 :                                 ctx->cts -= ctx->framelen;
     277          34 :                                 ctx->filepos -= ctx->frame_size;
     278          34 :                                 gf_filter_pid_drop_packet(ctx->ipid);
     279             :                                 //post a seek, this will trash remaining packets in buffers
     280          34 :                                 GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
     281          34 :                                 fevt.seek.start_offset = ctx->filepos;
     282          34 :                                 gf_filter_pid_send_event(ctx->ipid, &fevt);
     283          34 :                                 return GF_OK;
     284             :                         }
     285        2290 :                         ctx->cts += ctx->framelen;
     286             :                 }
     287             :         }
     288        2266 :         gf_filter_pid_drop_packet(ctx->ipid);
     289        2266 :         return GF_OK;
     290             : }
     291             : 
     292             : 
     293             : static GF_FilterCapability PCMReframeCaps[] =
     294             : {
     295             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     296             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "pcm"),
     297             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/x-pcm"),
     298             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     299             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
     300             :         CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     301             : };
     302             : 
     303             : #define OFFS(_n)        #_n, offsetof(GF_PCMReframeCtx, _n)
     304             : static GF_FilterArgs PCMReframeArgs[] =
     305             : {
     306             :         { OFFS(sr), "sample rate", GF_PROP_UINT, "44100", NULL, 0},
     307             :         { OFFS(safmt), "audio format", GF_PROP_PCMFMT, "none", NULL, 0},
     308             :         { OFFS(ch), "number of channels", GF_PROP_UINT, "2", NULL, 0},
     309             :         { OFFS(framelen), "number of samples to put in one audio frame. For planar formats, indicate plane size in samples", GF_PROP_UINT, "1024", NULL, GF_FS_ARG_HINT_ADVANCED},
     310             :         {0}
     311             : };
     312             : 
     313             : 
     314             : GF_FilterRegister PCMReframeRegister = {
     315             :         .name = "rfpcm",
     316             :         GF_FS_SET_DESCRIPTION("PCM reframer")
     317             :         GF_FS_SET_HELP("This filter parses raw PCM file/data and outputs corresponding raw audio PID and frames.")
     318             :         .private_size = sizeof(GF_PCMReframeCtx),
     319             :         .args = PCMReframeArgs,
     320             :         SETCAPS(PCMReframeCaps),
     321             :         .configure_pid = pcmreframe_configure_pid,
     322             :         .process = pcmreframe_process,
     323             :         .process_event = pcmreframe_process_event
     324             : };
     325             : 
     326             : 
     327        2877 : const GF_FilterRegister *pcmreframe_register(GF_FilterSession *session)
     328             : {
     329        2877 :         PCMReframeArgs[1].min_max_enum = gf_audio_fmt_all_names();
     330        2877 :         PCMReframeCaps[1].val.value.string = (char *) gf_audio_fmt_all_shortnames();
     331        2877 :         return &PCMReframeRegister;
     332             : }
     333             : 

Generated by: LCOV version 1.13