Unified · Split
| @@ -34,6 +34,7 @@ version <next>: · 33 unmodified lines | |||
| 34 | - floodfill video filter | 34 | - floodfill video filter |
| 35 | - pseudocolor video filter | 35 | - pseudocolor video filter |
| 36 | - raw G.726 muxer and demuxer, left- and right-justified | 36 | - raw G.726 muxer and demuxer, left- and right-justified |
| 37 | - NewTek NDI input/output device | ||
| 37 | 38 | ||
| 38 | version 3.3: | 39 | version 3.3: |
| 39 | - CrystalHD decoder moved to new decode API | 40 | - CrystalHD decoder moved to new decode API |
| @@ -277,6 +277,7 @@ External library support: · 276 unmodified lines | |||
| 277 | --enable-libzvbi enable teletext support via libzvbi [no] | 277 | --enable-libzvbi enable teletext support via libzvbi [no] |
| 278 | --disable-lzma disable lzma [autodetect] | 278 | --disable-lzma disable lzma [autodetect] |
| 279 | --enable-decklink enable Blackmagic DeckLink I/O support [no] | 279 | --enable-decklink enable Blackmagic DeckLink I/O support [no] |
| 280 | --enable-libndi_newtek enable Newteck NDI I/O support [no] | ||
| 280 | --enable-mediacodec enable Android MediaCodec support [no] | 281 | --enable-mediacodec enable Android MediaCodec support [no] |
| 281 | --enable-libmysofa enable libmysofa, needed for sofalizer filter [no] | 282 | --enable-libmysofa enable libmysofa, needed for sofalizer filter [no] |
| 282 | --enable-openal enable OpenAL 1.1 capture support [no] | 283 | --enable-openal enable OpenAL 1.1 capture support [no] |
| @@ -1508,6 +1509,7 @@ EXTERNAL_LIBRARY_GPL_LIST=" · 1225 unmodified lines | |||
| 1508 | 1509 | ||
| 1509 | EXTERNAL_LIBRARY_NONFREE_LIST=" | 1510 | EXTERNAL_LIBRARY_NONFREE_LIST=" |
| 1510 | decklink | 1511 | decklink |
| 1512 | libndi_newtek | ||
| 1511 | libfdk_aac | 1513 | libfdk_aac |
| 1512 | openssl | 1514 | openssl |
| 1513 | " | 1515 | " |
| @@ -3014,6 +3016,10 @@ decklink_indev_deps="decklink threads" · 1500 unmodified lines | |||
| 3014 | decklink_indev_extralibs="-lstdc++" | 3016 | decklink_indev_extralibs="-lstdc++" |
| 3015 | decklink_outdev_deps="decklink threads" | 3017 | decklink_outdev_deps="decklink threads" |
| 3016 | decklink_outdev_extralibs="-lstdc++" | 3018 | decklink_outdev_extralibs="-lstdc++" |
| 3019 | libndi_newtek_indev_deps="libndi_newtek" | ||
| 3020 | libndi_newtek_indev_extralibs="-lndi" | ||
| 3021 | libndi_newtek_outdev_deps="libndi_newtek" | ||
| 3022 | libndi_newtek_outdev_extralibs="-lndi" | ||
| 3017 | dshow_indev_deps="IBaseFilter" | 3023 | dshow_indev_deps="IBaseFilter" |
| 3018 | dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid -loleaut32 -lshlwapi" | 3024 | dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid -loleaut32 -lshlwapi" |
| 3019 | dv1394_indev_deps="dv1394" | 3025 | dv1394_indev_deps="dv1394" |
| @@ -5567,6 +5573,8 @@ avisynth_demuxer_extralibs='$ldl' · 2547 unmodified lines | |||
| 5567 | cuda_extralibs='$ldl' | 5573 | cuda_extralibs='$ldl' |
| 5568 | decklink_outdev_extralibs="$decklink_outdev_extralibs $ldl" | 5574 | decklink_outdev_extralibs="$decklink_outdev_extralibs $ldl" |
| 5569 | decklink_indev_extralibs="$decklink_indev_extralibs $ldl" | 5575 | decklink_indev_extralibs="$decklink_indev_extralibs $ldl" |
| 5576 | libndi_newtek_outdev_extralibs="$libndi_newtek_outdev_extralibs $ldl" | ||
| 5577 | libndi_newtek_indev_extralibs="$libndi_newtek_indev_extralibs $ldl" | ||
| 5570 | frei0r_filter_extralibs='$ldl' | 5578 | frei0r_filter_extralibs='$ldl' |
| 5571 | frei0r_src_filter_extralibs='$ldl' | 5579 | frei0r_src_filter_extralibs='$ldl' |
| 5572 | ladspa_filter_extralibs='$ldl' | 5580 | ladspa_filter_extralibs='$ldl' |
| @@ -5825,6 +5833,7 @@ enabled coreimage_filter && { check_header_objcc QuartzCore/CoreImage.h || disa · 252 unmodified lines | |||
| 5825 | enabled coreimagesrc_filter && { check_header_objcc QuartzCore/CoreImage.h || disable coreimagesrc_filter; } | 5833 | enabled coreimagesrc_filter && { check_header_objcc QuartzCore/CoreImage.h || disable coreimagesrc_filter; } |
| 5826 | enabled decklink && { { check_header DeckLinkAPI.h || die "ERROR: DeckLinkAPI.h header not found"; } && | 5834 | enabled decklink && { { check_header DeckLinkAPI.h || die "ERROR: DeckLinkAPI.h header not found"; } && |
| 5827 | { check_cpp_condition DeckLinkAPIVersion.h "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a060100" || die "ERROR: Decklink API version must be >= 10.6.1."; } } | 5835 | { check_cpp_condition DeckLinkAPIVersion.h "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a060100" || die "ERROR: Decklink API version must be >= 10.6.1."; } } |
| 5836 | enabled libndi_newtek && { check_header Processing.NDI.Lib.h || die "ERROR: Processing.NDI.Lib.h header not found"; } | ||
| 5828 | enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; } | 5837 | enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; } |
| 5829 | enabled gmp && require gmp gmp.h mpz_export -lgmp | 5838 | enabled gmp && require gmp gmp.h mpz_export -lgmp |
| 5830 | enabled gnutls && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init | 5839 | enabled gnutls && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init |
| @@ -332,6 +332,54 @@ ffmpeg -channels 16 -format_code Hi50 -f decklink -i 'UltraStudio Mini Recorder' · 331 unmodified lines | |||
| 332 | 332 | ||
| 333 | @end itemize | 333 | @end itemize |
| 334 | 334 | ||
| 335 | @section libndi_newtek | ||
| 336 | |||
| 337 | The libndi_newtek input device provides capture capabilities for using NDI (Network | ||
| 338 | Device Interface, standard created by NewTek). | ||
| 339 | |||
| 340 | Input filename is a NDI source name that could be found by sending -find_sources 1 | ||
| 341 | to command line - it has no specific syntax but human-readable formatted. | ||
| 342 | |||
| 343 | To enable this input device, you need the NDI SDK and you | ||
| 344 | need to configure with the appropriate @code{--extra-cflags} | ||
| 345 | and @code{--extra-ldflags}. | ||
| 346 | |||
| 347 | @subsection Options | ||
| 348 | |||
| 349 | @table @option | ||
| 350 | |||
| 351 | @item find_sources | ||
| 352 | If set to @option{true}, print a list of found/available NDI sources and exit. | ||
| 353 | Defaults to @option{false}. | ||
| 354 | |||
| 355 | @item wait_sources | ||
| 356 | Override time to wait until the number of online sources have changed. | ||
| 357 | Defaults to @option{0.5}. | ||
| 358 | |||
| 359 | @item allow_video_fields | ||
| 360 | When this flag is @option{false}, all video that you receive will be progressive. | ||
| 361 | Defaults to @option{true}. | ||
| 362 | |||
| 363 | @end table | ||
| 364 | |||
| 365 | @subsection Examples | ||
| 366 | |||
| 367 | @itemize | ||
| 368 | |||
| 369 | @item | ||
| 370 | List input devices: | ||
| 371 | @example | ||
| 372 | ffmpeg -f libndi_newtek -find_sources 1 -i dummy | ||
| 373 | @end example | ||
| 374 | |||
| 375 | @item | ||
| 376 | Restream to NDI: | ||
| 377 | @example | ||
| 378 | ffmpeg -f libndi_newtek -i "DEV-5.INTERNAL.M1STEREO.TV (NDI_SOURCE_NAME_1)" -f libndi_newtek -y NDI_SOURCE_NAME_2 | ||
| 379 | @end example | ||
| 380 | |||
| 381 | @end itemize | ||
| 382 | |||
| 335 | @section dshow | 383 | @section dshow |
| 336 | 384 | ||
| 337 | Windows DirectShow input device. | 385 | Windows DirectShow input device. |
| @@ -182,6 +182,51 @@ ffmpeg -i test.avi -f decklink -pix_fmt uyvy422 -s 720x486 -r 24000/1001 'DeckLi · 181 unmodified lines | |||
| 182 | 182 | ||
| 183 | @end itemize | 183 | @end itemize |
| 184 | 184 | ||
| 185 | @section libndi_newtek | ||
| 186 | |||
| 187 | The libndi_newtek output device provides playback capabilities for using NDI (Network | ||
| 188 | Device Interface, standard created by NewTek). | ||
| 189 | |||
| 190 | Output filename is a NDI name. | ||
| 191 | |||
| 192 | To enable this output device, you need the NDI SDK and you | ||
| 193 | need to configure with the appropriate @code{--extra-cflags} | ||
| 194 | and @code{--extra-ldflags}. | ||
| 195 | |||
| 196 | NDI uses uyvy422 pixel format natively, but also supports bgra, bgr0, rgba and | ||
| 197 | rgb0. | ||
| 198 | |||
| 199 | @subsection Options | ||
| 200 | |||
| 201 | @table @option | ||
| 202 | |||
| 203 | @item reference_level | ||
| 204 | The audio reference level in dB. This specifies how many dB above the | ||
| 205 | reference level (+4dBU) is the full range of 16 bit audio. | ||
| 206 | Defaults to @option{0}. | ||
| 207 | |||
| 208 | @item clock_video | ||
| 209 | These specify whether video "clock" themselves. | ||
| 210 | Defaults to @option{false}. | ||
| 211 | |||
| 212 | @item clock_audio | ||
| 213 | These specify whether audio "clock" themselves. | ||
| 214 | Defaults to @option{false}. | ||
| 215 | |||
| 216 | @end table | ||
| 217 | |||
| 218 | @subsection Examples | ||
| 219 | |||
| 220 | @itemize | ||
| 221 | |||
| 222 | @item | ||
| 223 | Play video clip: | ||
| 224 | @example | ||
| 225 | ffmpeg -i "udp://@@239.1.1.1:10480?fifo_size=1000000&overrun_nonfatal=1" -vf "scale=720:576,fps=fps=25,setdar=dar=16/9,format=pix_fmts=uyvy422" -f libndi_newtek NEW_NDI1 | ||
| 226 | @end example | ||
| 227 | |||
| 228 | @end itemize | ||
| 229 | |||
| 185 | @section fbdev | 230 | @section fbdev |
| 186 | 231 | ||
| 187 | Linux framebuffer output device. | 232 | Linux framebuffer output device. |
| @@ -19,6 +19,8 @@ OBJS-$(CONFIG_BKTR_INDEV) += bktr.o · 18 unmodified lines | |||
| 19 | OBJS-$(CONFIG_CACA_OUTDEV) += caca.o | 19 | OBJS-$(CONFIG_CACA_OUTDEV) += caca.o |
| 20 | OBJS-$(CONFIG_DECKLINK_OUTDEV) += decklink_enc.o decklink_enc_c.o decklink_common.o | 20 | OBJS-$(CONFIG_DECKLINK_OUTDEV) += decklink_enc.o decklink_enc_c.o decklink_common.o |
| 21 | OBJS-$(CONFIG_DECKLINK_INDEV) += decklink_dec.o decklink_dec_c.o decklink_common.o | 21 | OBJS-$(CONFIG_DECKLINK_INDEV) += decklink_dec.o decklink_dec_c.o decklink_common.o |
| 22 | OBJS-$(CONFIG_LIBNDI_NEWTEK_OUTDEV) += libndi_newtek_enc.o | ||
| 23 | OBJS-$(CONFIG_LIBNDI_NEWTEK_INDEV) += libndi_newtek_dec.o | ||
| 22 | OBJS-$(CONFIG_DSHOW_INDEV) += dshow_crossbar.o dshow.o dshow_enummediatypes.o \ | 24 | OBJS-$(CONFIG_DSHOW_INDEV) += dshow_crossbar.o dshow.o dshow_enummediatypes.o \ |
| 23 | dshow_enumpins.o dshow_filter.o \ | 25 | dshow_enumpins.o dshow_filter.o \ |
| 24 | dshow_pin.o dshow_common.o | 26 | dshow_pin.o dshow_common.o |
| @@ -60,6 +62,8 @@ SLIBOBJS-$(HAVE_GNU_WINDRES) += avdeviceres.o · 35 unmodified lines | |||
| 60 | SKIPHEADERS += decklink_common.h | 62 | SKIPHEADERS += decklink_common.h |
| 61 | SKIPHEADERS-$(CONFIG_DECKLINK) += decklink_enc.h decklink_dec.h \ | 63 | SKIPHEADERS-$(CONFIG_DECKLINK) += decklink_enc.h decklink_dec.h \ |
| 62 | decklink_common_c.h | 64 | decklink_common_c.h |
| 65 | SKIPHEADERS-$(CONFIG_LIBNDI_NEWTEK_INDEV) += libndi_newtek_common.h | ||
| 66 | SKIPHEADERS-$(CONFIG_LIBNDI_NEWTEK_OUTDEV) += libndi_newtek_common.h | ||
| 63 | SKIPHEADERS-$(CONFIG_DSHOW_INDEV) += dshow_capture.h | 67 | SKIPHEADERS-$(CONFIG_DSHOW_INDEV) += dshow_capture.h |
| 64 | SKIPHEADERS-$(CONFIG_FBDEV_INDEV) += fbdev_common.h | 68 | SKIPHEADERS-$(CONFIG_FBDEV_INDEV) += fbdev_common.h |
| 65 | SKIPHEADERS-$(CONFIG_FBDEV_OUTDEV) += fbdev_common.h | 69 | SKIPHEADERS-$(CONFIG_FBDEV_OUTDEV) += fbdev_common.h |
| @@ -46,6 +46,7 @@ static void register_all(void) · 45 unmodified lines | |||
| 46 | REGISTER_INDEV (BKTR, bktr); | 46 | REGISTER_INDEV (BKTR, bktr); |
| 47 | REGISTER_OUTDEV (CACA, caca); | 47 | REGISTER_OUTDEV (CACA, caca); |
| 48 | REGISTER_INOUTDEV(DECKLINK, decklink); | 48 | REGISTER_INOUTDEV(DECKLINK, decklink); |
| 49 | REGISTER_INOUTDEV(LIBNDI_NEWTEK, libndi_newtek); | ||
| 49 | REGISTER_INDEV (DSHOW, dshow); | 50 | REGISTER_INDEV (DSHOW, dshow); |
| 50 | REGISTER_INDEV (DV1394, dv1394); | 51 | REGISTER_INDEV (DV1394, dv1394); |
| 51 | REGISTER_INOUTDEV(FBDEV, fbdev); | 52 | REGISTER_INOUTDEV(FBDEV, fbdev); |
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | * NewTek NDI common code | ||
| 3 | * Copyright (c) 2017 Maksym Veremeyenko | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifndef AVDEVICE_LIBNDI_NEWTEK_COMMON_H | ||
| 23 | #define AVDEVICE_LIBNDI_NEWTEK_COMMON_H | ||
| 24 | |||
| 25 | #include <Processing.NDI.Lib.h> | ||
| 26 | |||
| 27 | #define NDI_TIME_BASE 10000000 | ||
| 28 | #define NDI_TIME_BASE_Q (AVRational){1, NDI_TIME_BASE} | ||
| 29 | |||
| 30 | #endif | ||
| @@ -0,0 +1,340 @@ | |||
| 1 | /* | ||
| 2 | * Newtek NDI input | ||
| 3 | * Copyright (c) 2017 Maksym Veremeyenko | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "libavformat/avformat.h" | ||
| 23 | #include "libavformat/internal.h" | ||
| 24 | #include "libavutil/opt.h" | ||
| 25 | #include "libavutil/imgutils.h" | ||
| 26 | |||
| 27 | #include "libndi_newtek_common.h" | ||
| 28 | |||
| 29 | struct NDIContext { | ||
| 30 | const AVClass *cclass; | ||
| 31 | |||
| 32 | /* Options */ | ||
| 33 | int find_sources; | ||
| 34 | int64_t wait_sources; | ||
| 35 | int allow_video_fields; | ||
| 36 | |||
| 37 | /* Runtime */ | ||
| 38 | NDIlib_recv_create_t *recv; | ||
| 39 | NDIlib_find_instance_t ndi_find; | ||
| 40 | |||
| 41 | /* Streams */ | ||
| 42 | AVStream *video_st, *audio_st; | ||
| 43 | }; | ||
| 44 | |||
| 45 | static int ndi_set_video_packet(AVFormatContext *avctx, NDIlib_video_frame_t *v, AVPacket *pkt) | ||
| 46 | { | ||
| 47 | int ret; | ||
| 48 | struct NDIContext *ctx = avctx->priv_data; | ||
| 49 | |||
| 50 | ret = av_new_packet(pkt, v->yres * v->line_stride_in_bytes); | ||
| 51 | if (ret < 0) | ||
| 52 | return ret; | ||
| 53 | |||
| 54 | pkt->dts = pkt->pts = av_rescale_q(v->timecode, NDI_TIME_BASE_Q, ctx->video_st->time_base); | ||
| 55 | pkt->duration = av_rescale_q(1, (AVRational){v->frame_rate_D, v->frame_rate_N}, ctx->video_st->time_base); | ||
| 56 | |||
| 57 | av_log(avctx, AV_LOG_DEBUG, "%s: pkt->dts = pkt->pts = %"PRId64", duration=%"PRId64", timecode=%"PRId64"\n", | ||
| 58 | __func__, pkt->dts, pkt->duration, v->timecode); | ||
| 59 | |||
| 60 | pkt->flags |= AV_PKT_FLAG_KEY; | ||
| 61 | pkt->stream_index = ctx->video_st->index; | ||
| 62 | |||
| 63 | memcpy(pkt->data, v->p_data, pkt->size); | ||
| 64 | |||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | static int ndi_set_audio_packet(AVFormatContext *avctx, NDIlib_audio_frame_t *a, AVPacket *pkt) | ||
| 69 | { | ||
| 70 | int ret; | ||
| 71 | struct NDIContext *ctx = avctx->priv_data; | ||
| 72 | |||
| 73 | NDIlib_audio_frame_interleaved_16s_t dst; | ||
| 74 | |||
| 75 | ret = av_new_packet(pkt, 2 * a->no_samples * a->no_channels); | ||
| 76 | if (ret < 0) | ||
| 77 | return ret; | ||
| 78 | |||
| 79 | pkt->dts = pkt->pts = av_rescale_q(a->timecode, NDI_TIME_BASE_Q, ctx->audio_st->time_base); | ||
| 80 | pkt->duration = av_rescale_q(1, (AVRational){a->no_samples, a->sample_rate}, ctx->audio_st->time_base); | ||
| 81 | |||
| 82 | av_log(avctx, AV_LOG_DEBUG, "%s: pkt->dts = pkt->pts = %"PRId64", duration=%"PRId64", timecode=%"PRId64"\n", | ||
| 83 | __func__, pkt->dts, pkt->duration, a->timecode); | ||
| 84 | |||
| 85 | pkt->flags |= AV_PKT_FLAG_KEY; | ||
| 86 | pkt->stream_index = ctx->audio_st->index; | ||
| 87 | |||
| 88 | dst.reference_level = 0; | ||
| 89 | dst.p_data = (short *)pkt->data; | ||
| 90 | NDIlib_util_audio_to_interleaved_16s(a, &dst); | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int ndi_find_sources(AVFormatContext *avctx, const char *name, NDIlib_source_t *source_to_connect_to) | ||
| 96 | { | ||
| 97 | int j = AVERROR(ENODEV); | ||
| 98 | unsigned int n, i; | ||
| 99 | struct NDIContext *ctx = avctx->priv_data; | ||
| 100 | const NDIlib_source_t *ndi_srcs = NULL; | ||
| 101 | const NDIlib_find_create_t find_create_desc = { .show_local_sources = true, | ||
| 102 | .p_groups = NULL, .p_extra_ips = NULL }; | ||
| 103 | |||
| 104 | if (!ctx->ndi_find) | ||
| 105 | ctx->ndi_find = NDIlib_find_create2(&find_create_desc); | ||
| 106 | if (!ctx->ndi_find) { | ||
| 107 | av_log(avctx, AV_LOG_ERROR, "NDIlib_find_create failed.\n"); | ||
| 108 | return AVERROR(EIO); | ||
| 109 | } | ||
| 110 | |||
| 111 | while (1) | ||
| 112 | { | ||
| 113 | int f, t = ctx->wait_sources / 1000; | ||
| 114 | av_log(avctx, AV_LOG_DEBUG, "Waiting for sources %d miliseconds\n", t); | ||
| 115 | f = NDIlib_find_wait_for_sources(ctx->ndi_find, t); | ||
| 116 | av_log(avctx, AV_LOG_DEBUG, "NDIlib_find_wait_for_sources returns %d\n", f); | ||
| 117 | if (!f) | ||
| 118 | break; | ||
| 119 | }; | ||
| 120 | |||
| 121 | ndi_srcs = NDIlib_find_get_current_sources(ctx->ndi_find, &n); | ||
| 122 | |||
| 123 | if (ctx->find_sources) | ||
| 124 | av_log(avctx, AV_LOG_INFO, "Found %d NDI sources:\n", n); | ||
| 125 | |||
| 126 | for (i = 0; i < n; i++) { | ||
| 127 | if (ctx->find_sources) | ||
| 128 | av_log(avctx, AV_LOG_INFO, "\t'%s'\t'%s'\n", ndi_srcs[i].p_ndi_name, ndi_srcs[i].p_ip_address); | ||
| 129 | |||
| 130 | if (!strcmp(name, ndi_srcs[i].p_ndi_name)) { | ||
| 131 | *source_to_connect_to = ndi_srcs[i]; | ||
| 132 | j = i; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | return j; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int ndi_read_header(AVFormatContext *avctx) | ||
| 140 | { | ||
| 141 | int ret; | ||
| 142 | NDIlib_recv_create_t recv_create_desc; | ||
| 143 | const NDIlib_tally_t tally_state = { .on_program = true, .on_preview = false }; | ||
| 144 | struct NDIContext *ctx = avctx->priv_data; | ||
| 145 | |||
| 146 | if (!NDIlib_initialize()) { | ||
| 147 | av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n"); | ||
| 148 | return AVERROR_EXTERNAL; | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Find available sources. */ | ||
| 152 | ret = ndi_find_sources(avctx, avctx->filename, &recv_create_desc.source_to_connect_to); | ||
| 153 | if (ctx->find_sources) { | ||
| 154 | return AVERROR_EXIT; | ||
| 155 | } | ||
| 156 | if (ret < 0) | ||
| 157 | return ret; | ||
| 158 | |||
| 159 | /* Create receiver description */ | ||
| 160 | recv_create_desc.color_format = NDIlib_recv_color_format_e_UYVY_RGBA; | ||
| 161 | recv_create_desc.bandwidth = NDIlib_recv_bandwidth_highest; | ||
| 162 | recv_create_desc.allow_video_fields = ctx->allow_video_fields; | ||
| 163 | |||
| 164 | /* Create the receiver */ | ||
| 165 | ctx->recv = NDIlib_recv_create(&recv_create_desc); | ||
| 166 | if (!ctx->recv) { | ||
| 167 | av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_create2 failed.\n"); | ||
| 168 | return AVERROR(EIO); | ||
| 169 | } | ||
| 170 | |||
| 171 | /* Set tally */ | ||
| 172 | NDIlib_recv_set_tally(ctx->recv, &tally_state); | ||
| 173 | |||
| 174 | avctx->ctx_flags |= AVFMTCTX_NOHEADER; | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | static int ndi_create_video_stream(AVFormatContext *avctx, NDIlib_video_frame_t *v) | ||
| 180 | { | ||
| 181 | AVStream *st; | ||
| 182 | AVRational tmp; | ||
| 183 | struct NDIContext *ctx = avctx->priv_data; | ||
| 184 | |||
| 185 | st = avformat_new_stream(avctx, NULL); | ||
| 186 | if (!st) { | ||
| 187 | av_log(avctx, AV_LOG_ERROR, "Cannot add video stream\n"); | ||
| 188 | return AVERROR(ENOMEM); | ||
| 189 | } | ||
| 190 | |||
| 191 | st->time_base = NDI_TIME_BASE_Q; | ||
| 192 | av_stream_set_r_frame_rate(st, av_make_q(v->frame_rate_N, v->frame_rate_D)); | ||
| 193 | |||
| 194 | tmp = av_mul_q(av_d2q(v->picture_aspect_ratio, INT_MAX), (AVRational){v->yres, v->xres}); | ||
| 195 | av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, tmp.num, tmp.den, 1000); | ||
| 196 | st->codecpar->sample_aspect_ratio = st->sample_aspect_ratio; | ||
| 197 | |||
| 198 | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; | ||
| 199 | st->codecpar->width = v->xres; | ||
| 200 | st->codecpar->height = v->yres; | ||
| 201 | st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; | ||
| 202 | st->codecpar->bit_rate = av_rescale(v->xres * v->yres * 16, v->frame_rate_N, v->frame_rate_D); | ||
| 203 | st->codecpar->field_order = v->frame_format_type == NDIlib_frame_format_type_progressive | ||
| 204 | ? AV_FIELD_PROGRESSIVE : AV_FIELD_TT; | ||
| 205 | |||
| 206 | if (NDIlib_FourCC_type_UYVY == v->FourCC || NDIlib_FourCC_type_UYVA == v->FourCC) { | ||
| 207 | st->codecpar->format = AV_PIX_FMT_UYVY422; | ||
| 208 | st->codecpar->codec_tag = MKTAG('U', 'Y', 'V', 'Y'); | ||
| 209 | if (NDIlib_FourCC_type_UYVA == v->FourCC) | ||
| 210 | av_log(avctx, AV_LOG_WARNING, "Alpha channel ignored\n"); | ||
| 211 | } else if (NDIlib_FourCC_type_BGRA == v->FourCC) { | ||
| 212 | st->codecpar->format = AV_PIX_FMT_BGRA; | ||
| 213 | st->codecpar->codec_tag = MKTAG('B', 'G', 'R', 'A'); | ||
| 214 | } else if (NDIlib_FourCC_type_BGRX == v->FourCC) { | ||
| 215 | st->codecpar->format = AV_PIX_FMT_BGR0; | ||
| 216 | st->codecpar->codec_tag = MKTAG('B', 'G', 'R', '0'); | ||
| 217 | } else if (NDIlib_FourCC_type_RGBA == v->FourCC) { | ||
| 218 | st->codecpar->format = AV_PIX_FMT_RGBA; | ||
| 219 | st->codecpar->codec_tag = MKTAG('R', 'G', 'B', 'A'); | ||
| 220 | } else if (NDIlib_FourCC_type_RGBX == v->FourCC) { | ||
| 221 | st->codecpar->format = AV_PIX_FMT_RGB0; | ||
| 222 | st->codecpar->codec_tag = MKTAG('R', 'G', 'B', '0'); | ||
| 223 | } else { | ||
| 224 | av_log(avctx, AV_LOG_ERROR, "Unsupported video stream format, v->FourCC=%d\n", v->FourCC); | ||
| 225 | return AVERROR(EINVAL); | ||
| 226 | } | ||
| 227 | |||
| 228 | avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); | ||
| 229 | |||
| 230 | ctx->video_st = st; | ||
| 231 | |||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | static int ndi_create_audio_stream(AVFormatContext *avctx, NDIlib_audio_frame_t *a) | ||
| 236 | { | ||
| 237 | AVStream *st; | ||
| 238 | struct NDIContext *ctx = avctx->priv_data; | ||
| 239 | |||
| 240 | st = avformat_new_stream(avctx, NULL); | ||
| 241 | if (!st) { | ||
| 242 | av_log(avctx, AV_LOG_ERROR, "Cannot add audio stream\n"); | ||
| 243 | return AVERROR(ENOMEM); | ||
| 244 | } | ||
| 245 | |||
| 246 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | ||
| 247 | st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; | ||
| 248 | st->codecpar->sample_rate = a->sample_rate; | ||
| 249 | st->codecpar->channels = a->no_channels; | ||
| 250 | |||
| 251 | avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); | ||
| 252 | |||
| 253 | ctx->audio_st = st; | ||
| 254 | |||
| 255 | return 0; | ||
| 256 | } | ||
| 257 | |||
| 258 | static int ndi_read_packet(AVFormatContext *avctx, AVPacket *pkt) | ||
| 259 | { | ||
| 260 | int ret = 0; | ||
| 261 | struct NDIContext *ctx = avctx->priv_data; | ||
| 262 | |||
| 263 | while (!ret) { | ||
| 264 | NDIlib_video_frame_t v; | ||
| 265 | NDIlib_audio_frame_t a; | ||
| 266 | NDIlib_metadata_frame_t m; | ||
| 267 | NDIlib_frame_type_e t; | ||
| 268 | |||
| 269 | av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture...\n"); | ||
| 270 | t = NDIlib_recv_capture(ctx->recv, &v, &a, &m, 40); | ||
| 271 | av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture=%d\n", t); | ||
| 272 | |||
| 273 | if (t == NDIlib_frame_type_video) { | ||
| 274 | if (!ctx->video_st) | ||
| 275 | ret = ndi_create_video_stream(avctx, &v); | ||
| 276 | if (!ret) | ||
| 277 | ret = ndi_set_video_packet(avctx, &v, pkt); | ||
| 278 | NDIlib_recv_free_video(ctx->recv, &v); | ||
| 279 | break; | ||
| 280 | } | ||
| 281 | else if (t == NDIlib_frame_type_audio) { | ||
| 282 | if (!ctx->audio_st) | ||
| 283 | ret = ndi_create_audio_stream(avctx, &a); | ||
| 284 | if (!ret) | ||
| 285 | ret = ndi_set_audio_packet(avctx, &a, pkt); | ||
| 286 | NDIlib_recv_free_audio(ctx->recv, &a); | ||
| 287 | break; | ||
| 288 | } | ||
| 289 | else if (t == NDIlib_frame_type_metadata) | ||
| 290 | NDIlib_recv_free_metadata(ctx->recv, &m); | ||
| 291 | else if (t == NDIlib_frame_type_error){ | ||
| 292 | av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_capture failed with error\n"); | ||
| 293 | ret = AVERROR(EIO); | ||
| 294 | } | ||
| 295 | }; | ||
| 296 | |||
| 297 | return ret; | ||
| 298 | } | ||
| 299 | |||
| 300 | static int ndi_read_close(AVFormatContext *avctx) | ||
| 301 | { | ||
| 302 | struct NDIContext *ctx = (struct NDIContext *)avctx->priv_data; | ||
| 303 | |||
| 304 | if (ctx->recv) | ||
| 305 | NDIlib_recv_destroy(ctx->recv); | ||
| 306 | |||
| 307 | if (ctx->ndi_find) | ||
| 308 | NDIlib_find_destroy(ctx->ndi_find); | ||
| 309 | |||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | #define OFFSET(x) offsetof(struct NDIContext, x) | ||
| 314 | #define DEC AV_OPT_FLAG_DECODING_PARAM | ||
| 315 | |||
| 316 | static const AVOption options[] = { | ||
| 317 | { "find_sources", "Find available sources" , OFFSET(find_sources), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, | ||
| 318 | { "wait_sources", "Time to wait until the number of online sources have changed" , OFFSET(wait_sources), AV_OPT_TYPE_DURATION, { .i64 = 1000000 }, 100000, 20000000, DEC }, | ||
| 319 | { "allow_video_fields", "When this flag is FALSE, all video that you receive will be progressive" , OFFSET(allow_video_fields), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, DEC }, | ||
| 320 | { NULL }, | ||
| 321 | }; | ||
| 322 | |||
| 323 | static const AVClass libndi_newtek_demuxer_class = { | ||
| 324 | .class_name = "NDI demuxer", | ||
| 325 | .item_name = av_default_item_name, | ||
| 326 | .option = options, | ||
| 327 | .version = LIBAVUTIL_VERSION_INT, | ||
| 328 | .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, | ||
| 329 | }; | ||
| 330 | |||
| 331 | AVInputFormat ff_libndi_newtek_demuxer = { | ||
| 332 | .name = "libndi_newtek", | ||
| 333 | .long_name = NULL_IF_CONFIG_SMALL("Network Device Interface (NDI) input using NewTek library"), | ||
| 334 | .flags = AVFMT_NOFILE, | ||
| 335 | .priv_class = &libndi_newtek_demuxer_class, | ||
| 336 | .priv_data_size = sizeof(struct NDIContext), | ||
| 337 | .read_header = ndi_read_header, | ||
| 338 | .read_packet = ndi_read_packet, | ||
| 339 | .read_close = ndi_read_close, | ||
| 340 | }; | ||
| @@ -0,0 +1,299 @@ | |||
| 1 | /* | ||
| 2 | * NewTek NDI output | ||
| 3 | * Copyright (c) 2017 Maksym Veremeyenko | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "libavformat/avformat.h" | ||
| 23 | #include "libavformat/internal.h" | ||
| 24 | #include "libavutil/opt.h" | ||
| 25 | #include "libavutil/imgutils.h" | ||
| 26 | |||
| 27 | #include "libndi_newtek_common.h" | ||
| 28 | |||
| 29 | struct NDIContext { | ||
| 30 | const AVClass *cclass; | ||
| 31 | |||
| 32 | /* Options */ | ||
| 33 | int reference_level; | ||
| 34 | int clock_video, clock_audio; | ||
| 35 | |||
| 36 | NDIlib_video_frame_t *video; | ||
| 37 | NDIlib_audio_frame_interleaved_16s_t *audio; | ||
| 38 | NDIlib_send_instance_t ndi_send; | ||
| 39 | AVFrame *last_avframe; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static int ndi_write_trailer(AVFormatContext *avctx) | ||
| 43 | { | ||
| 44 | struct NDIContext *ctx = avctx->priv_data; | ||
| 45 | |||
| 46 | if (ctx->ndi_send) { | ||
| 47 | NDIlib_send_destroy(ctx->ndi_send); | ||
| 48 | av_frame_free(&ctx->last_avframe); | ||
| 49 | } | ||
| 50 | |||
| 51 | av_freep(&ctx->video); | ||
| 52 | av_freep(&ctx->audio); | ||
| 53 | |||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | |||
| 57 | static int ndi_write_video_packet(AVFormatContext *avctx, AVStream *st, AVPacket *pkt) | ||
| 58 | { | ||
| 59 | struct NDIContext *ctx = avctx->priv_data; | ||
| 60 | AVFrame *avframe, *tmp = (AVFrame *)pkt->data; | ||
| 61 | |||
| 62 | if (tmp->format != AV_PIX_FMT_UYVY422 && tmp->format != AV_PIX_FMT_BGRA && | ||
| 63 | tmp->format != AV_PIX_FMT_BGR0 && tmp->format != AV_PIX_FMT_RGBA && | ||
| 64 | tmp->format != AV_PIX_FMT_RGB0) { | ||
| 65 | av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid pixel format.\n"); | ||
| 66 | return AVERROR(EINVAL); | ||
| 67 | } | ||
| 68 | |||
| 69 | if (tmp->linesize[0] < 0) { | ||
| 70 | av_log(avctx, AV_LOG_ERROR, "Got a frame with negative linesize.\n"); | ||
| 71 | return AVERROR(EINVAL); | ||
| 72 | } | ||
| 73 | |||
| 74 | if (tmp->width != ctx->video->xres || | ||
| 75 | tmp->height != ctx->video->yres) { | ||
| 76 | av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid dimension.\n"); | ||
| 77 | av_log(avctx, AV_LOG_ERROR, "tmp->width=%d, tmp->height=%d, ctx->video->xres=%d, ctx->video->yres=%d\n", | ||
| 78 | tmp->width, tmp->height, ctx->video->xres, ctx->video->yres); | ||
| 79 | return AVERROR(EINVAL); | ||
| 80 | } | ||
| 81 | |||
| 82 | avframe = av_frame_clone(tmp); | ||
| 83 | if (!avframe) | ||
| 84 | return AVERROR(ENOMEM); | ||
| 85 | |||
| 86 | ctx->video->timecode = av_rescale_q(pkt->pts, st->time_base, NDI_TIME_BASE_Q); | ||
| 87 | |||
| 88 | ctx->video->line_stride_in_bytes = avframe->linesize[0]; | ||
| 89 | ctx->video->p_data = (void *)(avframe->data[0]); | ||
| 90 | |||
| 91 | av_log(avctx, AV_LOG_DEBUG, "%s: pkt->pts=%"PRId64", timecode=%"PRId64", st->time_base=%d/%d\n", | ||
| 92 | __func__, pkt->pts, ctx->video->timecode, st->time_base.num, st->time_base.den); | ||
| 93 | |||
| 94 | /* asynchronous for one frame, but will block if a second frame | ||
| 95 | is given before the first one has been sent */ | ||
| 96 | NDIlib_send_send_video_async(ctx->ndi_send, ctx->video); | ||
| 97 | |||
| 98 | av_frame_free(&ctx->last_avframe); | ||
| 99 | ctx->last_avframe = avframe; | ||
| 100 | |||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | static int ndi_write_audio_packet(AVFormatContext *avctx, AVStream *st, AVPacket *pkt) | ||
| 105 | { | ||
| 106 | struct NDIContext *ctx = avctx->priv_data; | ||
| 107 | |||
| 108 | ctx->audio->p_data = (short *)pkt->data; | ||
| 109 | ctx->audio->timecode = av_rescale_q(pkt->pts, st->time_base, NDI_TIME_BASE_Q); | ||
| 110 | ctx->audio->no_samples = pkt->size / (ctx->audio->no_channels << 1); | ||
| 111 | |||
| 112 | av_log(avctx, AV_LOG_DEBUG, "%s: pkt->pts=%"PRId64", timecode=%"PRId64", st->time_base=%d/%d\n", | ||
| 113 | __func__, pkt->pts, ctx->audio->timecode, st->time_base.num, st->time_base.den); | ||
| 114 | |||
| 115 | NDIlib_util_send_send_audio_interleaved_16s(ctx->ndi_send, ctx->audio); | ||
| 116 | |||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | static int ndi_write_packet(AVFormatContext *avctx, AVPacket *pkt) | ||
| 121 | { | ||
| 122 | AVStream *st = avctx->streams[pkt->stream_index]; | ||
| 123 | |||
| 124 | if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) | ||
| 125 | return ndi_write_video_packet(avctx, st, pkt); | ||
| 126 | else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) | ||
| 127 | return ndi_write_audio_packet(avctx, st, pkt); | ||
| 128 | |||
| 129 | return AVERROR_BUG; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int ndi_setup_audio(AVFormatContext *avctx, AVStream *st) | ||
| 133 | { | ||
| 134 | struct NDIContext *ctx = avctx->priv_data; | ||
| 135 | AVCodecParameters *c = st->codecpar; | ||
| 136 | |||
| 137 | if (ctx->audio) { | ||
| 138 | av_log(avctx, AV_LOG_ERROR, "Only one audio stream is supported!\n"); | ||
| 139 | return AVERROR(EINVAL); | ||
| 140 | } | ||
| 141 | |||
| 142 | ctx->audio = av_mallocz(sizeof(NDIlib_audio_frame_interleaved_16s_t)); | ||
| 143 | if (!ctx->audio) | ||
| 144 | return AVERROR(ENOMEM); | ||
| 145 | |||
| 146 | ctx->audio->sample_rate = c->sample_rate; | ||
| 147 | ctx->audio->no_channels = c->channels; | ||
| 148 | ctx->audio->reference_level = ctx->reference_level; | ||
| 149 | |||
| 150 | avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); | ||
| 151 | |||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static int ndi_setup_video(AVFormatContext *avctx, AVStream *st) | ||
| 156 | { | ||
| 157 | struct NDIContext *ctx = avctx->priv_data; | ||
| 158 | AVCodecParameters *c = st->codecpar; | ||
| 159 | |||
| 160 | if (ctx->video) { | ||
| 161 | av_log(avctx, AV_LOG_ERROR, "Only one video stream is supported!\n"); | ||
| 162 | return AVERROR(EINVAL); | ||
| 163 | } | ||
| 164 | |||
| 165 | if (c->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME) { | ||
| 166 | av_log(avctx, AV_LOG_ERROR, "Unsupported codec format!" | ||
| 167 | " Only AV_CODEC_ID_WRAPPED_AVFRAME is supported (-vcodec wrapped_avframe).\n"); | ||
| 168 | return AVERROR(EINVAL); | ||
| 169 | } | ||
| 170 | |||
| 171 | if (c->format != AV_PIX_FMT_UYVY422 && c->format != AV_PIX_FMT_BGRA && | ||
| 172 | c->format != AV_PIX_FMT_BGR0 && c->format != AV_PIX_FMT_RGBA && | ||
| 173 | c->format != AV_PIX_FMT_RGB0) { | ||
| 174 | av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format!" | ||
| 175 | " Only AV_PIX_FMT_UYVY422, AV_PIX_FMT_BGRA, AV_PIX_FMT_BGR0," | ||
| 176 | " AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB0 is supported.\n"); | ||
| 177 | return AVERROR(EINVAL); | ||
| 178 | } | ||
| 179 | |||
| 180 | if (c->field_order == AV_FIELD_BB || c->field_order == AV_FIELD_BT) { | ||
| 181 | av_log(avctx, AV_LOG_ERROR, "Lower field-first disallowed"); | ||
| 182 | return AVERROR(EINVAL); | ||
| 183 | } | ||
| 184 | |||
| 185 | ctx->video = av_mallocz(sizeof(NDIlib_video_frame_t)); | ||
| 186 | if (!ctx->video) | ||
| 187 | return AVERROR(ENOMEM); | ||
| 188 | |||
| 189 | switch(c->format) { | ||
| 190 | case AV_PIX_FMT_UYVY422: | ||
| 191 | ctx->video->FourCC = NDIlib_FourCC_type_UYVY; | ||
| 192 | break; | ||
| 193 | case AV_PIX_FMT_BGRA: | ||
| 194 | ctx->video->FourCC = NDIlib_FourCC_type_BGRA; | ||
| 195 | break; | ||
| 196 | case AV_PIX_FMT_BGR0: | ||
| 197 | ctx->video->FourCC = NDIlib_FourCC_type_BGRX; | ||
| 198 | break; | ||
| 199 | case AV_PIX_FMT_RGBA: | ||
| 200 | ctx->video->FourCC = NDIlib_FourCC_type_RGBA; | ||
| 201 | break; | ||
| 202 | case AV_PIX_FMT_RGB0: | ||
| 203 | ctx->video->FourCC = NDIlib_FourCC_type_RGBX; | ||
| 204 | break; | ||
| 205 | } | ||
| 206 | |||
| 207 | ctx->video->xres = c->width; | ||
| 208 | ctx->video->yres = c->height; | ||
| 209 | ctx->video->frame_rate_N = st->avg_frame_rate.num; | ||
| 210 | ctx->video->frame_rate_D = st->avg_frame_rate.den; | ||
| 211 | ctx->video->frame_format_type = c->field_order == AV_FIELD_PROGRESSIVE | ||
| 212 | ? NDIlib_frame_format_type_progressive | ||
| 213 | : NDIlib_frame_format_type_interleaved; | ||
| 214 | |||
| 215 | if (st->sample_aspect_ratio.num) { | ||
| 216 | AVRational display_aspect_ratio; | ||
| 217 | av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, | ||
| 218 | st->codecpar->width * (int64_t)st->sample_aspect_ratio.num, | ||
| 219 | st->codecpar->height * (int64_t)st->sample_aspect_ratio.den, | ||
| 220 | 1024 * 1024); | ||
| 221 | ctx->video->picture_aspect_ratio = av_q2d(display_aspect_ratio); | ||
| 222 | } | ||
| 223 | else | ||
| 224 | ctx->video->picture_aspect_ratio = (double)st->codecpar->width/st->codecpar->height; | ||
| 225 | |||
| 226 | avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); | ||
| 227 | |||
| 228 | return 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | static int ndi_write_header(AVFormatContext *avctx) | ||
| 232 | { | ||
| 233 | int ret = 0; | ||
| 234 | unsigned int n; | ||
| 235 | struct NDIContext *ctx = avctx->priv_data; | ||
| 236 | const NDIlib_send_create_t ndi_send_desc = { .p_ndi_name = avctx->filename, | ||
| 237 | .p_groups = NULL, .clock_video = ctx->clock_video, .clock_audio = ctx->clock_audio }; | ||
| 238 | |||
| 239 | if (!NDIlib_initialize()) { | ||
| 240 | av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n"); | ||
| 241 | return AVERROR_EXTERNAL; | ||
| 242 | } | ||
| 243 | |||
| 244 | /* check if streams compatible */ | ||
| 245 | for (n = 0; n < avctx->nb_streams; n++) { | ||
| 246 | AVStream *st = avctx->streams[n]; | ||
| 247 | AVCodecParameters *c = st->codecpar; | ||
| 248 | if (c->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 249 | if ((ret = ndi_setup_audio(avctx, st))) | ||
| 250 | goto error; | ||
| 251 | } else if (c->codec_type == AVMEDIA_TYPE_VIDEO) { | ||
| 252 | if ((ret = ndi_setup_video(avctx, st))) | ||
| 253 | goto error; | ||
| 254 | } else { | ||
| 255 | av_log(avctx, AV_LOG_ERROR, "Unsupported stream type.\n"); | ||
| 256 | ret = AVERROR(EINVAL); | ||
| 257 | goto error; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | ctx->ndi_send = NDIlib_send_create(&ndi_send_desc); | ||
| 262 | if (!ctx->ndi_send) { | ||
| 263 | av_log(avctx, AV_LOG_ERROR, "Failed to create NDI output %s\n", avctx->filename); | ||
| 264 | ret = AVERROR_EXTERNAL; | ||
| 265 | } | ||
| 266 | |||
| 267 | error: | ||
| 268 | return ret; | ||
| 269 | } | ||
| 270 | |||
| 271 | #define OFFSET(x) offsetof(struct NDIContext, x) | ||
| 272 | static const AVOption options[] = { | ||
| 273 | { "reference_level", "The audio reference level in dB" , OFFSET(reference_level), AV_OPT_TYPE_INT, { .i64 = 0 }, -20, 20, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM}, | ||
| 274 | { "clock_video", "These specify whether video 'clock' themselves" , OFFSET(clock_video), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM }, | ||
| 275 | { "clock_audio", "These specify whether audio 'clock' themselves" , OFFSET(clock_audio), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM }, | ||
| 276 | { NULL }, | ||
| 277 | }; | ||
| 278 | |||
| 279 | static const AVClass libndi_newtek_muxer_class = { | ||
| 280 | .class_name = "NDI muxer", | ||
| 281 | .item_name = av_default_item_name, | ||
| 282 | .option = options, | ||
| 283 | .version = LIBAVUTIL_VERSION_INT, | ||
| 284 | .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, | ||
| 285 | }; | ||
| 286 | |||
| 287 | AVOutputFormat ff_libndi_newtek_muxer = { | ||
| 288 | .name = "libndi_newtek", | ||
| 289 | .long_name = NULL_IF_CONFIG_SMALL("Network Device Interface (NDI) output using NewTek library"), | ||
| 290 | .audio_codec = AV_CODEC_ID_PCM_S16LE, | ||
| 291 | .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, | ||
| 292 | .subtitle_codec = AV_CODEC_ID_NONE, | ||
| 293 | .flags = AVFMT_NOFILE, | ||
| 294 | .priv_class = &libndi_newtek_muxer_class, | ||
| 295 | .priv_data_size = sizeof(struct NDIContext), | ||
| 296 | .write_header = ndi_write_header, | ||
| 297 | .write_packet = ndi_write_packet, | ||
| 298 | .write_trailer = ndi_write_trailer, | ||
| 299 | }; | ||
| @@ -28,8 +28,8 @@ · 27 unmodified lines | |||
| 28 | #include "libavutil/version.h" | 28 | #include "libavutil/version.h" |
| 29 | 29 | ||
| 30 | #define LIBAVDEVICE_VERSION_MAJOR 57 | 30 | #define LIBAVDEVICE_VERSION_MAJOR 57 |
| 31 | #define LIBAVDEVICE_VERSION_MINOR 7 | 31 | #define LIBAVDEVICE_VERSION_MINOR 8 |
| 32 | #define LIBAVDEVICE_VERSION_MICRO 101 | 32 | #define LIBAVDEVICE_VERSION_MICRO 100 |
| 33 | 33 | ||
| 34 | #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ | 34 | #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ |
| 35 | LIBAVDEVICE_VERSION_MINOR, \ | 35 | LIBAVDEVICE_VERSION_MINOR, \ |