LCOV - code coverage report
Current view: top level - filters - rewind.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 83 96 86.5 %
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 2018
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / media rewinder 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             : #include <gpac/internal/compositor_dev.h>
      30             : 
      31             : typedef struct
      32             : {
      33             :         //opts
      34             :         u32 rbuffer;
      35             : 
      36             :         //internal
      37             :         GF_FilterPid *ipid, *opid;
      38             :         u32 type;
      39             : 
      40             :         Bool passthrough;
      41             : 
      42             :         GF_List *frames;
      43             : 
      44             :         u32 nb_ch;
      45             :         u32 bytes_per_sample;
      46             :         Bool is_planar;
      47             : 
      48             :         Bool wait_for_next_sap;
      49             : 
      50             : } GF_RewindCtx;
      51             : 
      52             : 
      53           2 : static GF_Err rewind_initialize(GF_Filter *filter)
      54             : {
      55           2 :         GF_RewindCtx *ctx = gf_filter_get_udta(filter);
      56           2 :         ctx->frames = gf_list_new();
      57           2 :         return GF_OK;
      58             : }
      59             : 
      60           2 : static void rewind_finalize(GF_Filter *filter)
      61             : {
      62           2 :         GF_RewindCtx *ctx = gf_filter_get_udta(filter);
      63           2 :         gf_list_del(ctx->frames);
      64           2 : }
      65             : 
      66             : 
      67           3 : static GF_Err rewind_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      68             : {
      69             :         const GF_PropertyValue *p;
      70             :         u32 afmt;
      71           3 :         GF_RewindCtx *ctx = gf_filter_get_udta(filter);
      72           3 :         if (is_remove) {
      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           3 :         if (! gf_filter_pid_check_caps(pid))
      80             :                 return GF_NOT_SUPPORTED;
      81             : 
      82             : 
      83           3 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
      84           3 :         if (!p) return GF_NOT_SUPPORTED;
      85           3 :         ctx->type = p->value.uint;
      86             : 
      87           3 :         if (ctx->type==GF_STREAM_AUDIO) {
      88           1 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
      89           1 :                 if (p) ctx->nb_ch = p->value.uint;
      90           1 :                 if (!ctx->nb_ch) ctx->nb_ch = 1;
      91             : 
      92           1 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
      93           1 :                 if (!p) return GF_NOT_SUPPORTED;
      94             : 
      95           1 :                 afmt = p->value.uint;
      96           1 :                 ctx->bytes_per_sample = gf_audio_fmt_bit_depth(afmt) * ctx->nb_ch / 8;
      97           1 :                 ctx->is_planar = gf_audio_fmt_is_planar(afmt);
      98             :         }
      99             : 
     100           3 :         if (!ctx->opid) {
     101           2 :                 ctx->opid = gf_filter_pid_new(filter);
     102           2 :                 gf_filter_pid_set_max_buffer(ctx->opid, gf_filter_pid_get_max_buffer(pid) );
     103             :         }
     104           3 :         if (!ctx->ipid) {
     105           2 :                 ctx->ipid = pid;
     106             :         }
     107           3 :         gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
     108           3 :         return GF_OK;
     109             : }
     110             : 
     111          28 : static GF_Err rewind_process_video(GF_RewindCtx *ctx, GF_FilterPacket *pck)
     112             : {
     113             :         Bool do_flush = GF_FALSE;
     114          28 :         if (pck) {
     115             :                 //keep a ref on this packet
     116          26 :                 gf_filter_pck_ref(&pck);
     117             :                 //and drop input
     118          26 :                 gf_filter_pid_drop_packet(ctx->ipid);
     119          26 :                 if (gf_filter_pck_get_sap(pck)) {
     120             :                         do_flush = GF_TRUE;
     121           1 :                         ctx->wait_for_next_sap = GF_FALSE;
     122             :                 }
     123          25 :                 else if (gf_list_count(ctx->frames)>ctx->rbuffer) {
     124             :                         do_flush = GF_TRUE;
     125           0 :                         ctx->wait_for_next_sap = GF_TRUE;
     126           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Rewind] Too many frames in GOP, %d vs %d max allowed, flushing until next SAP\n", gf_list_count(ctx->frames), ctx->rbuffer));
     127             :                 }
     128             :         } else {
     129             :                 do_flush = GF_TRUE;
     130             :         }
     131             :         //frame was a SAP, flush all previous frames in reverse order
     132             :         if (do_flush) {
     133          26 :                 while (1) {
     134          29 :                         GF_FilterPacket *frame = gf_list_pop_back(ctx->frames);
     135          29 :                         if (!frame) break;
     136          26 :                         gf_filter_pck_forward(frame, ctx->opid);
     137          26 :                         gf_filter_pck_unref(frame);
     138             :                 }
     139             :         }
     140          28 :         if (pck) {
     141             :                 //rewind buffer exceeded
     142          26 :                 if (ctx->wait_for_next_sap) {
     143           0 :                         gf_filter_pck_forward(pck, ctx->opid);
     144           0 :                         gf_filter_pck_unref(pck);
     145             :                 } else {
     146          26 :                         gf_list_add(ctx->frames, pck);
     147             :                 }
     148             :         }
     149          28 :         return GF_OK;
     150             : }
     151             : 
     152          48 : static GF_Err rewind_process(GF_Filter *filter)
     153             : {
     154             :         u8 *output;
     155             :         const u8 *data;
     156             :         u32 size;
     157             :         GF_FilterPacket *pck, *dstpck;
     158          48 :         GF_RewindCtx *ctx = gf_filter_get_udta(filter);
     159             : 
     160          48 :         if (!ctx->ipid) return GF_OK;
     161             : 
     162          48 :         pck = gf_filter_pid_get_packet(ctx->ipid);
     163             : 
     164          48 :         if (!pck) {
     165           4 :                 if (gf_filter_pid_is_eos(ctx->ipid)) {
     166           3 :                         if (!ctx->passthrough && (ctx->type == GF_STREAM_VISUAL)) {
     167           2 :                                 return rewind_process_video(ctx, NULL);
     168             :                         }
     169           1 :                         gf_filter_pid_set_eos(ctx->opid);
     170           1 :                         return GF_EOS;
     171             :                 }
     172             :                 return GF_OK;
     173             :         }
     174          44 :         if (ctx->passthrough) {
     175           0 :                 gf_filter_pck_forward(pck, ctx->opid);
     176           0 :                 gf_filter_pid_drop_packet(ctx->ipid);
     177           0 :                 return GF_OK;
     178             :         }
     179             : 
     180          44 :         if (ctx->type == GF_STREAM_VISUAL) {
     181          26 :                 return rewind_process_video(ctx, pck);
     182             :         }
     183          18 :         data = gf_filter_pck_get_data(pck, &size);
     184             : 
     185          18 :         dstpck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
     186          18 :         if (!dstpck) return GF_OK;
     187          18 :         gf_filter_pck_merge_properties(pck, dstpck);
     188             : 
     189          18 :         if (ctx->is_planar) {
     190             :                 u32 i, j, nb_samples, planesize, bytes_per_samp;
     191          18 :                 nb_samples = size / ctx->bytes_per_sample;
     192          18 :                 planesize = nb_samples * ctx->bytes_per_sample / ctx->nb_ch;
     193             : 
     194          18 :                 bytes_per_samp = ctx->bytes_per_sample / ctx->nb_ch;
     195          54 :                 for (j=0; j<ctx->nb_ch; j++) {
     196          36 :                         char *dst = output + j * planesize;
     197             :                         char *src = (char *) data + j * planesize;
     198             : 
     199       36900 :                         for (i=0; i<nb_samples; i++) {
     200       36864 :                                 memcpy(dst + i*bytes_per_samp, src + (nb_samples - i - 1)*bytes_per_samp, bytes_per_samp);
     201             :                         }
     202             :                 }
     203             :         } else {
     204             :                 u32 i, nb_samples;
     205           0 :                 nb_samples = size / ctx->bytes_per_sample;
     206             : 
     207           0 :                 for (i=0; i<nb_samples; i++) {
     208           0 :                         memcpy(output + i*ctx->bytes_per_sample, data + (nb_samples - i - 1)*ctx->bytes_per_sample, ctx->bytes_per_sample);
     209             :                 }
     210             :         }
     211          18 :         gf_filter_pck_send(dstpck);
     212          18 :         gf_filter_pid_drop_packet(ctx->ipid);
     213          18 :         return GF_OK;
     214             : }
     215             : 
     216           4 : static Bool rewind_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     217             : {
     218           4 :         GF_RewindCtx *ctx = gf_filter_get_udta(filter);
     219           4 :         if (evt->base.type==GF_FEVT_PLAY) {
     220           2 :                 if (evt->play.speed>0) ctx->passthrough = GF_TRUE;
     221           2 :                 else ctx->passthrough = GF_FALSE;
     222             :         }
     223           4 :         return GF_FALSE;
     224             : }
     225             : 
     226             : static const GF_FilterCapability RewinderCaps[] =
     227             : {
     228             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     229             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
     230             :         {0},
     231             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     232             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
     233             : };
     234             : 
     235             : #define OFFS(_n)        #_n, offsetof(GF_RewindCtx, _n)
     236             : static const GF_FilterArgs RewinderArgs[] =
     237             : {
     238             :         { OFFS(rbuffer), "size of video rewind buffer in frames. If more frames than this, flush is performed", GF_PROP_UINT, "100", NULL, GF_FS_ARG_HINT_ADVANCED},
     239             :         {0}
     240             : };
     241             : 
     242             : GF_FilterRegister RewinderRegister = {
     243             :         .name = "rewind",
     244             :         GF_FS_SET_DESCRIPTION("Audio/Video rewinder")
     245             :         GF_FS_SET_HELP("This filter reverses audio and video frames in negative playback speed.\nThe filter is in passthrough if speed is positive. Otherwise, it reverts decoded GOPs for video, or revert samples in decoded frame for audio (not really nice for most codecs).")
     246             :         .private_size = sizeof(GF_RewindCtx),
     247             :         //rewind shall be explicitly loaded
     248             :         .flags = GF_FS_REG_EXPLICIT_ONLY,
     249             :         .initialize = rewind_initialize,
     250             :         .finalize = rewind_finalize,
     251             :         .args = RewinderArgs,
     252             :         SETCAPS(RewinderCaps),
     253             :         .configure_pid = rewind_configure_pid,
     254             :         .process = rewind_process,
     255             :         .process_event = rewind_process_event
     256             : };
     257             : 
     258             : 
     259        2877 : const GF_FilterRegister *rewind_register(GF_FilterSession *session)
     260             : {
     261        2877 :         return &RewinderRegister;
     262             : }

Generated by: LCOV version 1.13