libndi_newtek_dec.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. #include "libavformat/avformat.h"
  22. #include "libavformat/internal.h"
  23. #include "libavutil/opt.h"
  24. #include "libavutil/imgutils.h"
  25. #include "libndi_newtek_common.h"
  26. struct NDIContext {
  27. const AVClass *cclass;
  28. /* Options */
  29. int find_sources;
  30. int64_t wait_sources;
  31. int allow_video_fields;
  32. char *extra_ips;
  33. /* Runtime */
  34. NDIlib_recv_create_t *recv;
  35. NDIlib_find_instance_t ndi_find;
  36. /* Streams */
  37. AVStream *video_st, *audio_st;
  38. };
  39. static int ndi_set_video_packet(AVFormatContext *avctx, NDIlib_video_frame_t *v, AVPacket *pkt)
  40. {
  41. int ret;
  42. struct NDIContext *ctx = avctx->priv_data;
  43. ret = av_new_packet(pkt, v->yres * v->line_stride_in_bytes);
  44. if (ret < 0)
  45. return ret;
  46. pkt->dts = pkt->pts = av_rescale_q(v->timecode, NDI_TIME_BASE_Q, ctx->video_st->time_base);
  47. pkt->duration = av_rescale_q(1, (AVRational){v->frame_rate_D, v->frame_rate_N}, ctx->video_st->time_base);
  48. av_log(avctx, AV_LOG_DEBUG, "%s: pkt->dts = pkt->pts = %"PRId64", duration=%"PRId64", timecode=%"PRId64"\n",
  49. __func__, pkt->dts, pkt->duration, v->timecode);
  50. pkt->flags |= AV_PKT_FLAG_KEY;
  51. pkt->stream_index = ctx->video_st->index;
  52. memcpy(pkt->data, v->p_data, pkt->size);
  53. return 0;
  54. }
  55. static int ndi_set_audio_packet(AVFormatContext *avctx, NDIlib_audio_frame_t *a, AVPacket *pkt)
  56. {
  57. int ret;
  58. struct NDIContext *ctx = avctx->priv_data;
  59. NDIlib_audio_frame_interleaved_16s_t dst;
  60. ret = av_new_packet(pkt, 2 * a->no_samples * a->no_channels);
  61. if (ret < 0)
  62. return ret;
  63. pkt->dts = pkt->pts = av_rescale_q(a->timecode, NDI_TIME_BASE_Q, ctx->audio_st->time_base);
  64. pkt->duration = av_rescale_q(1, (AVRational){a->no_samples, a->sample_rate}, ctx->audio_st->time_base);
  65. av_log(avctx, AV_LOG_DEBUG, "%s: pkt->dts = pkt->pts = %"PRId64", duration=%"PRId64", timecode=%"PRId64"\n",
  66. __func__, pkt->dts, pkt->duration, a->timecode);
  67. pkt->flags |= AV_PKT_FLAG_KEY;
  68. pkt->stream_index = ctx->audio_st->index;
  69. dst.reference_level = 0;
  70. dst.p_data = (short *)pkt->data;
  71. NDIlib_util_audio_to_interleaved_16s(a, &dst);
  72. return 0;
  73. }
  74. static int ndi_find_sources(AVFormatContext *avctx, const char *name, NDIlib_source_t *source_to_connect_to)
  75. {
  76. int j = AVERROR(ENODEV);
  77. unsigned int n, i;
  78. struct NDIContext *ctx = avctx->priv_data;
  79. const NDIlib_source_t *ndi_srcs = NULL;
  80. const NDIlib_find_create_t find_create_desc = { .show_local_sources = true,
  81. .p_groups = NULL, .p_extra_ips = ctx->extra_ips };
  82. if (!ctx->ndi_find)
  83. ctx->ndi_find = NDIlib_find_create2(&find_create_desc);
  84. if (!ctx->ndi_find) {
  85. av_log(avctx, AV_LOG_ERROR, "NDIlib_find_create failed.\n");
  86. return AVERROR(EIO);
  87. }
  88. while (1)
  89. {
  90. int f, t = ctx->wait_sources / 1000;
  91. av_log(avctx, AV_LOG_DEBUG, "Waiting for sources %d miliseconds\n", t);
  92. f = NDIlib_find_wait_for_sources(ctx->ndi_find, t);
  93. av_log(avctx, AV_LOG_DEBUG, "NDIlib_find_wait_for_sources returns %d\n", f);
  94. if (!f)
  95. break;
  96. };
  97. ndi_srcs = NDIlib_find_get_current_sources(ctx->ndi_find, &n);
  98. if (ctx->find_sources)
  99. av_log(avctx, AV_LOG_INFO, "Found %d NDI sources:\n", n);
  100. for (i = 0; i < n; i++) {
  101. if (ctx->find_sources)
  102. av_log(avctx, AV_LOG_INFO, "\t'%s'\t'%s'\n", ndi_srcs[i].p_ndi_name, ndi_srcs[i].p_ip_address);
  103. if (!strcmp(name, ndi_srcs[i].p_ndi_name)) {
  104. *source_to_connect_to = ndi_srcs[i];
  105. j = i;
  106. }
  107. }
  108. return j;
  109. }
  110. static int ndi_read_header(AVFormatContext *avctx)
  111. {
  112. int ret;
  113. NDIlib_recv_create_t recv_create_desc;
  114. const NDIlib_tally_t tally_state = { .on_program = true, .on_preview = false };
  115. struct NDIContext *ctx = avctx->priv_data;
  116. if (!NDIlib_initialize()) {
  117. av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n");
  118. return AVERROR_EXTERNAL;
  119. }
  120. /* Find available sources. */
  121. ret = ndi_find_sources(avctx, avctx->url, &recv_create_desc.source_to_connect_to);
  122. if (ctx->find_sources) {
  123. return AVERROR_EXIT;
  124. }
  125. if (ret < 0)
  126. return ret;
  127. /* Create receiver description */
  128. recv_create_desc.color_format = NDIlib_recv_color_format_e_UYVY_RGBA;
  129. recv_create_desc.bandwidth = NDIlib_recv_bandwidth_highest;
  130. recv_create_desc.allow_video_fields = ctx->allow_video_fields;
  131. /* Create the receiver */
  132. ctx->recv = NDIlib_recv_create(&recv_create_desc);
  133. if (!ctx->recv) {
  134. av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_create2 failed.\n");
  135. return AVERROR(EIO);
  136. }
  137. /* Set tally */
  138. NDIlib_recv_set_tally(ctx->recv, &tally_state);
  139. avctx->ctx_flags |= AVFMTCTX_NOHEADER;
  140. return 0;
  141. }
  142. static int ndi_create_video_stream(AVFormatContext *avctx, NDIlib_video_frame_t *v)
  143. {
  144. AVStream *st;
  145. AVRational tmp;
  146. struct NDIContext *ctx = avctx->priv_data;
  147. st = avformat_new_stream(avctx, NULL);
  148. if (!st) {
  149. av_log(avctx, AV_LOG_ERROR, "Cannot add video stream\n");
  150. return AVERROR(ENOMEM);
  151. }
  152. st->time_base = NDI_TIME_BASE_Q;
  153. st->r_frame_rate = av_make_q(v->frame_rate_N, v->frame_rate_D);
  154. tmp = av_mul_q(av_d2q(v->picture_aspect_ratio, INT_MAX), (AVRational){v->yres, v->xres});
  155. av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, tmp.num, tmp.den, 1000);
  156. st->codecpar->sample_aspect_ratio = st->sample_aspect_ratio;
  157. st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
  158. st->codecpar->width = v->xres;
  159. st->codecpar->height = v->yres;
  160. st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
  161. st->codecpar->bit_rate = av_rescale(v->xres * v->yres * 16, v->frame_rate_N, v->frame_rate_D);
  162. st->codecpar->field_order = v->frame_format_type == NDIlib_frame_format_type_progressive
  163. ? AV_FIELD_PROGRESSIVE : AV_FIELD_TT;
  164. if (NDIlib_FourCC_type_UYVY == v->FourCC || NDIlib_FourCC_type_UYVA == v->FourCC) {
  165. st->codecpar->format = AV_PIX_FMT_UYVY422;
  166. st->codecpar->codec_tag = MKTAG('U', 'Y', 'V', 'Y');
  167. if (NDIlib_FourCC_type_UYVA == v->FourCC)
  168. av_log(avctx, AV_LOG_WARNING, "Alpha channel ignored\n");
  169. } else if (NDIlib_FourCC_type_BGRA == v->FourCC) {
  170. st->codecpar->format = AV_PIX_FMT_BGRA;
  171. st->codecpar->codec_tag = MKTAG('B', 'G', 'R', 'A');
  172. } else if (NDIlib_FourCC_type_BGRX == v->FourCC) {
  173. st->codecpar->format = AV_PIX_FMT_BGR0;
  174. st->codecpar->codec_tag = MKTAG('B', 'G', 'R', '0');
  175. } else if (NDIlib_FourCC_type_RGBA == v->FourCC) {
  176. st->codecpar->format = AV_PIX_FMT_RGBA;
  177. st->codecpar->codec_tag = MKTAG('R', 'G', 'B', 'A');
  178. } else if (NDIlib_FourCC_type_RGBX == v->FourCC) {
  179. st->codecpar->format = AV_PIX_FMT_RGB0;
  180. st->codecpar->codec_tag = MKTAG('R', 'G', 'B', '0');
  181. } else {
  182. av_log(avctx, AV_LOG_ERROR, "Unsupported video stream format, v->FourCC=%d\n", v->FourCC);
  183. return AVERROR(EINVAL);
  184. }
  185. avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE);
  186. ctx->video_st = st;
  187. return 0;
  188. }
  189. static int ndi_create_audio_stream(AVFormatContext *avctx, NDIlib_audio_frame_t *a)
  190. {
  191. AVStream *st;
  192. struct NDIContext *ctx = avctx->priv_data;
  193. st = avformat_new_stream(avctx, NULL);
  194. if (!st) {
  195. av_log(avctx, AV_LOG_ERROR, "Cannot add audio stream\n");
  196. return AVERROR(ENOMEM);
  197. }
  198. st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
  199. st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
  200. st->codecpar->sample_rate = a->sample_rate;
  201. st->codecpar->channels = a->no_channels;
  202. avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE);
  203. ctx->audio_st = st;
  204. return 0;
  205. }
  206. static int ndi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
  207. {
  208. int ret = 0;
  209. struct NDIContext *ctx = avctx->priv_data;
  210. while (!ret) {
  211. NDIlib_video_frame_t v;
  212. NDIlib_audio_frame_t a;
  213. NDIlib_metadata_frame_t m;
  214. NDIlib_frame_type_e t;
  215. av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture...\n");
  216. t = NDIlib_recv_capture(ctx->recv, &v, &a, &m, 40);
  217. av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture=%d\n", t);
  218. if (t == NDIlib_frame_type_video) {
  219. if (!ctx->video_st)
  220. ret = ndi_create_video_stream(avctx, &v);
  221. if (!ret)
  222. ret = ndi_set_video_packet(avctx, &v, pkt);
  223. NDIlib_recv_free_video(ctx->recv, &v);
  224. break;
  225. }
  226. else if (t == NDIlib_frame_type_audio) {
  227. if (!ctx->audio_st)
  228. ret = ndi_create_audio_stream(avctx, &a);
  229. if (!ret)
  230. ret = ndi_set_audio_packet(avctx, &a, pkt);
  231. NDIlib_recv_free_audio(ctx->recv, &a);
  232. break;
  233. }
  234. else if (t == NDIlib_frame_type_metadata)
  235. NDIlib_recv_free_metadata(ctx->recv, &m);
  236. else if (t == NDIlib_frame_type_error){
  237. av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_capture failed with error\n");
  238. ret = AVERROR(EIO);
  239. }
  240. };
  241. return ret;
  242. }
  243. static int ndi_read_close(AVFormatContext *avctx)
  244. {
  245. struct NDIContext *ctx = (struct NDIContext *)avctx->priv_data;
  246. if (ctx->recv)
  247. NDIlib_recv_destroy(ctx->recv);
  248. if (ctx->ndi_find)
  249. NDIlib_find_destroy(ctx->ndi_find);
  250. return 0;
  251. }
  252. #define OFFSET(x) offsetof(struct NDIContext, x)
  253. #define DEC AV_OPT_FLAG_DECODING_PARAM
  254. static const AVOption options[] = {
  255. { "find_sources", "Find available sources" , OFFSET(find_sources), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC },
  256. { "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 },
  257. { "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 },
  258. { "extra_ips", "List of comma separated ip addresses to scan for remote sources", OFFSET(extra_ips), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC },
  259. { NULL },
  260. };
  261. static const AVClass libndi_newtek_demuxer_class = {
  262. .class_name = "NDI demuxer",
  263. .item_name = av_default_item_name,
  264. .option = options,
  265. .version = LIBAVUTIL_VERSION_INT,
  266. .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
  267. };
  268. AVInputFormat ff_libndi_newtek_demuxer = {
  269. .name = "libndi_newtek",
  270. .long_name = NULL_IF_CONFIG_SMALL("Network Device Interface (NDI) input using NewTek library"),
  271. .flags = AVFMT_NOFILE,
  272. .priv_class = &libndi_newtek_demuxer_class,
  273. .priv_data_size = sizeof(struct NDIContext),
  274. .read_header = ndi_read_header,
  275. .read_packet = ndi_read_packet,
  276. .read_close = ndi_read_close,
  277. };