FFmpeg  4.0
libndi_newtek_enc.c
Go to the documentation of this file.
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 */
35 
36  NDIlib_video_frame_t *video;
37  NDIlib_audio_frame_interleaved_16s_t *audio;
38  NDIlib_send_instance_t ndi_send;
40 };
41 
43 {
44  struct NDIContext *ctx = avctx->priv_data;
45 
46  if (ctx->ndi_send) {
47  NDIlib_send_destroy(ctx->ndi_send);
49  }
50 
51  av_freep(&ctx->video);
52  av_freep(&ctx->audio);
53 
54  return 0;
55 }
56 
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 
99  ctx->last_avframe = avframe;
100 
101  return 0;
102 }
103 
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 
121 {
122  AVStream *st = avctx->streams[pkt->stream_index];
123 
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;
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 
151 
152  return 0;
153 }
154 
155 static int ndi_setup_video(AVFormatContext *avctx, AVStream *st)
156 {
157  struct NDIContext *ctx = avctx->priv_data;
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 
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 
227 
228  return 0;
229 }
230 
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->url,
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];
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->url);
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 
280  .class_name = "NDI muxer",
281  .item_name = av_default_item_name,
282  .option = options,
283  .version = LIBAVUTIL_VERSION_INT,
285 };
286 
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 };
packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
Definition: pixfmt.h:77
#define NULL
Definition: coverity.c:32
enum AVFieldOrder field_order
Video only.
Definition: avcodec.h:3965
This structure describes decoded (raw) audio or video data.
Definition: frame.h:218
AVOption.
Definition: opt.h:246
misc image utilities
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition: utils.c:4811
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: avcodec.h:3884
AVRational sample_aspect_ratio
sample aspect ratio (0 if unknown)
Definition: avformat.h:935
int num
Numerator.
Definition: rational.h:59
#define AV_OPT_FLAG_AUDIO_PARAM
Definition: opt.h:278
int size
Definition: avcodec.h:1431
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
static int ndi_write_header(AVFormatContext *avctx)
static AVPacket pkt
This struct describes the properties of an encoded stream.
Definition: avcodec.h:3876
Format I/O context.
Definition: avformat.h:1342
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
int width
Video only.
Definition: avcodec.h:3950
packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
Definition: pixfmt.h:234
AVOptions.
#define NDI_TIME_BASE_Q
AVFrame * last_avframe
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1410
uint8_t * data
Definition: avcodec.h:1430
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition: rational.h:104
static int ndi_write_packet(AVFormatContext *avctx, AVPacket *pkt)
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max)
Reduce a fraction.
Definition: rational.c:35
#define av_log(a,...)
#define AV_OPT_FLAG_ENCODING_PARAM
a generic parameter which can be set by the user for muxing or encoding
Definition: opt.h:276
static int ndi_write_trailer(AVFormatContext *avctx)
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
Definition: mathematics.c:142
int width
Definition: frame.h:276
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
#define AVERROR(e)
Definition: error.h:43
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:202
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
Definition: pixfmt.h:91
char * url
input or output URL.
Definition: avformat.h:1438
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:3880
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:236
AVRational avg_frame_rate
Average framerate.
Definition: avformat.h:946
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition: pixfmt.h:89
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1398
NDIlib_video_frame_t * video
static const AVOption options[]
const char * name
Definition: avformat.h:507
AVFormatContext * ctx
Definition: movenc.c:48
int n
Definition: avisynth_c.h:684
Passthrough codec, AVFrames wrapped in AVPacket.
Definition: avcodec.h:691
if(ret< 0)
Definition: vf_mcdeint.c:279
static void error(const char *err)
AVFrame * av_frame_clone(const AVFrame *src)
Create a new frame that references the same data as src.
Definition: frame.c:538
NDIlib_audio_frame_interleaved_16s_t * audio
Stream structure.
Definition: avformat.h:873
int format
format of the frame, -1 if unknown or unset Values correspond to enum AVPixelFormat for video frames...
Definition: frame.h:291
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:249
#define AV_OPT_FLAG_VIDEO_PARAM
Definition: opt.h:279
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
Describe the class of an AVClass context structure.
Definition: log.h:67
Rational number (pair of numerator and denominator).
Definition: rational.h:58
packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
Definition: pixfmt.h:236
static int ndi_setup_audio(AVFormatContext *avctx, AVStream *st)
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:232
int sample_rate
Audio only.
Definition: avcodec.h:3994
Main libavformat public API header.
#define AVFMT_NOFILE
Demuxer will use avio_open, no opened file should be provided by the caller.
Definition: avformat.h:465
static double c[64]
int den
Denominator.
Definition: rational.h:60
static const AVClass libndi_newtek_muxer_class
const AVClass * cclass
void * priv_data
Format private data.
Definition: avformat.h:1370
int channels
Audio only.
Definition: avcodec.h:3990
static int ndi_write_audio_packet(AVFormatContext *avctx, AVStream *st, AVPacket *pkt)
static int ndi_write_video_packet(AVFormatContext *avctx, AVStream *st, AVPacket *pkt)
#define NDI_TIME_BASE
int height
Definition: frame.h:276
#define av_freep(p)
NDIlib_send_instance_t ndi_send
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1020
int stream_index
Definition: avcodec.h:1432
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avformat.h:902
AVOutputFormat ff_libndi_newtek_muxer
static int ndi_setup_video(AVFormatContext *avctx, AVStream *st)
#define OFFSET(x)
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:57
This structure stores compressed data.
Definition: avcodec.h:1407
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: avcodec.h:1423
static uint8_t tmp[11]
Definition: aes_ctr.c:26