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 : }
|