Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52867 views
1
/*
2
* Blackmagic DeckLink output
3
* Copyright (c) 2013-2014 Luca Barbato, Deti Fliegl
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 <DeckLinkAPI.h>
23
24
#include <pthread.h>
25
#include <semaphore.h>
26
27
extern "C" {
28
#include "config.h"
29
#include "libavformat/avformat.h"
30
#include "libavformat/internal.h"
31
#include "libavutil/imgutils.h"
32
#if CONFIG_LIBZVBI
33
#include <libzvbi.h>
34
#endif
35
}
36
37
#include "decklink_common.h"
38
#include "decklink_dec.h"
39
40
#if CONFIG_LIBZVBI
41
static uint8_t calc_parity_and_line_offset(int line)
42
{
43
uint8_t ret = (line < 313) << 5;
44
if (line >= 7 && line <= 22)
45
ret += line;
46
if (line >= 320 && line <= 335)
47
ret += (line - 313);
48
return ret;
49
}
50
51
int teletext_data_unit_from_vbi_data(int line, uint8_t *src, uint8_t *tgt)
52
{
53
vbi_bit_slicer slicer;
54
55
vbi_bit_slicer_init(&slicer, 720, 13500000, 6937500, 6937500, 0x00aaaae4, 0xffff, 18, 6, 42 * 8, VBI_MODULATION_NRZ_MSB, VBI_PIXFMT_UYVY);
56
57
if (vbi_bit_slice(&slicer, src, tgt + 4) == FALSE)
58
return -1;
59
60
tgt[0] = 0x02; // data_unit_id
61
tgt[1] = 0x2c; // data_unit_length
62
tgt[2] = calc_parity_and_line_offset(line); // field_parity, line_offset
63
tgt[3] = 0xe4; // framing code
64
65
return 0;
66
}
67
#endif
68
69
static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q)
70
{
71
memset(q, 0, sizeof(AVPacketQueue));
72
pthread_mutex_init(&q->mutex, NULL);
73
pthread_cond_init(&q->cond, NULL);
74
q->avctx = avctx;
75
}
76
77
static void avpacket_queue_flush(AVPacketQueue *q)
78
{
79
AVPacketList *pkt, *pkt1;
80
81
pthread_mutex_lock(&q->mutex);
82
for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
83
pkt1 = pkt->next;
84
av_packet_unref(&pkt->pkt);
85
av_freep(&pkt);
86
}
87
q->last_pkt = NULL;
88
q->first_pkt = NULL;
89
q->nb_packets = 0;
90
q->size = 0;
91
pthread_mutex_unlock(&q->mutex);
92
}
93
94
static void avpacket_queue_end(AVPacketQueue *q)
95
{
96
avpacket_queue_flush(q);
97
pthread_mutex_destroy(&q->mutex);
98
pthread_cond_destroy(&q->cond);
99
}
100
101
static unsigned long long avpacket_queue_size(AVPacketQueue *q)
102
{
103
unsigned long long size;
104
pthread_mutex_lock(&q->mutex);
105
size = q->size;
106
pthread_mutex_unlock(&q->mutex);
107
return size;
108
}
109
110
static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt)
111
{
112
AVPacketList *pkt1;
113
114
// Drop Packet if queue size is > 1GB
115
if (avpacket_queue_size(q) > 1024 * 1024 * 1024 ) {
116
av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n");
117
return -1;
118
}
119
/* duplicate the packet */
120
if (av_dup_packet(pkt) < 0) {
121
return -1;
122
}
123
124
pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
125
if (!pkt1) {
126
return -1;
127
}
128
pkt1->pkt = *pkt;
129
pkt1->next = NULL;
130
131
pthread_mutex_lock(&q->mutex);
132
133
if (!q->last_pkt) {
134
q->first_pkt = pkt1;
135
} else {
136
q->last_pkt->next = pkt1;
137
}
138
139
q->last_pkt = pkt1;
140
q->nb_packets++;
141
q->size += pkt1->pkt.size + sizeof(*pkt1);
142
143
pthread_cond_signal(&q->cond);
144
145
pthread_mutex_unlock(&q->mutex);
146
return 0;
147
}
148
149
static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block)
150
{
151
AVPacketList *pkt1;
152
int ret;
153
154
pthread_mutex_lock(&q->mutex);
155
156
for (;; ) {
157
pkt1 = q->first_pkt;
158
if (pkt1) {
159
q->first_pkt = pkt1->next;
160
if (!q->first_pkt) {
161
q->last_pkt = NULL;
162
}
163
q->nb_packets--;
164
q->size -= pkt1->pkt.size + sizeof(*pkt1);
165
*pkt = pkt1->pkt;
166
av_free(pkt1);
167
ret = 1;
168
break;
169
} else if (!block) {
170
ret = 0;
171
break;
172
} else {
173
pthread_cond_wait(&q->cond, &q->mutex);
174
}
175
}
176
pthread_mutex_unlock(&q->mutex);
177
return ret;
178
}
179
180
class decklink_input_callback : public IDeckLinkInputCallback
181
{
182
public:
183
decklink_input_callback(AVFormatContext *_avctx);
184
~decklink_input_callback();
185
186
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }
187
virtual ULONG STDMETHODCALLTYPE AddRef(void);
188
virtual ULONG STDMETHODCALLTYPE Release(void);
189
virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags);
190
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);
191
192
private:
193
ULONG m_refCount;
194
pthread_mutex_t m_mutex;
195
AVFormatContext *avctx;
196
decklink_ctx *ctx;
197
int no_video;
198
int64_t initial_video_pts;
199
int64_t initial_audio_pts;
200
};
201
202
decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : m_refCount(0)
203
{
204
avctx = _avctx;
205
decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
206
ctx = (struct decklink_ctx *) cctx->ctx;
207
initial_audio_pts = initial_video_pts = AV_NOPTS_VALUE;
208
pthread_mutex_init(&m_mutex, NULL);
209
}
210
211
decklink_input_callback::~decklink_input_callback()
212
{
213
pthread_mutex_destroy(&m_mutex);
214
}
215
216
ULONG decklink_input_callback::AddRef(void)
217
{
218
pthread_mutex_lock(&m_mutex);
219
m_refCount++;
220
pthread_mutex_unlock(&m_mutex);
221
222
return (ULONG)m_refCount;
223
}
224
225
ULONG decklink_input_callback::Release(void)
226
{
227
pthread_mutex_lock(&m_mutex);
228
m_refCount--;
229
pthread_mutex_unlock(&m_mutex);
230
231
if (m_refCount == 0) {
232
delete this;
233
return 0;
234
}
235
236
return (ULONG)m_refCount;
237
}
238
239
HRESULT decklink_input_callback::VideoInputFrameArrived(
240
IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame)
241
{
242
void *frameBytes;
243
void *audioFrameBytes;
244
BMDTimeValue frameTime;
245
BMDTimeValue frameDuration;
246
247
ctx->frameCount++;
248
249
// Handle Video Frame
250
if (videoFrame) {
251
AVPacket pkt;
252
AVCodecContext *c;
253
av_init_packet(&pkt);
254
c = ctx->video_st->codec;
255
if (ctx->frameCount % 25 == 0) {
256
unsigned long long qsize = avpacket_queue_size(&ctx->queue);
257
av_log(avctx, AV_LOG_DEBUG,
258
"Frame received (#%lu) - Valid (%liB) - QSize %fMB\n",
259
ctx->frameCount,
260
videoFrame->GetRowBytes() * videoFrame->GetHeight(),
261
(double)qsize / 1024 / 1024);
262
}
263
264
videoFrame->GetBytes(&frameBytes);
265
videoFrame->GetStreamTime(&frameTime, &frameDuration,
266
ctx->video_st->time_base.den);
267
268
if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) {
269
if (videoFrame->GetPixelFormat() == bmdFormat8BitYUV) {
270
unsigned bars[8] = {
271
0xEA80EA80, 0xD292D210, 0xA910A9A5, 0x90229035,
272
0x6ADD6ACA, 0x51EF515A, 0x286D28EF, 0x10801080 };
273
int width = videoFrame->GetWidth();
274
int height = videoFrame->GetHeight();
275
unsigned *p = (unsigned *)frameBytes;
276
277
for (int y = 0; y < height; y++) {
278
for (int x = 0; x < width; x += 2)
279
*p++ = bars[(x * 8) / width];
280
}
281
}
282
283
if (!no_video) {
284
av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - No input signal detected "
285
"- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);
286
}
287
no_video = 1;
288
} else {
289
if (no_video) {
290
av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - Input returned "
291
"- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);
292
}
293
no_video = 0;
294
}
295
296
pkt.pts = frameTime / ctx->video_st->time_base.num;
297
298
if (initial_video_pts == AV_NOPTS_VALUE) {
299
initial_video_pts = pkt.pts;
300
}
301
302
pkt.pts -= initial_video_pts;
303
pkt.dts = pkt.pts;
304
305
pkt.duration = frameDuration;
306
//To be made sure it still applies
307
pkt.flags |= AV_PKT_FLAG_KEY;
308
pkt.stream_index = ctx->video_st->index;
309
pkt.data = (uint8_t *)frameBytes;
310
pkt.size = videoFrame->GetRowBytes() *
311
videoFrame->GetHeight();
312
//fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts);
313
314
#if CONFIG_LIBZVBI
315
if (!no_video && ctx->teletext_lines && videoFrame->GetPixelFormat() == bmdFormat8BitYUV && videoFrame->GetWidth() == 720) {
316
IDeckLinkVideoFrameAncillary *vanc;
317
AVPacket txt_pkt;
318
uint8_t txt_buf0[1611]; // max 35 * 46 bytes decoded teletext lines + 1 byte data_identifier
319
uint8_t *txt_buf = txt_buf0;
320
321
if (videoFrame->GetAncillaryData(&vanc) == S_OK) {
322
int i;
323
int64_t line_mask = 1;
324
txt_buf[0] = 0x10; // data_identifier - EBU_data
325
txt_buf++;
326
for (i = 6; i < 336; i++, line_mask <<= 1) {
327
uint8_t *buf;
328
if ((ctx->teletext_lines & line_mask) && vanc->GetBufferForVerticalBlankingLine(i, (void**)&buf) == S_OK) {
329
if (teletext_data_unit_from_vbi_data(i, buf, txt_buf) >= 0)
330
txt_buf += 46;
331
}
332
if (i == 22)
333
i = 317;
334
}
335
vanc->Release();
336
if (txt_buf - txt_buf0 > 1) {
337
int stuffing_units = (4 - ((45 + txt_buf - txt_buf0) / 46) % 4) % 4;
338
while (stuffing_units--) {
339
memset(txt_buf, 0xff, 46);
340
txt_buf[1] = 0x2c; // data_unit_length
341
txt_buf += 46;
342
}
343
av_init_packet(&txt_pkt);
344
txt_pkt.pts = pkt.pts;
345
txt_pkt.dts = pkt.dts;
346
txt_pkt.stream_index = ctx->teletext_st->index;
347
txt_pkt.data = txt_buf0;
348
txt_pkt.size = txt_buf - txt_buf0;
349
if (avpacket_queue_put(&ctx->queue, &txt_pkt) < 0) {
350
++ctx->dropped;
351
}
352
}
353
}
354
}
355
#endif
356
357
c->frame_number++;
358
if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
359
++ctx->dropped;
360
}
361
}
362
363
// Handle Audio Frame
364
if (audioFrame) {
365
AVCodecContext *c;
366
AVPacket pkt;
367
BMDTimeValue audio_pts;
368
av_init_packet(&pkt);
369
370
c = ctx->audio_st->codec;
371
//hack among hacks
372
pkt.size = audioFrame->GetSampleFrameCount() * ctx->audio_st->codec->channels * (16 / 8);
373
audioFrame->GetBytes(&audioFrameBytes);
374
audioFrame->GetPacketTime(&audio_pts, ctx->audio_st->time_base.den);
375
pkt.pts = audio_pts / ctx->audio_st->time_base.num;
376
377
if (initial_audio_pts == AV_NOPTS_VALUE) {
378
initial_audio_pts = pkt.pts;
379
}
380
381
pkt.pts -= initial_audio_pts;
382
pkt.dts = pkt.pts;
383
384
//fprintf(stderr,"Audio Frame size %d ts %d\n", pkt.size, pkt.pts);
385
pkt.flags |= AV_PKT_FLAG_KEY;
386
pkt.stream_index = ctx->audio_st->index;
387
pkt.data = (uint8_t *)audioFrameBytes;
388
389
c->frame_number++;
390
if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
391
++ctx->dropped;
392
}
393
}
394
395
return S_OK;
396
}
397
398
HRESULT decklink_input_callback::VideoInputFormatChanged(
399
BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode,
400
BMDDetectedVideoInputFormatFlags)
401
{
402
return S_OK;
403
}
404
405
static HRESULT decklink_start_input(AVFormatContext *avctx)
406
{
407
struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
408
struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx;
409
410
ctx->input_callback = new decklink_input_callback(avctx);
411
ctx->dli->SetCallback(ctx->input_callback);
412
return ctx->dli->StartStreams();
413
}
414
415
extern "C" {
416
417
av_cold int ff_decklink_read_close(AVFormatContext *avctx)
418
{
419
struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
420
struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx;
421
422
if (ctx->capture_started) {
423
ctx->dli->StopStreams();
424
ctx->dli->DisableVideoInput();
425
ctx->dli->DisableAudioInput();
426
}
427
428
if (ctx->dli)
429
ctx->dli->Release();
430
if (ctx->dl)
431
ctx->dl->Release();
432
433
avpacket_queue_end(&ctx->queue);
434
435
av_freep(&cctx->ctx);
436
437
return 0;
438
}
439
440
av_cold int ff_decklink_read_header(AVFormatContext *avctx)
441
{
442
struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
443
struct decklink_ctx *ctx;
444
IDeckLinkDisplayModeIterator *itermode;
445
IDeckLinkIterator *iter;
446
IDeckLink *dl = NULL;
447
AVStream *st;
448
HRESULT result;
449
char fname[1024];
450
char *tmp;
451
int mode_num = 0;
452
453
ctx = (struct decklink_ctx *) av_mallocz(sizeof(struct decklink_ctx));
454
if (!ctx)
455
return AVERROR(ENOMEM);
456
ctx->list_devices = cctx->list_devices;
457
ctx->list_formats = cctx->list_formats;
458
ctx->teletext_lines = cctx->teletext_lines;
459
ctx->preroll = cctx->preroll;
460
cctx->ctx = ctx;
461
462
#if !CONFIG_LIBZVBI
463
if (ctx->teletext_lines) {
464
av_log(avctx, AV_LOG_ERROR, "Libzvbi support is needed for capturing teletext, please recompile FFmpeg.\n");
465
return AVERROR(ENOSYS);
466
}
467
#endif
468
469
/* Check audio channel option for valid values: 2, 8 or 16 */
470
switch (cctx->audio_channels) {
471
case 2:
472
case 8:
473
case 16:
474
break;
475
default:
476
av_log(avctx, AV_LOG_ERROR, "Value of channels option must be one of 2, 8 or 16\n");
477
return AVERROR(EINVAL);
478
}
479
480
iter = CreateDeckLinkIteratorInstance();
481
if (!iter) {
482
av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n");
483
return AVERROR(EIO);
484
}
485
486
/* List available devices. */
487
if (ctx->list_devices) {
488
ff_decklink_list_devices(avctx);
489
return AVERROR_EXIT;
490
}
491
492
strcpy (fname, avctx->filename);
493
tmp=strchr (fname, '@');
494
if (tmp != NULL) {
495
mode_num = atoi (tmp+1);
496
*tmp = 0;
497
}
498
499
/* Open device. */
500
while (iter->Next(&dl) == S_OK) {
501
const char *displayName;
502
ff_decklink_get_display_name(dl, &displayName);
503
if (!strcmp(fname, displayName)) {
504
av_free((void *) displayName);
505
ctx->dl = dl;
506
break;
507
}
508
av_free((void *) displayName);
509
dl->Release();
510
}
511
iter->Release();
512
if (!ctx->dl) {
513
av_log(avctx, AV_LOG_ERROR, "Could not open '%s'\n", fname);
514
return AVERROR(EIO);
515
}
516
517
/* Get input device. */
518
if (ctx->dl->QueryInterface(IID_IDeckLinkInput, (void **) &ctx->dli) != S_OK) {
519
av_log(avctx, AV_LOG_ERROR, "Could not open output device from '%s'\n",
520
avctx->filename);
521
ctx->dl->Release();
522
return AVERROR(EIO);
523
}
524
525
/* List supported formats. */
526
if (ctx->list_formats) {
527
ff_decklink_list_formats(avctx, DIRECTION_IN);
528
ctx->dli->Release();
529
ctx->dl->Release();
530
return AVERROR_EXIT;
531
}
532
533
if (ctx->dli->GetDisplayModeIterator(&itermode) != S_OK) {
534
av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n");
535
ctx->dl->Release();
536
return AVERROR(EIO);
537
}
538
539
if (mode_num > 0) {
540
if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) {
541
av_log(avctx, AV_LOG_ERROR, "Could not set mode %d for %s\n", mode_num, fname);
542
goto error;
543
}
544
}
545
546
itermode->Release();
547
548
/* Setup streams. */
549
st = avformat_new_stream(avctx, NULL);
550
if (!st) {
551
av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
552
goto error;
553
}
554
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
555
st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
556
st->codec->sample_rate = bmdAudioSampleRate48kHz;
557
st->codec->channels = cctx->audio_channels;
558
avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
559
ctx->audio_st=st;
560
561
st = avformat_new_stream(avctx, NULL);
562
if (!st) {
563
av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
564
goto error;
565
}
566
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
567
st->codec->width = ctx->bmd_width;
568
st->codec->height = ctx->bmd_height;
569
570
st->codec->time_base.den = ctx->bmd_tb_den;
571
st->codec->time_base.num = ctx->bmd_tb_num;
572
st->codec->bit_rate = av_image_get_buffer_size(st->codec->pix_fmt, ctx->bmd_width, ctx->bmd_height, 1) * 1/av_q2d(st->codec->time_base) * 8;
573
574
if (cctx->v210) {
575
st->codec->codec_id = AV_CODEC_ID_V210;
576
st->codec->codec_tag = MKTAG('V', '2', '1', '0');
577
} else {
578
st->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
579
st->codec->pix_fmt = AV_PIX_FMT_UYVY422;
580
st->codec->codec_tag = MKTAG('U', 'Y', 'V', 'Y');
581
}
582
583
avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
584
585
ctx->video_st=st;
586
587
if (ctx->teletext_lines) {
588
st = avformat_new_stream(avctx, NULL);
589
if (!st) {
590
av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
591
goto error;
592
}
593
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
594
st->codec->time_base.den = ctx->bmd_tb_den;
595
st->codec->time_base.num = ctx->bmd_tb_num;
596
st->codec->codec_id = AV_CODEC_ID_DVB_TELETEXT;
597
avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
598
ctx->teletext_st = st;
599
}
600
601
av_log(avctx, AV_LOG_VERBOSE, "Using %d input audio channels\n", ctx->audio_st->codec->channels);
602
result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, ctx->audio_st->codec->channels);
603
604
if (result != S_OK) {
605
av_log(avctx, AV_LOG_ERROR, "Cannot enable audio input\n");
606
goto error;
607
}
608
609
result = ctx->dli->EnableVideoInput(ctx->bmd_mode,
610
cctx->v210 ? bmdFormat10BitYUV : bmdFormat8BitYUV,
611
bmdVideoInputFlagDefault);
612
613
if (result != S_OK) {
614
av_log(avctx, AV_LOG_ERROR, "Cannot enable video input\n");
615
goto error;
616
}
617
618
avpacket_queue_init (avctx, &ctx->queue);
619
620
if (decklink_start_input (avctx) != S_OK) {
621
av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n");
622
goto error;
623
}
624
625
return 0;
626
627
error:
628
629
ctx->dli->Release();
630
ctx->dl->Release();
631
632
return AVERROR(EIO);
633
}
634
635
int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt)
636
{
637
struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
638
struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx;
639
AVFrame *frame = ctx->video_st->codec->coded_frame;
640
641
avpacket_queue_get(&ctx->queue, pkt, 1);
642
if (frame && (ctx->bmd_field_dominance == bmdUpperFieldFirst || ctx->bmd_field_dominance == bmdLowerFieldFirst)) {
643
frame->interlaced_frame = 1;
644
if (ctx->bmd_field_dominance == bmdUpperFieldFirst) {
645
frame->top_field_first = 1;
646
}
647
}
648
649
return 0;
650
}
651
652
} /* extern "C" */
653
654