Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2017-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / ffmpeg demux 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/setup.h>
27 :
28 : #ifdef GPAC_HAS_FFMPEG
29 :
30 : #include "ff_common.h"
31 :
32 : //for NTP clock
33 : #include <gpac/network.h>
34 :
35 : enum
36 : {
37 : COPY_NO,
38 : COPY_A,
39 : COPY_V,
40 : COPY_AV
41 : };
42 :
43 : typedef struct
44 : {
45 : //options
46 : const char *src;
47 : u32 block_size;
48 : u32 copy, probes;
49 : Bool sclock;
50 : const char *fmt, *dev;
51 :
52 : //internal data
53 : const char *fname;
54 : u32 log_class;
55 :
56 : Bool initialized;
57 : Bool raw_data;
58 : //input file
59 : AVFormatContext *demuxer;
60 : //demux options
61 : AVDictionary *options;
62 :
63 : GF_FilterPid **pids;
64 :
65 : Bool raw_pck_out;
66 : u32 nb_playing;
67 : Bool stop_seen;
68 : u64 first_sample_clock, last_frame_ts;
69 : u32 probe_frames;
70 : u32 nb_pck_sent;
71 :
72 : AVPacket pkt;
73 : s32 audio_idx, video_idx;
74 :
75 : u64 *probe_times;
76 :
77 : Bool copy_audio, copy_video;
78 :
79 : u8 *avio_ctx_buffer;
80 : AVIOContext *avio_ctx;
81 : FILE *gfio;
82 : } GF_FFDemuxCtx;
83 :
84 7 : static void ffdmx_finalize(GF_Filter *filter)
85 : {
86 7 : GF_FFDemuxCtx *ctx = (GF_FFDemuxCtx *) gf_filter_get_udta(filter);
87 7 : if (ctx->pids)
88 7 : gf_free(ctx->pids);
89 7 : if (ctx->options)
90 0 : av_dict_free(&ctx->options);
91 7 : if (ctx->probe_times)
92 1 : gf_free(ctx->probe_times);
93 7 : if (ctx->demuxer) {
94 7 : avformat_close_input(&ctx->demuxer);
95 7 : avformat_free_context(ctx->demuxer);
96 : }
97 7 : if (ctx->avio_ctx) {
98 1 : if (ctx->avio_ctx->buffer) av_freep(&ctx->avio_ctx->buffer);
99 1 : av_freep(&ctx->avio_ctx);
100 : }
101 7 : if (ctx->gfio) gf_fclose(ctx->gfio);
102 7 : return;
103 : }
104 :
105 6 : void ffdmx_shared_pck_release(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck)
106 : {
107 6 : GF_FFDemuxCtx *ctx = (GF_FFDemuxCtx *) gf_filter_get_udta(filter);
108 6 : if (ctx->raw_pck_out) {
109 6 : av_free_packet(&ctx->pkt);
110 6 : ctx->raw_pck_out = GF_FALSE;
111 6 : gf_filter_post_process_task(filter);
112 : }
113 6 : }
114 :
115 1202 : static GF_Err ffdmx_process(GF_Filter *filter)
116 : {
117 : GF_Err e;
118 : u32 i;
119 : u64 sample_time;
120 : u8 *data_dst;
121 : Bool copy = GF_TRUE;
122 : GF_FilterPacket *pck_dst;
123 1202 : GF_FFDemuxCtx *ctx = (GF_FFDemuxCtx *) gf_filter_get_udta(filter);
124 :
125 1202 : if (!ctx->nb_playing)
126 : return GF_EOS;
127 :
128 1188 : if (ctx->raw_pck_out)
129 : return GF_EOS;
130 :
131 1188 : sample_time = gf_sys_clock_high_res();
132 1188 : av_init_packet(&ctx->pkt);
133 1188 : ctx->pkt.stream_index = -1;
134 : /*EOF*/
135 1188 : if (av_read_frame(ctx->demuxer, &ctx->pkt) <0) {
136 5 : av_free_packet(&ctx->pkt);
137 5 : if (!ctx->raw_data) {
138 5 : for (i=0; i<ctx->demuxer->nb_streams; i++) {
139 5 : if (ctx->pids[i]) gf_filter_pid_set_eos(ctx->pids[i]);
140 : }
141 : return GF_EOS;
142 : }
143 : return GF_OK;
144 : }
145 :
146 : assert(ctx->pkt.stream_index>=0);
147 : assert(ctx->pkt.stream_index < (s32) ctx->demuxer->nb_streams);
148 :
149 1183 : if (ctx->pkt.pts == AV_NOPTS_VALUE) {
150 0 : if (ctx->pkt.dts == AV_NOPTS_VALUE) {
151 0 : GF_LOG(GF_LOG_WARNING, ctx->log_class, ("[%s] No PTS for packet on stream %d\n", ctx->fname, ctx->pkt.stream_index ));
152 : } else {
153 0 : ctx->pkt.pts = ctx->pkt.dts;
154 : }
155 : }
156 :
157 1183 : if (! ctx->pids[ctx->pkt.stream_index] ) {
158 0 : GF_LOG(GF_LOG_DEBUG, ctx->log_class, ("[%s] No PID defined for given stream %d\n", ctx->fname, ctx->pkt.stream_index ));
159 0 : av_free_packet(&ctx->pkt);
160 0 : return GF_OK;
161 : }
162 1183 : if (ctx->stop_seen && ! gf_filter_pid_is_playing( ctx->pids[ctx->pkt.stream_index] ) ) {
163 0 : av_free_packet(&ctx->pkt);
164 0 : return GF_OK;
165 : }
166 1183 : if (ctx->raw_data && (ctx->probe_frames<ctx->probes) ) {
167 10 : if (ctx->pkt.stream_index==ctx->audio_idx) {
168 0 : av_free_packet(&ctx->pkt);
169 0 : return GF_OK;
170 : }
171 :
172 10 : ctx->probe_times[ctx->probe_frames] = ctx->sclock ? sample_time : ctx->pkt.pts;
173 10 : ctx->probe_frames++;
174 10 : if (ctx->probe_frames==ctx->probes) {
175 : u32 best_diff=0, max_stat=0;
176 10 : for (i=0; i<ctx->probes; i++) {
177 10 : if (i) {
178 : u32 j, nb_stats=0;
179 9 : u32 diff = (u32) (ctx->probe_times[i]-ctx->probe_times[i-1]);
180 90 : for (j=1; j<ctx->probes; j++) {
181 81 : s32 sdiff = (s32) (ctx->probe_times[j]-ctx->probe_times[j-1]);
182 81 : sdiff -= (s32) diff;
183 81 : if (sdiff<0) sdiff = -sdiff;
184 81 : if (sdiff<2000) nb_stats++;
185 : }
186 9 : if (max_stat<nb_stats) {
187 : max_stat = nb_stats;
188 : best_diff = diff;
189 : }
190 : }
191 : }
192 1 : GF_LOG(GF_LOG_INFO, ctx->log_class, ("[%s] Video probing done, frame diff is %d us (for %d frames out of %d)\n", ctx->fname, best_diff, max_stat, ctx->probes));
193 : } else {
194 9 : av_free_packet(&ctx->pkt);
195 9 : return GF_OK;
196 : }
197 : }
198 :
199 1174 : if (ctx->raw_data) {
200 6 : if (ctx->pkt.stream_index==ctx->audio_idx) copy = ctx->copy_audio;
201 6 : else copy = ctx->copy_video;
202 : }
203 :
204 1174 : if (ctx->raw_data && !copy) {
205 : //we don't use shared memory on demuxers since they are usually the ones performing all the buffering
206 6 : pck_dst = gf_filter_pck_new_shared(ctx->pids[ctx->pkt.stream_index], ctx->pkt.data, ctx->pkt.size, ffdmx_shared_pck_release);
207 6 : if (!pck_dst) return GF_OUT_OF_MEM;
208 6 : ctx->raw_pck_out = GF_TRUE;
209 : } else {
210 : //we don't use shared memory on demuxers since they are usually the ones performing all the buffering
211 1168 : pck_dst = gf_filter_pck_new_alloc(ctx->pids[ctx->pkt.stream_index] , ctx->pkt.size, &data_dst);
212 1168 : if (!pck_dst) return GF_OUT_OF_MEM;
213 : assert(pck_dst);
214 1168 : memcpy(data_dst, ctx->pkt.data, ctx->pkt.size);
215 : }
216 :
217 :
218 1177 : if (ctx->raw_data && ctx->sclock) {
219 : u64 ts;
220 3 : if (!ctx->first_sample_clock) {
221 1 : ctx->last_frame_ts = ctx->first_sample_clock = sample_time;
222 : }
223 3 : ts = sample_time - ctx->first_sample_clock;
224 3 : gf_filter_pck_set_cts(pck_dst, ts );
225 3 : ctx->last_frame_ts = ts;
226 1171 : } else if (ctx->pkt.pts != AV_NOPTS_VALUE) {
227 1171 : AVStream *stream = ctx->demuxer->streams[ctx->pkt.stream_index];
228 : u64 ts;
229 1171 : if ((stream->first_dts!=AV_NOPTS_VALUE) && (stream->first_dts<0))
230 0 : ts = (ctx->pkt.pts - stream->first_dts) * stream->time_base.num;
231 : else
232 1171 : ts = ctx->pkt.pts * stream->time_base.num;
233 :
234 1171 : gf_filter_pck_set_cts(pck_dst, ts );
235 :
236 1171 : if (ctx->pkt.dts != AV_NOPTS_VALUE) {
237 1167 : if ((stream->first_dts!=AV_NOPTS_VALUE) && (stream->first_dts<0))
238 0 : ts = (ctx->pkt.dts - stream->first_dts) * stream->time_base.num;
239 : else
240 1167 : ts = ctx->pkt.dts * stream->time_base.num;
241 :
242 1167 : gf_filter_pck_set_dts(pck_dst, ts);
243 : }
244 :
245 1171 : if (ctx->pkt.duration)
246 928 : gf_filter_pck_set_duration(pck_dst, (u32) ctx->pkt.duration);
247 : }
248 :
249 : //fixme: try to identify SAP type 2 and more
250 1174 : if (ctx->pkt.flags & AV_PKT_FLAG_KEY)
251 352 : gf_filter_pck_set_sap(pck_dst, GF_FILTER_SAP_1);
252 :
253 1174 : if (ctx->pkt.flags & AV_PKT_FLAG_CORRUPT)
254 0 : gf_filter_pck_set_corrupted(pck_dst, GF_TRUE);
255 :
256 1174 : gf_net_get_utc();
257 :
258 1174 : if (ctx->raw_data) {
259 6 : u64 ntp = gf_net_get_ntp_ts();
260 6 : gf_filter_pck_set_property(pck_dst, GF_PROP_PCK_SENDER_NTP, &PROP_LONGUINT(ntp) );
261 : }
262 1174 : e = gf_filter_pck_send(pck_dst);
263 1174 : ctx->nb_pck_sent++;
264 1174 : if (!ctx->raw_pck_out)
265 1170 : av_free_packet(&ctx->pkt);
266 : return e;
267 : }
268 :
269 :
270 7 : static GF_Err ffdmx_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *arg_val)
271 : {
272 : s32 res;
273 7 : GF_FFDemuxCtx *ctx = gf_filter_get_udta(filter);
274 :
275 : //initial parsing of arguments
276 7 : if (!ctx->initialized) {
277 2 : switch (arg_val->type) {
278 2 : case GF_PROP_STRING:
279 2 : res = av_dict_set(&ctx->options, arg_name, arg_val->value.string, 0);
280 2 : if (res<0) {
281 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Failed to set option %s:%s\n", ctx->fname, arg_name, arg_val ));
282 : }
283 : break;
284 0 : default:
285 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Failed to set option %s:%s, unrecognized type %d\n", ctx->fname, arg_name, arg_val, arg_val->type ));
286 : return GF_NOT_SUPPORTED;
287 : }
288 : return GF_OK;
289 : }
290 : //updates of arguments, not supported for ffmpeg demuxers
291 : return GF_NOT_SUPPORTED;
292 : }
293 :
294 7 : GF_Err ffdmx_init_common(GF_Filter *filter, GF_FFDemuxCtx *ctx, Bool is_grab)
295 : {
296 : u32 i;
297 : u32 nb_a, nb_v;
298 : #ifdef FF_SUB_SUPPORT
299 : u32 nb_t = 0;
300 : #endif
301 : char szName[50];
302 :
303 :
304 7 : ctx->pids = gf_malloc(sizeof(GF_FilterPid *)*ctx->demuxer->nb_streams);
305 7 : memset(ctx->pids, 0, sizeof(GF_FilterPid *)*ctx->demuxer->nb_streams);
306 :
307 : nb_a = nb_v = 0;
308 14 : for (i = 0; i < ctx->demuxer->nb_streams; i++) {
309 : GF_FilterPid *pid=NULL;
310 : Bool force_reframer = GF_FALSE;
311 : Bool expose_ffdec=GF_FALSE;
312 7 : AVStream *stream = ctx->demuxer->streams[i];
313 7 : AVCodecContext *codec = stream->codec;
314 : u32 gpac_codec_id;
315 :
316 7 : switch(codec->codec_type) {
317 1 : case AVMEDIA_TYPE_AUDIO:
318 1 : pid = gf_filter_pid_new(filter);
319 1 : if (!pid) return GF_OUT_OF_MEM;
320 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_AUDIO) );
321 1 : nb_a++;
322 : sprintf(szName, "audio%d", nb_a);
323 1 : if (ctx->audio_idx<0)
324 0 : ctx->audio_idx = i;
325 : break;
326 :
327 6 : case AVMEDIA_TYPE_VIDEO:
328 6 : pid = gf_filter_pid_new(filter);
329 6 : if (!pid) return GF_OUT_OF_MEM;
330 6 : gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_VISUAL) );
331 6 : nb_v++;
332 : sprintf(szName, "video%d", nb_v);
333 6 : if (ctx->video_idx<0)
334 2 : ctx->video_idx = i;
335 : break;
336 : #ifdef FF_SUB_SUPPORT
337 : case AVMEDIA_TYPE_SUBTITLE:
338 : pid = gf_filter_pid_new(filter);
339 : if (!pid) return GF_OUT_OF_MEM;
340 : gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_TEXT) );
341 : nb_t++;
342 : sprintf(szName, "text%d", nb_t);
343 : break;
344 : #endif
345 0 : default:
346 0 : sprintf(szName, "ffdmx%d", i+1);
347 : break;
348 : }
349 7 : if (!pid) continue;
350 7 : ctx->pids[i] = pid;
351 7 : gf_filter_pid_set_udta(pid, stream);
352 :
353 7 : gf_filter_pid_set_property(pid, GF_PROP_PID_ID, &PROP_UINT(stream->id ? stream->id : i+1) );
354 7 : gf_filter_pid_set_name(pid, szName);
355 :
356 7 : if (ctx->raw_data && ctx->sclock) {
357 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(1000000) );
358 : } else {
359 6 : gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(stream->time_base.den) );
360 : }
361 :
362 7 : if (!ctx->raw_data) {
363 5 : if (stream->duration>=0)
364 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(stream->duration, stream->time_base.den) );
365 4 : else if (ctx->demuxer->duration>=0)
366 4 : gf_filter_pid_set_property(pid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(ctx->demuxer->duration, AV_TIME_BASE) );
367 :
368 5 : if ((stream->first_dts!=AV_NOPTS_VALUE) && (stream->first_dts<0))
369 0 : gf_filter_pid_set_property(pid, GF_PROP_PID_DELAY, &PROP_LONGSINT( stream->first_dts) );
370 : }
371 :
372 7 : if (stream->sample_aspect_ratio.num && stream->sample_aspect_ratio.den)
373 2 : gf_filter_pid_set_property(pid, GF_PROP_PID_SAR, &PROP_FRAC_INT( stream->sample_aspect_ratio.num, stream->sample_aspect_ratio.den ) );
374 :
375 7 : if (stream->metadata) {
376 : AVDictionaryEntry *ent=NULL;
377 : while (1) {
378 12 : ent = av_dict_get(stream->metadata, "", ent, AV_DICT_IGNORE_SUFFIX);
379 8 : if (!ent) break;
380 :
381 : //we use the same syntax as ffmpeg here
382 4 : gf_filter_pid_set_property_str(pid, ent->key, &PROP_STRING(ent->value) );
383 : }
384 : }
385 :
386 7 : gpac_codec_id = ffmpeg_codecid_to_gpac(codec->codec_id);
387 7 : if (!gpac_codec_id) {
388 0 : gf_filter_pid_set_property(pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_FFMPEG) );
389 : expose_ffdec=GF_TRUE;
390 : } else {
391 7 : gf_filter_pid_set_property(pid, GF_PROP_PID_CODECID, &PROP_UINT(gpac_codec_id) );
392 : }
393 :
394 7 : if (is_grab)
395 2 : gf_filter_pid_set_property(pid, GF_PROP_PID_RAWGRAB, &PROP_BOOL(GF_TRUE) );
396 5 : else if (ctx->demuxer->iformat) {
397 5 : if ((ctx->demuxer->iformat->flags & AVFMT_SEEK_TO_PTS) || ctx->demuxer->iformat->read_seek)
398 4 : gf_filter_pid_set_property(pid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
399 : }
400 :
401 : //force reframer for the following formats if no DSI is found
402 7 : if (!codec->extradata_size) {
403 4 : switch (gpac_codec_id) {
404 1 : case GF_CODECID_AC3:
405 : case GF_CODECID_AAC_MPEG4:
406 : case GF_CODECID_AAC_MPEG2_MP:
407 : case GF_CODECID_AAC_MPEG2_LCP:
408 : case GF_CODECID_AAC_MPEG2_SSRP:
409 : case GF_CODECID_AVC:
410 : case GF_CODECID_HEVC:
411 : case GF_CODECID_AV1:
412 : force_reframer = GF_TRUE;
413 1 : break;
414 : }
415 : }
416 7 : if (expose_ffdec) {
417 0 : const char *cname = avcodec_get_name(codec->codec_id);
418 0 : gf_filter_pid_set_property(pid, GF_FFMPEG_DECODER_CONFIG, &PROP_POINTER( (void*)codec ) );
419 :
420 0 : if (cname)
421 0 : gf_filter_pid_set_property_str(pid, "ffmpeg:codec", &PROP_STRING(cname ) );
422 7 : } else if (codec->extradata_size) {
423 :
424 : //avc/hevc read by ffmpeg is still in annex B format
425 3 : if (ctx->demuxer->iformat) {
426 3 : if (!strcmp(ctx->demuxer->iformat->name, "h264") || !strcmp(ctx->demuxer->iformat->name, "hevc")) {
427 : force_reframer = GF_TRUE;
428 : }
429 : }
430 :
431 3 : if (!force_reframer) {
432 : //expose as const data
433 3 : gf_filter_pid_set_property(pid, GF_PROP_PID_DECODER_CONFIG, &PROP_CONST_DATA( (char *)codec->extradata, codec->extradata_size) );
434 : }
435 : }
436 :
437 7 : if (force_reframer) {
438 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_UNFRAMED, &PROP_BOOL(GF_TRUE) );
439 : }
440 :
441 :
442 7 : if (codec->sample_rate)
443 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT( codec->sample_rate ) );
444 7 : if (codec->frame_size)
445 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_SAMPLES_PER_FRAME, &PROP_UINT( codec->frame_size ) );
446 7 : if (codec->channels)
447 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT( codec->channels ) );
448 :
449 7 : if (codec->width)
450 6 : gf_filter_pid_set_property(pid, GF_PROP_PID_WIDTH, &PROP_UINT( codec->width ) );
451 7 : if (codec->height)
452 6 : gf_filter_pid_set_property(pid, GF_PROP_PID_HEIGHT, &PROP_UINT( codec->height ) );
453 : #if LIBAVCODEC_VERSION_MAJOR >= 58
454 : if (codec->framerate.num && codec->framerate.den)
455 : gf_filter_pid_set_property(pid, GF_PROP_PID_FPS, &PROP_FRAC_INT( codec->framerate.num, codec->framerate.den ) );
456 : #endif
457 :
458 7 : if (codec->field_order>AV_FIELD_PROGRESSIVE)
459 0 : gf_filter_pid_set_property(pid, GF_PROP_PID_INTERLACED, &PROP_BOOL(GF_TRUE) );
460 :
461 7 : if ((codec->codec_type==AVMEDIA_TYPE_VIDEO)
462 6 : && (codec->pix_fmt || ((codec->codec_id==AV_CODEC_ID_RAWVIDEO) && codec->codec_tag))
463 : ) {
464 3 : Bool is_full_range = GF_FALSE;
465 : u32 pfmt = 0;
466 :
467 3 : if (codec->pix_fmt) {
468 3 : pfmt = ffmpeg_pixfmt_to_gpac(codec->pix_fmt);
469 3 : is_full_range = ffmpeg_pixfmt_is_fullrange(codec->pix_fmt);
470 0 : } else if (codec->codec_tag) {
471 0 : pfmt = ffmpeg_pixfmt_from_codec_tag(codec->codec_tag, &is_full_range);
472 : }
473 :
474 3 : if (!pfmt) {
475 0 : GF_LOG(GF_LOG_WARNING, ctx->log_class, ("[%s] Unsupported pixel format %d\n", ctx->fname, codec->pix_fmt));
476 : } else {
477 3 : gf_filter_pid_set_property(pid, GF_PROP_PID_PIXFMT, &PROP_UINT( pfmt) );
478 3 : if (is_full_range)
479 0 : gf_filter_pid_set_property(pid, GF_PROP_PID_COLR_RANGE, &PROP_BOOL( GF_TRUE ) );
480 : }
481 : }
482 :
483 :
484 7 : if (codec->sample_fmt>0) {
485 : u32 sfmt = 0;
486 1 : switch (codec->sample_fmt) {
487 : case AV_SAMPLE_FMT_U8: sfmt = GF_AUDIO_FMT_U8; break;
488 0 : case AV_SAMPLE_FMT_S16: sfmt = GF_AUDIO_FMT_S16; break;
489 0 : case AV_SAMPLE_FMT_S32: sfmt = GF_AUDIO_FMT_S32; break;
490 0 : case AV_SAMPLE_FMT_FLT: sfmt = GF_AUDIO_FMT_FLT; break;
491 0 : case AV_SAMPLE_FMT_DBL: sfmt = GF_AUDIO_FMT_DBL; break;
492 0 : case AV_SAMPLE_FMT_U8P: sfmt = GF_AUDIO_FMT_U8P; break;
493 0 : case AV_SAMPLE_FMT_S16P: sfmt = GF_AUDIO_FMT_S16P; break;
494 0 : case AV_SAMPLE_FMT_S32P: sfmt = GF_AUDIO_FMT_S32P; break;
495 1 : case AV_SAMPLE_FMT_FLTP: sfmt = GF_AUDIO_FMT_FLTP; break;
496 0 : case AV_SAMPLE_FMT_DBLP: sfmt = GF_AUDIO_FMT_DBLP; break;
497 0 : default:
498 0 : GF_LOG(GF_LOG_WARNING, ctx->log_class, ("[%s] Unsupported sample format %d\n", ctx->fname, codec->sample_fmt));
499 : }
500 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT( sfmt) );
501 : }
502 :
503 7 : if (codec->bit_rate)
504 1 : gf_filter_pid_set_property(pid, GF_PROP_PID_BITRATE, &PROP_UINT( (u32) codec->bit_rate ) );
505 :
506 7 : gf_filter_pid_set_property(pid, GF_PROP_PID_URL, &PROP_STRING(ctx->demuxer->filename));
507 : }
508 :
509 7 : if (!nb_a && !nb_v
510 : #ifdef FF_SUB_SUPPORT
511 : && !nb_t
512 : #endif
513 : )
514 : return GF_NOT_SUPPORTED;
515 :
516 7 : return GF_OK;
517 : }
518 :
519 27 : static int ffavio_read_packet(void *opaque, uint8_t *buf, int buf_size)
520 : {
521 : GF_FFDemuxCtx *ctx = (GF_FFDemuxCtx *)opaque;
522 27 : return (int) gf_fread(buf, buf_size, ctx->gfio);
523 : }
524 :
525 4 : static int64_t ffavio_seek(void *opaque, int64_t offset, int whence)
526 : {
527 : GF_FFDemuxCtx *ctx = (GF_FFDemuxCtx *)opaque;
528 4 : if (whence==AVSEEK_SIZE) {
529 4 : u64 pos = gf_ftell(ctx->gfio);
530 4 : u64 size = gf_fsize(ctx->gfio);
531 4 : gf_fseek(ctx->gfio, pos, SEEK_SET);
532 4 : return size;
533 : }
534 0 : return (int64_t) gf_fseek(ctx->gfio, offset, whence);
535 : }
536 :
537 5 : static GF_Err ffdmx_initialize(GF_Filter *filter)
538 : {
539 5 : GF_FFDemuxCtx *ctx = gf_filter_get_udta(filter);
540 : GF_Err e;
541 : s32 res;
542 : char *ext;
543 : const char *url;
544 : AVInputFormat *av_in = NULL;
545 5 : ctx->fname = "FFDmx";
546 5 : ctx->log_class = GF_LOG_CONTAINER;
547 :
548 5 : ffmpeg_setup_logs(ctx->log_class);
549 :
550 5 : ctx->initialized = GF_TRUE;
551 : #ifdef GPAC_ENABLE_COVERAGE
552 5 : if (gf_sys_is_cov_mode()) {
553 5 : ffdmx_update_arg(filter, NULL, NULL);
554 : }
555 : #endif
556 5 : if (!ctx->src) {
557 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Missing file name, cannot open\n", ctx->fname));
558 : return GF_SERVICE_ERROR;
559 : }
560 : /*some extensions not supported by ffmpeg, overload input format*/
561 5 : ext = strrchr(ctx->src, '.');
562 5 : if (ext) {
563 4 : if (!stricmp(ext+1, "cmp")) av_in = av_find_input_format("m4v");
564 : }
565 :
566 5 : GF_LOG(GF_LOG_DEBUG, ctx->log_class, ("[%s] opening file %s - av_in %08x\n", ctx->fname, ctx->src, av_in));
567 :
568 5 : ctx->demuxer = avformat_alloc_context();
569 5 : ffmpeg_set_mx_dmx_flags(ctx->options, ctx->demuxer);
570 :
571 5 : url = ctx->src;
572 5 : if (!strncmp(ctx->src, "gfio://", 7)) {
573 1 : ctx->gfio = gf_fopen(ctx->src, "rb");
574 1 : if (!ctx->gfio) {
575 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Failed to open %s\n", ctx->fname, ctx->src));
576 : return GF_URL_ERROR;
577 : }
578 1 : ctx->avio_ctx_buffer = av_malloc(ctx->block_size);
579 1 : if (!ctx->avio_ctx_buffer) {
580 : return GF_OUT_OF_MEM;
581 : }
582 1 : ctx->avio_ctx = avio_alloc_context(ctx->avio_ctx_buffer, ctx->block_size,
583 : 0, ctx, &ffavio_read_packet, NULL, &ffavio_seek);
584 :
585 1 : if (!ctx->avio_ctx) {
586 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Failed to create AVIO context for %s\n", ctx->fname, ctx->src));
587 : return GF_OUT_OF_MEM;
588 : }
589 1 : ctx->demuxer->pb = ctx->avio_ctx;
590 1 : url = gf_fileio_translate_url(ctx->src);
591 : }
592 :
593 5 : res = avformat_open_input(&ctx->demuxer, url, av_in, ctx->options ? &ctx->options : NULL);
594 :
595 5 : switch (res) {
596 : case 0:
597 : e = GF_OK;
598 : break;
599 : case AVERROR_INVALIDDATA:
600 : e = GF_NON_COMPLIANT_BITSTREAM;
601 : break;
602 : case AVERROR_DEMUXER_NOT_FOUND:
603 : case -ENOENT:
604 : e = GF_URL_ERROR;
605 : break;
606 : case AVERROR_EOF:
607 : e = GF_EOS;
608 : break;
609 : default:
610 : e = GF_NOT_SUPPORTED;
611 : break;
612 : }
613 : if (e) {
614 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Fail to open %s - error %s\n", ctx->fname, ctx->src, av_err2str(res) ));
615 0 : avformat_close_input(&ctx->demuxer);
616 0 : avformat_free_context(ctx->demuxer);
617 0 : return e;
618 : }
619 :
620 :
621 5 : ffmpeg_report_unused_options(filter, ctx->options);
622 :
623 5 : res = avformat_find_stream_info(ctx->demuxer, ctx->options ? &ctx->options : NULL);
624 5 : if (res <0) {
625 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] cannot locate streams - error %s\n", ctx->fname, av_err2str(res)));
626 : e = GF_NOT_SUPPORTED;
627 0 : avformat_close_input(&ctx->demuxer);
628 0 : avformat_free_context(ctx->demuxer);
629 0 : return e;
630 : }
631 5 : GF_LOG(GF_LOG_DEBUG, ctx->log_class, ("[%s] file %s opened - %d streams\n", ctx->fname, ctx->src, ctx->demuxer->nb_streams));
632 :
633 5 : return ffdmx_init_common(filter, ctx, GF_FALSE);
634 : }
635 :
636 :
637 9 : static Bool ffdmx_process_event(GF_Filter *filter, const GF_FilterEvent *com)
638 : {
639 9 : GF_FFDemuxCtx *ctx = gf_filter_get_udta(filter);
640 :
641 9 : switch (com->base.type) {
642 7 : case GF_FEVT_PLAY:
643 7 : if (!ctx->nb_playing && !ctx->raw_data
644 5 : && (ctx->stop_seen || (com->play.start_range>0))
645 : ) {
646 0 : int res = av_seek_frame(ctx->demuxer, -1, (s64) (AV_TIME_BASE*com->play.start_range), AVSEEK_FLAG_BACKWARD);
647 0 : if (res<0) {
648 0 : GF_LOG(GF_LOG_WARNING, ctx->log_class, ("[%s] Fail to seek %s to %g - error %s\n", ctx->fname, ctx->src, com->play.start_range, av_err2str(res) ));
649 : }
650 : }
651 7 : ctx->nb_playing++;
652 7 : ctx->stop_seen = GF_FALSE;
653 : //cancel event
654 7 : return GF_TRUE;
655 :
656 2 : case GF_FEVT_STOP:
657 2 : if (ctx->nb_playing) {
658 2 : ctx->nb_playing--;
659 2 : if (!ctx->nb_playing && ctx->nb_pck_sent)
660 2 : ctx->stop_seen = GF_TRUE;
661 : }
662 : //cancel event
663 : return GF_TRUE;
664 :
665 : case GF_FEVT_SET_SPEED:
666 : //cancel event
667 : return GF_TRUE;
668 : default:
669 : break;
670 : }
671 : //by default don't cancel event - to rework once we have downloading in place
672 0 : return GF_FALSE;
673 : }
674 :
675 2938 : static GF_FilterProbeScore ffdmx_probe_url(const char *url, const char *mime)
676 : {
677 2938 : if (!strncmp(url, "video://", 8)) return GF_FPROBE_NOT_SUPPORTED;
678 2938 : if (!strncmp(url, "audio://", 8)) return GF_FPROBE_NOT_SUPPORTED;
679 2938 : if (!strncmp(url, "av://", 5)) return GF_FPROBE_NOT_SUPPORTED;
680 2938 : if (!strncmp(url, "pipe://", 7)) return GF_FPROBE_NOT_SUPPORTED;
681 2930 : return GF_FPROBE_MAYBE_SUPPORTED;
682 : }
683 :
684 :
685 3063 : static const char *ffdmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
686 : {
687 : int ffscore;
688 : AVInputFormat *probe_fmt;
689 : AVProbeData pb;
690 :
691 3063 : av_register_all();
692 :
693 :
694 : memset(&pb, 0, sizeof(AVProbeData));
695 : //not setting this crashes some probers in ffmpeg
696 3063 : pb.filename = "";
697 3063 : if (size <= AVPROBE_PADDING_SIZE) {
698 4 : pb.buf = gf_malloc(sizeof(char)*(size+AVPROBE_PADDING_SIZE) );
699 4 : memcpy(pb.buf, data, sizeof(char)*size);
700 4 : pb.buf_size = size;
701 4 : probe_fmt = av_probe_input_format3(&pb, GF_FALSE, &ffscore);
702 4 : gf_free(pb.buf);
703 : } else {
704 3059 : pb.buf = (char *) data;
705 3059 : pb.buf_size = size - AVPROBE_PADDING_SIZE;
706 3059 : probe_fmt = av_probe_input_format3(&pb, GF_FALSE, &ffscore);
707 : }
708 :
709 3063 : if (!probe_fmt) return NULL;
710 0 : if (probe_fmt->mime_type) {
711 : //TODO try to refine based on ffprobe score
712 0 : *score = GF_FPROBE_MAYBE_SUPPORTED;
713 0 : return probe_fmt->mime_type;
714 : }
715 : return NULL;
716 : }
717 :
718 : #define OFFS(_n) #_n, offsetof(GF_FFDemuxCtx, _n)
719 :
720 : static const GF_FilterCapability FFDmxCaps[] =
721 : {
722 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
723 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
724 : };
725 :
726 :
727 : GF_FilterRegister FFDemuxRegister = {
728 : .name = "ffdmx",
729 : .version=LIBAVFORMAT_IDENT,
730 : GF_FS_SET_DESCRIPTION("FFMPEG demuxer")
731 : GF_FS_SET_HELP("Demultiplexes files and open protocol using FFMPEG.\n"
732 : "See FFMPEG documentation (https://ffmpeg.org/documentation.html) for more details.\n"
733 : "To list all supported demuxers for your GPAC build, use `gpac -h ffdmx:*`.\n"
734 : "This will list both supported input formats and protocols.\n"
735 : "Input protocols are listed with `Description: Input protocol`, and the subclass name identitfes the protocol scheme.\n"
736 : "For example, if `ffdmx:rtmp` is listed as input protocol, this means `rtmp://` source URLs are supported.\n"
737 : )
738 : .private_size = sizeof(GF_FFDemuxCtx),
739 : SETCAPS(FFDmxCaps),
740 : .initialize = ffdmx_initialize,
741 : .finalize = ffdmx_finalize,
742 : .process = ffdmx_process,
743 : .update_arg = ffdmx_update_arg,
744 : .probe_url = ffdmx_probe_url,
745 : .probe_data = ffdmx_probe_data,
746 : .process_event = ffdmx_process_event,
747 : .flags = GF_FS_REG_META,
748 : };
749 :
750 :
751 : static const GF_FilterArgs FFDemuxArgs[] =
752 : {
753 : { OFFS(src), "URL of source content", GF_PROP_NAME, NULL, NULL, 0},
754 : { "*", -1, "any possible options defined for AVFormatContext and sub-classes. See `gpac -hx ffdmx` and `gpac -hx ffdmx:*`", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_META},
755 : {0}
756 : };
757 :
758 :
759 2877 : const GF_FilterRegister *ffdmx_register(GF_FilterSession *session)
760 : {
761 2877 : ffmpeg_build_register(session, &FFDemuxRegister, FFDemuxArgs, 2, FF_REG_TYPE_DEMUX);
762 2877 : return &FFDemuxRegister;
763 : }
764 :
765 :
766 :
767 2 : static GF_Err ffavin_initialize(GF_Filter *filter)
768 : {
769 2 : s32 res, i, dev_idx=-1;
770 : Bool has_a, has_v;
771 : char szPatchedName[1024];
772 : const char *dev_name=NULL;
773 : const char *default_fmt=NULL;
774 : AVInputFormat *dev_fmt=NULL;
775 2 : GF_FFDemuxCtx *ctx = gf_filter_get_udta(filter);
776 : Bool wants_audio = GF_FALSE;
777 : Bool wants_video = GF_FALSE;
778 2 : ctx->fname = "FFAVIn";
779 2 : ctx->log_class = GF_LOG_MMIO;
780 :
781 2 : ffmpeg_setup_logs(ctx->log_class);
782 :
783 2 : avdevice_register_all();
784 2 : ctx->initialized = GF_TRUE;
785 2 : if (!ctx->src) {
786 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] No source URL specified, expecting video://, audio:/ or av://\n", ctx->fname));
787 : return GF_SERVICE_ERROR;
788 : }
789 2 : default_fmt = ctx->fmt;
790 2 : if (!default_fmt) {
791 : #ifdef WIN32
792 : default_fmt = "dshow";
793 : #elif defined(__DARWIN) || defined(__APPLE__)
794 : default_fmt = "avfoundation";
795 : #else
796 : default_fmt = "video4linux2";
797 : #endif
798 : }
799 :
800 2 : if (default_fmt) {
801 2 : dev_fmt = av_find_input_format(default_fmt);
802 2 : if (dev_fmt == NULL) {
803 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Cannot find the input format %s\n", ctx->fname, ctx->fmt));
804 : }
805 : #if LIBAVCODEC_VERSION_MAJOR >= 58
806 : else if (dev_fmt->priv_class->category!=AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT) {
807 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s]] %s is not a video input device\n", ctx->fname, ctx->fmt));
808 : dev_fmt = NULL;
809 : }
810 : #else
811 : //not supported for old FFMPE versions
812 : #endif
813 : }
814 : #if (LIBAVCODEC_VERSION_MAJOR >= 58) && (LIBAVCODEC_VERSION_MINOR>=20)
815 : if (!dev_fmt) {
816 : while (1) {
817 : dev_fmt = av_input_video_device_next(dev_fmt);
818 : if (!dev_fmt) break;
819 : if (!dev_fmt || !dev_fmt->priv_class) continue;
820 : if ((dev_fmt->priv_class->category != AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT) && (dev_fmt->priv_class->category != AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT))
821 : continue;
822 :
823 : //listing devices is on its way of implementation in ffmpeg ... for now break at first provider
824 : break;
825 : }
826 : }
827 : #endif
828 2 : if (!dev_fmt) {
829 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] No input format specified\n", ctx->fname, ctx->fmt));
830 : return GF_BAD_PARAM;
831 : }
832 :
833 2 : dev_name = ctx->dev;
834 :
835 2 : if (!strncmp(ctx->src, "video://", 8)) wants_video = GF_TRUE;
836 0 : else if (!strncmp(ctx->src, "audio://", 8)) wants_audio = GF_TRUE;
837 0 : else if (!strncmp(ctx->src, "av://", 5)) wants_video = wants_audio = GF_TRUE;
838 :
839 2 : if (sscanf(dev_name, "%d", &dev_idx)==1) {
840 0 : sprintf(szPatchedName, "%d", dev_idx);
841 0 : if (strcmp(szPatchedName, dev_name))
842 0 : dev_idx = -1;
843 : } else {
844 2 : dev_idx = -1;
845 : }
846 :
847 2 : szPatchedName[0]=0;
848 :
849 : #if defined(__DARWIN) || defined(__APPLE__)
850 : if (!strncmp(dev_name, "screen", 6)) {
851 : strcpy(szPatchedName, "Capture screen ");
852 : strcat(szPatchedName, dev_name+6);
853 : dev_name = (char *) szPatchedName;
854 : }
855 : #endif
856 2 : if (!strncmp(dev_fmt->priv_class->class_name, "V4L2", 4) && (dev_idx>=0) ) {
857 0 : if (wants_audio) {
858 : sprintf(szPatchedName, "/dev/video%d:hw:%d", dev_idx, dev_idx);
859 : } else {
860 : sprintf(szPatchedName, "/dev/video%d", dev_idx);
861 : }
862 : dev_name = (char *) szPatchedName;
863 : }
864 :
865 2 : else if (wants_video && wants_audio && (dev_idx>=0)) {
866 : sprintf(szPatchedName, "%d:%d", dev_idx, dev_idx);
867 : dev_name = (char *) szPatchedName;
868 : }
869 : #if defined(__APPLE__) && !defined(GPAC_CONFIG_IOS)
870 : else if (!strncmp(dev_fmt->priv_class->class_name, "AVFoundation", 12) && wants_audio) {
871 : if (ctx->dev[0] != ':') {
872 : strcpy(szPatchedName, ":");
873 : strcat(szPatchedName, ctx->dev);
874 : dev_name = (char *) szPatchedName;
875 : }
876 : }
877 : #endif
878 :
879 : /* Open video */
880 2 : ctx->demuxer = avformat_alloc_context();
881 2 : ffmpeg_set_mx_dmx_flags(ctx->options, ctx->demuxer);
882 :
883 2 : res = avformat_open_input(&ctx->demuxer, dev_name, dev_fmt, &ctx->options);
884 2 : if ( (res < 0) && !stricmp(ctx->dev, "screen-capture-recorder") ) {
885 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Buggy screen capture input (open failed with code %d), retrying without specifying resolution\n", ctx->fname, res));
886 0 : av_dict_set(&ctx->options, "video_size", NULL, 0);
887 0 : res = avformat_open_input(&ctx->demuxer, ctx->dev, dev_fmt, &ctx->options);
888 : }
889 :
890 2 : if (res < 0) {
891 0 : if (ctx->options) {
892 0 : av_dict_free(&ctx->options);
893 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Error %d opening input - retrying without options\n", ctx->fname, res));
894 0 : res = avformat_open_input(&ctx->demuxer, dev_name, dev_fmt, NULL);
895 : }
896 0 : if (res<0) {
897 0 : av_dict_set(&ctx->options, "framerate", "30", 0);
898 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Error %d opening input - retrying with 30 fps\n", ctx->fname, res));
899 0 : res = avformat_open_input(&ctx->demuxer, dev_name, dev_fmt, &ctx->options);
900 0 : if (res < 0) {
901 0 : av_dict_set(&ctx->options, "framerate", "25", 0);
902 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Error %d opening input - retrying with 25 fps\n", ctx->fname, res));
903 0 : res = avformat_open_input(&ctx->demuxer, dev_name, dev_fmt, &ctx->options);
904 : }
905 : }
906 : }
907 :
908 2 : if (res < 0) {
909 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] Cannot open device %s:%s\n", ctx->fname, dev_fmt->priv_class->class_name, ctx->dev));
910 : return -1;
911 : }
912 :
913 2 : ffmpeg_report_unused_options(filter, ctx->options);
914 :
915 2 : av_dump_format(ctx->demuxer, 0, ctx->dev, 0);
916 2 : ctx->raw_data = GF_TRUE;
917 2 : ctx->audio_idx = ctx->video_idx = -1;
918 :
919 2 : res = avformat_find_stream_info(ctx->demuxer, ctx->options ? &ctx->options : NULL);
920 :
921 2 : if (res <0) {
922 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] cannot locate streams - error %s\n", ctx->fname, av_err2str(res)));
923 : return GF_NOT_SUPPORTED;
924 : }
925 :
926 : //check we have the stream we want
927 : has_a = has_v = GF_FALSE;
928 2 : for (i = 0; (u32) i < ctx->demuxer->nb_streams; i++) {
929 2 : switch(ctx->demuxer->streams[i]->codec->codec_type) {
930 0 : case AVMEDIA_TYPE_AUDIO:
931 : has_a = GF_TRUE;
932 0 : break;
933 2 : case AVMEDIA_TYPE_VIDEO:
934 : has_v = GF_TRUE;
935 2 : break;
936 : default:
937 : break;
938 : }
939 : }
940 2 : if (wants_audio && !has_a) {
941 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] No audio stream in input device\n", ctx->fname));
942 : return GF_NOT_SUPPORTED;
943 : }
944 2 : if (wants_video && !has_v) {
945 0 : GF_LOG(GF_LOG_ERROR, ctx->log_class, ("[%s] No video stream in input device\n", ctx->fname));
946 : return GF_NOT_SUPPORTED;
947 : }
948 2 : ctx->probe_frames = ctx->probes;
949 2 : if (has_v && ctx->probes) {
950 1 : ctx->probe_times = gf_malloc(sizeof(u64) * ctx->probes);
951 1 : memset(ctx->probe_times, 0, sizeof(u64) * ctx->probes);
952 : //we probe timestamps in either modes because timestamps of first frames are sometimes off
953 1 : ctx->probe_frames = 0;
954 : }
955 :
956 2 : GF_LOG(GF_LOG_INFO, ctx->log_class, ("[%s] device %s:%s opened - %d streams\n", ctx->fname, dev_fmt->priv_class->class_name, ctx->dev, ctx->demuxer->nb_streams));
957 :
958 2 : ctx->copy_audio = ctx->copy_video = GF_FALSE;
959 2 : switch (ctx->copy) {
960 : case COPY_NO:
961 : break;
962 1 : case COPY_A:
963 1 : ctx->copy_audio = GF_TRUE;
964 1 : break;
965 0 : case COPY_V:
966 0 : ctx->copy_video = GF_TRUE;
967 0 : break;
968 0 : default:
969 0 : ctx->copy_audio = ctx->copy_video = GF_TRUE;
970 0 : break;
971 : }
972 2 : if (wants_audio && wants_video) {
973 0 : if (!ctx->copy_audio || !ctx->copy_video) {
974 0 : GF_LOG(GF_LOG_WARNING, ctx->log_class, ("[%s] using muxed capture av:// without copy on %s, this might introduce packet losses due to blocking modes or delayed consumption of the frames. If experiencing problems, either set [-copy]() to `AV` or consider using two filters video:// and audio://\n", ctx->fname, (!ctx->copy_audio && !ctx->copy_video) ? "audio and video streams" : ctx->copy_video ? "audio stream" : "video stream"));
975 : }
976 : }
977 2 : return ffdmx_init_common(filter, ctx, GF_TRUE);
978 : }
979 :
980 2941 : static GF_FilterProbeScore ffavin_probe_url(const char *url, const char *mime)
981 : {
982 2941 : if (!strncmp(url, "video://", 8)) return GF_FPROBE_MAYBE_SUPPORTED;
983 2939 : if (!strncmp(url, "audio://", 8)) return GF_FPROBE_MAYBE_SUPPORTED;
984 2939 : if (!strncmp(url, "av://", 5)) return GF_FPROBE_MAYBE_SUPPORTED;
985 2939 : return GF_FPROBE_NOT_SUPPORTED;
986 : }
987 :
988 : static const GF_FilterCapability FFAVInCaps[] =
989 : {
990 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
991 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL)
992 : //do not expose a specific codec ID (eg raw) as some grabbers might give us mjpeg
993 : };
994 :
995 : GF_FilterRegister FFAVInRegister = {
996 : .name = "ffavin",
997 : .version = LIBAVDEVICE_IDENT,
998 : GF_FS_SET_DESCRIPTION("FFMPEG AV Capture")
999 : GF_FS_SET_HELP("Reads from audio/video capture devices using FFMPEG.\n"
1000 : "See FFMPEG documentation (https://ffmpeg.org/documentation.html) for more details.\n"
1001 : "To list all supported grabbers for your GPAC build, use `gpac -h ffavin:*`.\n"
1002 : "\n"
1003 : "# Device identification\n"
1004 : "Typical classes are `dshow` on windows, `avfoundation` on OSX, `video4linux2` or `x11grab` on linux\n"
1005 : "\n"
1006 : "Typical device name can be the webcam name:\n"
1007 : "- `FaceTime HD Camera` on OSX, device name on windows, `/dev/video0` on linux\n"
1008 : "- `screen-capture-recorder`, see http://screencapturer.sf.net/ on windows\n"
1009 : "- `Capture screen 0` on OSX (0=first screen), or `screenN` for short\n"
1010 : "- X display name (eg `:0.0`) on linux\n"
1011 : "\n"
1012 : "The general mapping from ffmpeg command line is:\n"
1013 : "- ffmpeg `-f` maps to [-fmt]() option\n"
1014 : "- ffmpeg `-i` maps to [-dev]() option\n"
1015 : "\n"
1016 : "EX ffmpeg -f libndi_newtek -i MY_NDI_TEST ...\n"
1017 : "EX gpac -i av://:fmt=libndi_newtek:dev=MY_NDI_TEST ...\n"
1018 : "\n"
1019 : )
1020 : .private_size = sizeof(GF_FFDemuxCtx),
1021 : SETCAPS(FFAVInCaps),
1022 : .initialize = ffavin_initialize,
1023 : .finalize = ffdmx_finalize,
1024 : .process = ffdmx_process,
1025 : .update_arg = ffdmx_update_arg,
1026 : .probe_url = ffavin_probe_url,
1027 : .process_event = ffdmx_process_event,
1028 : .flags = GF_FS_REG_META,
1029 : };
1030 :
1031 :
1032 : static const GF_FilterArgs FFAVInArgs[] =
1033 : {
1034 : { OFFS(src), "url of device, `video://`, `audio://` or `av://`", GF_PROP_STRING, NULL, NULL, 0},
1035 : { OFFS(fmt), "name of device class - see filter help. If not set, defaults to first device class", GF_PROP_STRING, NULL, NULL, 0},
1036 : { OFFS(dev), "name of device or index of device - see filter help", GF_PROP_STRING, "0", NULL, 0},
1037 : { OFFS(copy), "set copy mode of raw frames\n"
1038 : "- N: frames are only forwarded (shared memory, no copy)\n"
1039 : "- A: audio frames are copied, video frames are forwarded\n"
1040 : "- V: video frames are copied, audio frames are forwarded\n"
1041 : "- AV: all frames are copied"
1042 : "", GF_PROP_UINT, "A", "N|A|V|AV", GF_FS_ARG_HINT_ADVANCED},
1043 : { OFFS(sclock), "use system clock (us) instead of device timestamp (for buggy devices)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1044 : { OFFS(probes), "probe a given number of video frames before emitting - this usually helps with bad timing of the first frames", GF_PROP_UINT, "10", "0-100", GF_FS_ARG_HINT_EXPERT},
1045 : { OFFS(block_size), "block size used to read file when using avio context", GF_PROP_UINT, "4096", NULL, GF_FS_ARG_HINT_EXPERT},
1046 : { "*", -1, "any possible options defined for AVInputFormat and AVFormatContext. See `gpac -hx ffavin` and `gpac -hx ffavin:*`", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_META},
1047 : {0}
1048 : };
1049 :
1050 :
1051 : //number of arguments defined above
1052 : const int FFAVIN_STATIC_ARGS = (sizeof (FFAVInArgs) / sizeof (GF_FilterArgs)) - 1;
1053 :
1054 2877 : const GF_FilterRegister *ffavin_register(GF_FilterSession *session)
1055 : {
1056 2877 : ffmpeg_build_register(session, &FFAVInRegister, FFAVInArgs, FFAVIN_STATIC_ARGS, FF_REG_TYPE_DEV_IN);
1057 2877 : return &FFAVInRegister;
1058 : }
1059 :
1060 : #else
1061 :
1062 : #include <gpac/filters.h>
1063 :
1064 : const GF_FilterRegister *ffdmx_register(GF_FilterSession *session)
1065 : {
1066 : return NULL;
1067 : }
1068 :
1069 : const GF_FilterRegister *ffavin_register(GF_FilterSession *session)
1070 : {
1071 : return NULL;
1072 : }
1073 : #endif
|