FFmpeg
ccfifo.c
Go to the documentation of this file.
1 /*
2  * CEA-708 Closed Captioning FIFO
3  * Copyright (c) 2023 LTN Global Communications
4  *
5  * Author: Devin Heitmueller <dheitmueller@ltnglobal.com>
6  *
7  * This file is part of FFmpeg.
8  *
9  * FFmpeg is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * FFmpeg is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with FFmpeg; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23 
24 #include "ccfifo.h"
25 #include "libavutil/fifo.h"
26 
27 #define MAX_CC_ELEMENTS 128
28 
29 struct cc_lookup {
30  int num;
31  int den;
32  int cc_count;
33  int num_608;
34 };
35 
36 const static struct cc_lookup cc_lookup_vals[] = {
37  { 15, 1, 40, 4 },
38  { 24, 1, 25, 3 },
39  { 24000, 1001, 25, 3 },
40  { 30, 1, 20, 2 },
41  { 30000, 1001, 20, 2},
42  { 60, 1, 10, 1 },
43  { 60000, 1001, 10, 1},
44 };
45 
47 {
50  memset(ccf, 0, sizeof(*ccf));
51 }
52 
53 int ff_ccfifo_init(CCFifo *ccf, AVRational framerate, void *log_ctx)
54 {
55  int i;
56 
57  memset(ccf, 0, sizeof(*ccf));
58  ccf->log_ctx = log_ctx;
59  ccf->framerate = framerate;
60 
62  goto error;
63 
65  goto error;
66 
67  /* Based on the target FPS, figure out the expected cc_count and number of
68  608 tuples per packet. See ANSI/CTA-708-E Sec 4.3.6.1. */
69  for (i = 0; i < FF_ARRAY_ELEMS(cc_lookup_vals); i++) {
70  if (framerate.num == cc_lookup_vals[i].num &&
71  framerate.den == cc_lookup_vals[i].den) {
74  break;
75  }
76  }
77 
78  if (ccf->expected_608 == 0) {
79  /* We didn't find an output frame we support. We'll let the call succeed
80  and the FIFO to be allocated, but the extract/inject functions will simply
81  leave everything the way it is */
82  ccf->passthrough = 1;
83  }
84 
85  return 0;
86 
87 error:
88  ff_ccfifo_uninit(ccf);
89  return AVERROR(ENOMEM);
90 }
91 
92 int ff_ccfifo_injectbytes(CCFifo *ccf, uint8_t *cc_data, size_t len)
93 {
94  int cc_608_tuples = 0;
95  int cc_708_tuples = 0;
96  int cc_filled = 0;
97 
98  if (ccf->passthrough) {
99  return 0;
100  }
101 
102  if (len < ff_ccfifo_getoutputsize(ccf)) {
103  return AVERROR(EINVAL);
104  }
105 
106  /* Insert any available data from the 608 FIFO */
107  if (ccf->expected_608 <= av_fifo_can_read(ccf->cc_608_fifo))
108  cc_608_tuples = ccf->expected_608;
109  else
110  cc_608_tuples = av_fifo_can_read(ccf->cc_608_fifo);
111  av_fifo_read(ccf->cc_608_fifo, cc_data, cc_608_tuples);
112  cc_filled += cc_608_tuples;
113 
114  /* Insert any available data from the 708 FIFO */
115  if ((ccf->expected_cc_count - cc_filled) <= av_fifo_can_read(ccf->cc_708_fifo))
116  cc_708_tuples = ccf->expected_cc_count - cc_filled;
117  else
118  cc_708_tuples = av_fifo_can_read(ccf->cc_708_fifo);
119  av_fifo_read(ccf->cc_708_fifo, &cc_data[cc_filled * CC_BYTES_PER_ENTRY], cc_708_tuples);
120  cc_filled += cc_708_tuples;
121 
122  /* Insert 708 padding into any remaining fields */
123  while (cc_filled < ccf->expected_cc_count) {
124  cc_data[cc_filled * CC_BYTES_PER_ENTRY] = 0xfa;
125  cc_data[cc_filled * CC_BYTES_PER_ENTRY + 1] = 0x00;
126  cc_data[cc_filled * CC_BYTES_PER_ENTRY + 2] = 0x00;
127  cc_filled++;
128  }
129 
130  return 0;
131 }
132 
134 {
135  AVFrameSideData *sd;
136  int ret;
137 
138  if (ccf->passthrough == 1 || ccf->cc_detected == 0)
139  return 0;
140 
143  if (sd) {
144  ret = ff_ccfifo_injectbytes(ccf, sd->data, sd->size);
145  if (ret < 0) {
147  return ret;
148  }
149  }
150 
151  return 0;
152 }
153 
154 int ff_ccfifo_extractbytes(CCFifo *ccf, uint8_t *cc_bytes, size_t len)
155 {
157 
158  if (ccf->passthrough == 1) {
160  "cc_fifo cannot transcode captions fps=%d/%d\n",
161  ccf->framerate.num, ccf->framerate.den);
162  return 0;
163  }
164 
165  ccf->cc_detected = 1;
166 
167  for (int i = 0; i < cc_count; i++) {
168  /* See ANSI/CTA-708-E Sec 4.3, Table 3 */
169  uint8_t cc_valid = (cc_bytes[CC_BYTES_PER_ENTRY*i] & 0x04) >> 2;
170  uint8_t cc_type = cc_bytes[CC_BYTES_PER_ENTRY*i] & 0x03;
171  if (cc_type == 0x00 || cc_type == 0x01) {
172  av_fifo_write(ccf->cc_608_fifo, &cc_bytes[CC_BYTES_PER_ENTRY*i], 1);
173  } else if (cc_valid && (cc_type == 0x02 || cc_type == 0x03)) {
174  av_fifo_write(ccf->cc_708_fifo, &cc_bytes[CC_BYTES_PER_ENTRY*i], 1);
175  }
176  }
177  return 0;
178 }
179 
180 /* Read the A53 side data, discard padding, and put 608/708 into
181  queues so we can ensure they get into the output frames at
182  the correct rate... */
184 {
186  if (side_data) {
187  ff_ccfifo_extractbytes(ccf, side_data->data, side_data->size);
188 
189  /* Remove the side data, as we will re-create it on the
190  output as needed */
191  if (!ccf->passthrough)
193  }
194  return 0;
195 }
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:31
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
ff_ccfifo_extractbytes
int ff_ccfifo_extractbytes(CCFifo *ccf, uint8_t *cc_bytes, size_t len)
Just like ff_ccfifo_extract(), but takes the raw bytes instead of an AVFrame.
Definition: ccfifo.c:154
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
av_frame_get_side_data
AVFrameSideData * av_frame_get_side_data(const AVFrame *frame, enum AVFrameSideDataType type)
Definition: frame.c:858
av_frame_new_side_data
AVFrameSideData * av_frame_new_side_data(AVFrame *frame, enum AVFrameSideDataType type, size_t size)
Add a new side data to a frame.
Definition: frame.c:786
AV_FRAME_DATA_A53_CC
@ AV_FRAME_DATA_A53_CC
ATSC A53 Part 4 Closed Captions.
Definition: frame.h:59
cc_lookup_vals
const static struct cc_lookup cc_lookup_vals[]
Definition: ccfifo.c:36
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:375
CCFifo::framerate
AVRational framerate
Definition: ccfifo.h:43
fifo.h
av_fifo_write
int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems)
Write data into a FIFO.
Definition: fifo.c:188
ff_ccfifo_uninit
void ff_ccfifo_uninit(CCFifo *ccf)
Free all memory allocated in a CCFifo and clear the context.
Definition: ccfifo.c:46
AVRational::num
int num
Numerator.
Definition: rational.h:59
CCFifo::expected_608
int expected_608
Definition: ccfifo.h:45
CCFifo::cc_detected
int cc_detected
Definition: ccfifo.h:46
AVFrameSideData::size
size_t size
Definition: frame.h:253
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
av_fifo_read
int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems)
Read data from a FIFO.
Definition: fifo.c:240
ff_ccfifo_inject
int ff_ccfifo_inject(CCFifo *ccf, AVFrame *frame)
Insert CC data from the FIFO into an AVFrame (as side data)
Definition: ccfifo.c:133
ff_ccfifo_getoutputsize
static int ff_ccfifo_getoutputsize(const CCFifo *ccf)
Provide the size in bytes of an output buffer to allocate.
Definition: ccfifo.h:95
MAX_CC_ELEMENTS
#define MAX_CC_ELEMENTS
Definition: ccfifo.c:27
cc_lookup::cc_count
int cc_count
Definition: ccfifo.c:32
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
CCFifo::passthrough
int passthrough
Definition: ccfifo.h:47
framerate
float framerate
Definition: av1_levels.c:29
cc_lookup
Definition: ccfifo.c:29
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
av_fifo_can_read
size_t av_fifo_can_read(const AVFifo *f)
Definition: fifo.c:87
ff_ccfifo_extract
int ff_ccfifo_extract(CCFifo *ccf, AVFrame *frame)
Extract CC data from an AVFrame.
Definition: ccfifo.c:183
CCFifo::cc_608_fifo
struct AVFifo * cc_608_fifo
Definition: ccfifo.h:41
CCFifo
Definition: ccfifo.h:40
cc_lookup::den
int den
Definition: ccfifo.c:31
CC_BYTES_PER_ENTRY
#define CC_BYTES_PER_ENTRY
Definition: ccfifo.h:38
AVFrameSideData::data
uint8_t * data
Definition: frame.h:252
av_frame_remove_side_data
void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type)
Remove and free all side data instances of the given type.
Definition: frame.c:924
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:255
len
int len
Definition: vorbis_enc_data.h:426
ff_ccfifo_init
int ff_ccfifo_init(CCFifo *ccf, AVRational framerate, void *log_ctx)
Initialize a CCFifo.
Definition: ccfifo.c:53
ret
ret
Definition: filter_design.txt:187
ff_ccfifo_injectbytes
int ff_ccfifo_injectbytes(CCFifo *ccf, uint8_t *cc_data, size_t len)
Just like ff_ccfifo_inject(), but takes the raw bytes to insert the CC data int rather than an AVFram...
Definition: ccfifo.c:92
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:264
cc_lookup::num
int num
Definition: ccfifo.c:30
av_fifo_alloc2
AVFifo * av_fifo_alloc2(size_t nb_elems, size_t elem_size, unsigned int flags)
Allocate and initialize an AVFifo with a given element size.
Definition: fifo.c:47
CCFifo::expected_cc_count
int expected_cc_count
Definition: ccfifo.h:44
CCFifo::cc_708_fifo
struct AVFifo * cc_708_fifo
Definition: ccfifo.h:42
CCFifo::log_ctx
void * log_ctx
Definition: ccfifo.h:49
AVRational::den
int den
Denominator.
Definition: rational.h:60
av_log_once
void av_log_once(void *avcl, int initial_level, int subsequent_level, int *state, const char *fmt,...)
Definition: log.c:422
AVFrameSideData
Structure to hold side data for an AVFrame.
Definition: frame.h:250
av_fifo_freep2
void av_fifo_freep2(AVFifo **f)
Free an AVFifo and reset pointer to NULL.
Definition: fifo.c:286
ccfifo.h
cc_lookup::num_608
int num_608
Definition: ccfifo.c:33
CCFifo::passthrough_warning
int passthrough_warning
Definition: ccfifo.h:48