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 video (YUV,RGB) 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 : GF_PropVec2i size;
34 : GF_PixelFormat spfmt;
35 : GF_Fraction fps;
36 : Bool copy;
37 :
38 : //only one input pid declared
39 : GF_FilterPid *ipid;
40 : //only one output pid declared
41 : GF_FilterPid *opid;
42 :
43 : Bool file_loaded, is_playing, initial_play_done;
44 : u64 cts;
45 : u32 frame_size, nb_bytes_in_frame;
46 : u64 filepos, total_frames;
47 : GF_FilterPacket *out_pck;
48 : u8 *out_data;
49 : Bool reverse_play, done;
50 : Bool is_v210;
51 : } GF_RawVidReframeCtx;
52 :
53 :
54 :
55 :
56 282 : GF_Err rawvidreframe_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
57 : {
58 : const GF_PropertyValue *p;
59 : u32 stride, stride_uv;
60 282 : GF_RawVidReframeCtx *ctx = gf_filter_get_udta(filter);
61 :
62 282 : if (is_remove) {
63 2 : ctx->ipid = NULL;
64 2 : if (ctx->opid) {
65 2 : gf_filter_pid_remove(ctx->opid);
66 2 : ctx->opid = NULL;
67 : }
68 : return GF_OK;
69 : }
70 280 : if (! gf_filter_pid_check_caps(pid))
71 : return GF_NOT_SUPPORTED;
72 :
73 280 : ctx->ipid = pid;
74 280 : ctx->is_v210 = GF_FALSE;
75 280 : if (!ctx->spfmt) {
76 280 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_EXT);
77 280 : if (p && p->value.string) {
78 280 : if (!strcmp(p->value.string, "v210")) {
79 0 : ctx->is_v210 = GF_TRUE;
80 : } else {
81 280 : ctx->spfmt = gf_pixel_fmt_parse(p->value.string);
82 : }
83 : }
84 : }
85 280 : if (!ctx->spfmt && !ctx->is_v210) {
86 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[RawVidReframe] Missing pixel format, cannot parse\n"));
87 : return GF_BAD_PARAM;
88 : }
89 280 : if (!ctx->size.x || !ctx->size.y) {
90 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[RawVidReframe] Missing video frame size, cannot parse\n"));
91 : return GF_BAD_PARAM;
92 : }
93 :
94 280 : stride = stride_uv = 0;
95 280 : if (ctx->is_v210) {
96 : #define V210_HORIZ_ALIGN_PIXEL 48
97 0 : if (ctx->size.x % V210_HORIZ_ALIGN_PIXEL)
98 0 : stride = ((ctx->size.x / V210_HORIZ_ALIGN_PIXEL) + 1) * V210_HORIZ_ALIGN_PIXEL;
99 : else
100 0 : stride = ctx->size.x;
101 0 : stride = stride * 16 / 6;
102 0 : ctx->frame_size = stride * ctx->size.y;
103 : } else {
104 280 : if (! gf_pixel_get_size_info(ctx->spfmt, ctx->size.x, ctx->size.y, &ctx->frame_size, &stride, &stride_uv, NULL, NULL)) {
105 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[RawVidReframe] Failed to query pixel format size info\n"));
106 : return GF_BAD_PARAM;
107 : }
108 : }
109 :
110 280 : if (!ctx->opid)
111 280 : ctx->opid = gf_filter_pid_new(filter);
112 :
113 280 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
114 280 : gf_filter_pid_set_framing_mode(ctx->ipid, GF_FALSE);
115 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_VISUAL));
116 280 : if (ctx->is_v210) {
117 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_V210));
118 : } else {
119 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW));
120 : }
121 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->size.x));
122 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->size.y));
123 280 : if (ctx->spfmt)
124 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PIXFMT, &PROP_UINT(ctx->spfmt));
125 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FPS, &PROP_FRAC(ctx->fps));
126 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->fps.num));
127 :
128 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(stride));
129 280 : if (stride_uv)
130 103 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, &PROP_UINT(stride_uv));
131 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_REWIND));
132 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, &PROP_BOOL(GF_TRUE));
133 :
134 280 : if (!gf_sys_is_test_mode() ) {
135 0 : u32 osize = 0;
136 0 : gf_pixel_get_size_info(ctx->spfmt, ctx->size.x, ctx->size.y, &osize, &stride, &stride_uv, NULL, NULL);
137 0 : if (osize) {
138 0 : u32 rate = osize * 8 * ctx->fps.num / ctx->fps.den;
139 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(rate));
140 : }
141 : }
142 :
143 :
144 280 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
145 280 : if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
146 :
147 280 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_DOWN_SIZE);
148 280 : if (p && p->value.longuint) {
149 : u64 nb_frames = p->value.longuint;
150 280 : nb_frames /= ctx->frame_size;
151 280 : ctx->total_frames = nb_frames;
152 280 : nb_frames *= ctx->fps.den;
153 280 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(nb_frames, ctx->fps.num));
154 : }
155 :
156 280 : if (!ctx->copy) {
157 261 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAY_BUFFER, &PROP_UINT(0));
158 261 : if (!gf_sys_is_test_mode())
159 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_RE_BUFFER, &PROP_UINT(0));
160 : }
161 : return GF_OK;
162 : }
163 :
164 3930 : static Bool rawvidreframe_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
165 : {
166 : u32 nb_frames;
167 : GF_FilterEvent fevt;
168 3930 : GF_RawVidReframeCtx *ctx = gf_filter_get_udta(filter);
169 :
170 3930 : switch (evt->base.type) {
171 280 : case GF_FEVT_PLAY:
172 280 : if (!ctx->is_playing) {
173 280 : ctx->is_playing = GF_TRUE;
174 280 : ctx->cts = 0;
175 : }
176 280 : ctx->done = GF_FALSE;
177 280 : if (evt->play.start_range>=0) {
178 280 : nb_frames = (u32) (evt->play.start_range * ctx->fps.num);
179 : } else {
180 0 : nb_frames = (u32) (ctx->cts / ctx->fps.den);
181 : }
182 280 : if (nb_frames>=ctx->total_frames)
183 1 : nb_frames = (u32) ctx->total_frames-1;
184 :
185 280 : ctx->cts = nb_frames * ctx->fps.den;
186 280 : ctx->filepos = nb_frames * ctx->frame_size;
187 280 : ctx->reverse_play = (evt->play.speed<0) ? GF_TRUE : GF_FALSE;
188 :
189 : //post a seek even for the beginning, to try to load frame by frame
190 280 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
191 280 : fevt.seek.start_offset = ctx->filepos;
192 280 : fevt.seek.hint_block_size = ctx->frame_size;
193 280 : gf_filter_pid_send_event(ctx->ipid, &fevt);
194 :
195 : //cancel event
196 280 : return GF_TRUE;
197 :
198 0 : case GF_FEVT_STOP:
199 : //don't cancel event
200 0 : ctx->is_playing = GF_FALSE;
201 0 : return GF_FALSE;
202 :
203 : case GF_FEVT_SET_SPEED:
204 : //cancel event
205 : return GF_TRUE;
206 : default:
207 : break;
208 : }
209 : //by default don't cancel event - to rework once we have downloading in place
210 3539 : return GF_FALSE;
211 : }
212 :
213 7614 : GF_Err rawvidreframe_process(GF_Filter *filter)
214 : {
215 7614 : GF_RawVidReframeCtx *ctx = gf_filter_get_udta(filter);
216 : GF_FilterPacket *pck;
217 : u64 byte_offset;
218 : char *data;
219 : u32 pck_size, offset_in_pck;
220 :
221 7614 : if (ctx->done) return GF_EOS;
222 :
223 7614 : if (!ctx->is_playing && ctx->opid) return GF_OK;
224 :
225 7018 : pck = gf_filter_pid_get_packet(ctx->ipid);
226 7018 : if (!pck) {
227 3498 : if (gf_filter_pid_is_eos(ctx->ipid) && !ctx->reverse_play) {
228 428 : if (ctx->out_pck) {
229 0 : gf_filter_pck_send(ctx->out_pck);
230 0 : ctx->out_pck = NULL;
231 : }
232 428 : if (ctx->opid)
233 428 : gf_filter_pid_set_eos(ctx->opid);
234 : return GF_EOS;
235 : }
236 : return GF_OK;
237 : }
238 :
239 3520 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
240 3520 : byte_offset = gf_filter_pck_get_byte_offset(pck);
241 : offset_in_pck = 0;
242 :
243 10533 : while (pck_size) {
244 : Bool use_ref = GF_FALSE;
245 3520 : if (!ctx->out_pck) {
246 : assert(! ctx->nb_bytes_in_frame);
247 3520 : if (!ctx->copy && (pck_size >= ctx->frame_size)) {
248 3007 : ctx->out_pck = gf_filter_pck_new_ref(ctx->opid, offset_in_pck, ctx->frame_size, pck);
249 3007 : use_ref = GF_TRUE;
250 : } else {
251 513 : ctx->out_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->frame_size, &ctx->out_data);
252 : }
253 3520 : if (!ctx->out_pck) return GF_OUT_OF_MEM;
254 :
255 3520 : gf_filter_pck_set_cts(ctx->out_pck, ctx->cts);
256 3520 : gf_filter_pck_set_sap(ctx->out_pck, GF_FILTER_SAP_1);
257 3520 : gf_filter_pck_set_duration(ctx->out_pck, ctx->fps.den);
258 3520 : gf_filter_pck_set_byte_offset(ctx->out_pck, byte_offset);
259 : }
260 :
261 3520 : if (pck_size + ctx->nb_bytes_in_frame < ctx->frame_size) {
262 0 : memcpy(ctx->out_data + ctx->nb_bytes_in_frame, data, pck_size);
263 0 : ctx->nb_bytes_in_frame += pck_size;
264 0 : pck_size = 0;
265 : } else {
266 3520 : u32 remain = ctx->frame_size - ctx->nb_bytes_in_frame;
267 3520 : if (!use_ref) {
268 513 : memcpy(ctx->out_data + ctx->nb_bytes_in_frame, data, remain);
269 : }
270 3520 : gf_filter_pck_send(ctx->out_pck);
271 :
272 3520 : pck_size -= remain;
273 3520 : data += remain;
274 3520 : byte_offset += remain;
275 3520 : offset_in_pck += remain;
276 :
277 3520 : ctx->out_pck = NULL;
278 3520 : ctx->nb_bytes_in_frame = 0;
279 :
280 : //reverse playback, the remaining data is for the next frame, we want the previous one.
281 : //Trash packet and seek to previous frame
282 3520 : if (ctx->reverse_play) {
283 : GF_FilterEvent fevt;
284 27 : if (!ctx->cts) {
285 1 : if (ctx->opid)
286 1 : gf_filter_pid_set_eos(ctx->opid);
287 1 : ctx->done = GF_TRUE;
288 1 : return GF_EOS;
289 : }
290 26 : ctx->cts -= ctx->fps.den;
291 26 : ctx->filepos -= ctx->frame_size;
292 26 : gf_filter_pid_drop_packet(ctx->ipid);
293 : //post a seek, this will trash remaining packets in buffers
294 26 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
295 26 : fevt.seek.start_offset = ctx->filepos;
296 26 : gf_filter_pid_send_event(ctx->ipid, &fevt);
297 26 : return GF_OK;
298 : }
299 3493 : ctx->cts += ctx->fps.den;
300 : }
301 : }
302 3493 : gf_filter_pid_drop_packet(ctx->ipid);
303 3493 : return GF_OK;
304 : }
305 :
306 :
307 : static GF_FilterCapability RawVidReframeCaps[] =
308 : {
309 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
310 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "yuv"),
311 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "video/x-yuv"),
312 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
313 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
314 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
315 : {0},
316 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
317 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "v210"),
318 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "video/x-raw-v210"),
319 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
320 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_V210),
321 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
322 : };
323 :
324 : #define OFFS(_n) #_n, offsetof(GF_RawVidReframeCtx, _n)
325 : static GF_FilterArgs RawVidReframeArgs[] =
326 : {
327 : { OFFS(size), "source video resolution", GF_PROP_VEC2I, "0x0", NULL, 0},
328 : { OFFS(spfmt), "source pixel format. When not set, derived from file extension", GF_PROP_PIXFMT, "none", NULL, 0},
329 : { OFFS(fps), "number of frames per second", GF_PROP_FRACTION, "25/1", NULL, 0},
330 : { OFFS(copy), "copy source bytes into output frame. If not set, source bytes are referenced only", GF_PROP_BOOL, "false", NULL, 0},
331 : {0}
332 : };
333 :
334 :
335 : GF_FilterRegister RawVidReframeRegister = {
336 : .name = "rfrawvid",
337 : GF_FS_SET_DESCRIPTION("RAW video reframer")
338 : GF_FS_SET_HELP("This filter parses raw YUV and RGB files/data and outputs corresponding raw video PID and frames.")
339 : .private_size = sizeof(GF_RawVidReframeCtx),
340 : .args = RawVidReframeArgs,
341 : SETCAPS(RawVidReframeCaps),
342 : .configure_pid = rawvidreframe_configure_pid,
343 : .process = rawvidreframe_process,
344 : .process_event = rawvidreframe_process_event
345 : };
346 :
347 :
348 2877 : const GF_FilterRegister *rawvidreframe_register(GF_FilterSession *session)
349 : {
350 2877 : RawVidReframeArgs[1].min_max_enum = gf_pixel_fmt_all_names();
351 2877 : RawVidReframeCaps[1].val.value.string = (char *) gf_pixel_fmt_all_shortnames();
352 2877 : return &RawVidReframeRegister;
353 : }
354 :
|