FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ccaption_dec.c
Go to the documentation of this file.
1 /*
2  * Closed Caption Decoding
3  * Copyright (c) 2015 Anshul Maheshwari
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 "avcodec.h"
23 #include "ass.h"
24 #include "libavutil/opt.h"
25 
26 #define SCREEN_ROWS 15
27 #define SCREEN_COLUMNS 32
28 
29 #define SET_FLAG(var, val) ( (var) |= ( 1 << (val)) )
30 #define UNSET_FLAG(var, val) ( (var) &= ~( 1 << (val)) )
31 #define CHECK_FLAG(var, val) ( (var) & ( 1 << (val)) )
32 
33 static const AVRational ms_tb = {1, 1000};
34 
35 /*
36  * TODO list
37  * 1) handle font and color completely
38  */
39 enum cc_mode {
44 };
45 
57 };
58 
59 enum cc_font {
64 };
65 
66 enum cc_charset {
71 };
72 
73 static const char *charset_overrides[4][128] =
74 {
76  [0x27] = "\u2019",
77  [0x2a] = "\u00e1",
78  [0x5c] = "\u00e9",
79  [0x5e] = "\u00ed",
80  [0x5f] = "\u00f3",
81  [0x60] = "\u00fa",
82  [0x7b] = "\u00e7",
83  [0x7c] = "\u00f7",
84  [0x7d] = "\u00d1",
85  [0x7e] = "\u00f1",
86  [0x7f] = "\u2588"
87  },
89  [0x30] = "\u00ae",
90  [0x31] = "\u00b0",
91  [0x32] = "\u00bd",
92  [0x33] = "\u00bf",
93  [0x34] = "\u2122",
94  [0x35] = "\u00a2",
95  [0x36] = "\u00a3",
96  [0x37] = "\u266a",
97  [0x38] = "\u00e0",
98  [0x39] = "\u00A0",
99  [0x3a] = "\u00e8",
100  [0x3b] = "\u00e2",
101  [0x3c] = "\u00ea",
102  [0x3d] = "\u00ee",
103  [0x3e] = "\u00f4",
104  [0x3f] = "\u00fb",
105  },
107  [0x20] = "\u00c1",
108  [0x21] = "\u00c9",
109  [0x22] = "\u00d3",
110  [0x23] = "\u00da",
111  [0x24] = "\u00dc",
112  [0x25] = "\u00fc",
113  [0x26] = "\u00b4",
114  [0x27] = "\u00a1",
115  [0x28] = "*",
116  [0x29] = "\u2018",
117  [0x2a] = "-",
118  [0x2b] = "\u00a9",
119  [0x2c] = "\u2120",
120  [0x2d] = "\u00b7",
121  [0x2e] = "\u201c",
122  [0x2f] = "\u201d",
123  [0x30] = "\u00c0",
124  [0x31] = "\u00c2",
125  [0x32] = "\u00c7",
126  [0x33] = "\u00c8",
127  [0x34] = "\u00ca",
128  [0x35] = "\u00cb",
129  [0x36] = "\u00eb",
130  [0x37] = "\u00ce",
131  [0x38] = "\u00cf",
132  [0x39] = "\u00ef",
133  [0x3a] = "\u00d4",
134  [0x3b] = "\u00d9",
135  [0x3c] = "\u00f9",
136  [0x3d] = "\u00db",
137  [0x3e] = "\u00ab",
138  [0x3f] = "\u00bb",
139  },
141  [0x20] = "\u00c3",
142  [0x21] = "\u00e3",
143  [0x22] = "\u00cd",
144  [0x23] = "\u00cc",
145  [0x24] = "\u00ec",
146  [0x25] = "\u00d2",
147  [0x26] = "\u00f2",
148  [0x27] = "\u00d5",
149  [0x28] = "\u00f5",
150  [0x29] = "{",
151  [0x2a] = "}",
152  [0x2b] = "\\",
153  [0x2c] = "^",
154  [0x2d] = "_",
155  [0x2e] = "|",
156  [0x2f] = "~",
157  [0x30] = "\u00c4",
158  [0x31] = "\u00e4",
159  [0x32] = "\u00d6",
160  [0x33] = "\u00f6",
161  [0x34] = "\u00df",
162  [0x35] = "\u00a5",
163  [0x36] = "\u00a4",
164  [0x37] = "\u00a6",
165  [0x38] = "\u00c5",
166  [0x39] = "\u00e5",
167  [0x3a] = "\u00d8",
168  [0x3b] = "\u00f8",
169  [0x3c] = "\u250c",
170  [0x3d] = "\u2510",
171  [0x3e] = "\u2514",
172  [0x3f] = "\u2518",
173  },
174 };
175 
176 static const unsigned char pac2_attribs[32][3] = // Color, font, ident
177 {
178  { CCCOL_WHITE, CCFONT_REGULAR, 0 }, // 0x40 || 0x60
179  { CCCOL_WHITE, CCFONT_UNDERLINED, 0 }, // 0x41 || 0x61
180  { CCCOL_GREEN, CCFONT_REGULAR, 0 }, // 0x42 || 0x62
181  { CCCOL_GREEN, CCFONT_UNDERLINED, 0 }, // 0x43 || 0x63
182  { CCCOL_BLUE, CCFONT_REGULAR, 0 }, // 0x44 || 0x64
183  { CCCOL_BLUE, CCFONT_UNDERLINED, 0 }, // 0x45 || 0x65
184  { CCCOL_CYAN, CCFONT_REGULAR, 0 }, // 0x46 || 0x66
185  { CCCOL_CYAN, CCFONT_UNDERLINED, 0 }, // 0x47 || 0x67
186  { CCCOL_RED, CCFONT_REGULAR, 0 }, // 0x48 || 0x68
187  { CCCOL_RED, CCFONT_UNDERLINED, 0 }, // 0x49 || 0x69
188  { CCCOL_YELLOW, CCFONT_REGULAR, 0 }, // 0x4a || 0x6a
189  { CCCOL_YELLOW, CCFONT_UNDERLINED, 0 }, // 0x4b || 0x6b
190  { CCCOL_MAGENTA, CCFONT_REGULAR, 0 }, // 0x4c || 0x6c
191  { CCCOL_MAGENTA, CCFONT_UNDERLINED, 0 }, // 0x4d || 0x6d
192  { CCCOL_WHITE, CCFONT_ITALICS, 0 }, // 0x4e || 0x6e
193  { CCCOL_WHITE, CCFONT_UNDERLINED_ITALICS, 0 }, // 0x4f || 0x6f
194  { CCCOL_WHITE, CCFONT_REGULAR, 0 }, // 0x50 || 0x70
195  { CCCOL_WHITE, CCFONT_UNDERLINED, 0 }, // 0x51 || 0x71
196  { CCCOL_WHITE, CCFONT_REGULAR, 4 }, // 0x52 || 0x72
197  { CCCOL_WHITE, CCFONT_UNDERLINED, 4 }, // 0x53 || 0x73
198  { CCCOL_WHITE, CCFONT_REGULAR, 8 }, // 0x54 || 0x74
199  { CCCOL_WHITE, CCFONT_UNDERLINED, 8 }, // 0x55 || 0x75
200  { CCCOL_WHITE, CCFONT_REGULAR, 12 }, // 0x56 || 0x76
201  { CCCOL_WHITE, CCFONT_UNDERLINED, 12 }, // 0x57 || 0x77
202  { CCCOL_WHITE, CCFONT_REGULAR, 16 }, // 0x58 || 0x78
203  { CCCOL_WHITE, CCFONT_UNDERLINED, 16 }, // 0x59 || 0x79
204  { CCCOL_WHITE, CCFONT_REGULAR, 20 }, // 0x5a || 0x7a
205  { CCCOL_WHITE, CCFONT_UNDERLINED, 20 }, // 0x5b || 0x7b
206  { CCCOL_WHITE, CCFONT_REGULAR, 24 }, // 0x5c || 0x7c
207  { CCCOL_WHITE, CCFONT_UNDERLINED, 24 }, // 0x5d || 0x7d
208  { CCCOL_WHITE, CCFONT_REGULAR, 28 }, // 0x5e || 0x7e
209  { CCCOL_WHITE, CCFONT_UNDERLINED, 28 } // 0x5f || 0x7f
210  /* total 32 entries */
211 };
212 
213 struct Screen {
214  /* +1 is used to compensate null character of string */
219  /*
220  * Bitmask of used rows; if a bit is not set, the
221  * corresponding row is not used.
222  * for setting row 1 use row | (1 << 0)
223  * for setting row 15 use row | (1 << 14)
224  */
225  int16_t row_used;
226 };
227 
228 typedef struct CCaptionSubContext {
229  AVClass *class;
231  struct Screen screen[2];
238  AVBPrint buffer;
240  int rollup;
241  enum cc_mode mode;
242  int64_t start_time;
243  /* visible screen time */
244  int64_t startv_time;
245  int64_t end_time;
247  int64_t last_real_time;
248  char prev_cmd[2];
249  /* buffer to store pkt data */
254 
255 
257 {
258  int ret;
259  CCaptionSubContext *ctx = avctx->priv_data;
260 
262  /* taking by default roll up to 2 */
263  ctx->mode = CCMODE_ROLLUP;
264  ctx->rollup = 2;
265  ctx->cursor_row = 10;
266  ret = ff_ass_subtitle_header(avctx, "Monospace",
273  3,
275  if (ret < 0) {
276  return ret;
277  }
278 
279  return ret;
280 }
281 
283 {
284  CCaptionSubContext *ctx = avctx->priv_data;
286  av_freep(&ctx->pktbuf);
287  ctx->pktbuf_size = 0;
288  return 0;
289 }
290 
291 static void flush_decoder(AVCodecContext *avctx)
292 {
293  CCaptionSubContext *ctx = avctx->priv_data;
294  ctx->screen[0].row_used = 0;
295  ctx->screen[1].row_used = 0;
296  ctx->prev_cmd[0] = 0;
297  ctx->prev_cmd[1] = 0;
298  ctx->mode = CCMODE_ROLLUP;
299  ctx->rollup = 2;
300  ctx->cursor_row = 10;
301  ctx->cursor_column = 0;
302  ctx->cursor_font = 0;
303  ctx->cursor_color = 0;
304  ctx->cursor_charset = 0;
305  ctx->active_screen = 0;
306  ctx->last_real_time = 0;
307  ctx->screen_touched = 0;
308  ctx->buffer_changed = 0;
309  if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
310  ctx->readorder = 0;
311  av_bprint_clear(&ctx->buffer);
312 }
313 
314 /**
315  * @param ctx closed caption context just to print log
316  */
317 static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch)
318 {
319  uint8_t col = ctx->cursor_column;
320  char *row = screen->characters[ctx->cursor_row];
321  char *font = screen->fonts[ctx->cursor_row];
322  char *charset = screen->charsets[ctx->cursor_row];
323 
324  if (col < SCREEN_COLUMNS) {
325  row[col] = ch;
326  font[col] = ctx->cursor_font;
327  charset[col] = ctx->cursor_charset;
329  if (ch) ctx->cursor_column++;
330  return;
331  }
332  /* We have extra space at end only for null character */
333  else if (col == SCREEN_COLUMNS && ch == 0) {
334  row[col] = ch;
335  return;
336  }
337  else {
338  av_log(ctx, AV_LOG_WARNING, "Data Ignored since exceeding screen width\n");
339  return;
340  }
341 }
342 
343 /**
344  * This function after validating parity bit, also remove it from data pair.
345  * The first byte doesn't pass parity, we replace it with a solid blank
346  * and process the pair.
347  * If the second byte doesn't pass parity, it returns INVALIDDATA
348  * user can ignore the whole pair and pass the other pair.
349  */
350 static int validate_cc_data_pair(uint8_t *cc_data_pair)
351 {
352  uint8_t cc_valid = (*cc_data_pair & 4) >>2;
353  uint8_t cc_type = *cc_data_pair & 3;
354 
355  if (!cc_valid)
356  return AVERROR_INVALIDDATA;
357 
358  // if EIA-608 data then verify parity.
359  if (cc_type==0 || cc_type==1) {
360  if (!av_parity(cc_data_pair[2])) {
361  return AVERROR_INVALIDDATA;
362  }
363  if (!av_parity(cc_data_pair[1])) {
364  cc_data_pair[1]=0x7F;
365  }
366  }
367 
368  //Skip non-data
369  if ((cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD)
370  && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0)
371  return AVERROR_PATCHWELCOME;
372 
373  //skip 708 data
374  if (cc_type == 3 || cc_type == 2)
375  return AVERROR_PATCHWELCOME;
376 
377  /* remove parity bit */
378  cc_data_pair[1] &= 0x7F;
379  cc_data_pair[2] &= 0x7F;
380 
381  return 0;
382 }
383 
385 {
386  switch (ctx->mode) {
387  case CCMODE_POPON:
388  // use Inactive screen
389  return ctx->screen + !ctx->active_screen;
390  case CCMODE_PAINTON:
391  case CCMODE_ROLLUP:
392  case CCMODE_TEXT:
393  // use active screen
394  return ctx->screen + ctx->active_screen;
395  }
396  /* It was never an option */
397  return NULL;
398 }
399 
401 {
402  struct Screen *screen;
403  int i, keep_lines;
404 
405  if (ctx->mode == CCMODE_TEXT)
406  return;
407 
408  screen = get_writing_screen(ctx);
409 
410  /* +1 signify cursor_row starts from 0
411  * Can't keep lines less then row cursor pos
412  */
413  keep_lines = FFMIN(ctx->cursor_row + 1, ctx->rollup);
414 
415  for (i = 0; i < SCREEN_ROWS; i++) {
416  if (i > ctx->cursor_row - keep_lines && i <= ctx->cursor_row)
417  continue;
418  UNSET_FLAG(screen->row_used, i);
419  }
420 
421  for (i = 0; i < keep_lines && screen->row_used; i++) {
422  const int i_row = ctx->cursor_row - keep_lines + i + 1;
423 
424  memcpy(screen->characters[i_row], screen->characters[i_row+1], SCREEN_COLUMNS);
425  memcpy(screen->colors[i_row], screen->colors[i_row+1], SCREEN_COLUMNS);
426  memcpy(screen->fonts[i_row], screen->fonts[i_row+1], SCREEN_COLUMNS);
427  memcpy(screen->charsets[i_row], screen->charsets[i_row+1], SCREEN_COLUMNS);
428  if (CHECK_FLAG(screen->row_used, i_row + 1))
429  SET_FLAG(screen->row_used, i_row);
430  }
431 
432  UNSET_FLAG(screen->row_used, ctx->cursor_row);
433 }
434 
436 {
437  int i, j, tab = 0;
438  struct Screen *screen = ctx->screen + ctx->active_screen;
439  enum cc_font prev_font = CCFONT_REGULAR;
440  av_bprint_clear(&ctx->buffer);
441 
442  for (i = 0; screen->row_used && i < SCREEN_ROWS; i++)
443  {
444  if (CHECK_FLAG(screen->row_used, i)) {
445  const char *row = screen->characters[i];
446  const char *charset = screen->charsets[i];
447  j = 0;
448  while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN)
449  j++;
450  if (!tab || j < tab)
451  tab = j;
452  }
453  }
454 
455  for (i = 0; screen->row_used && i < SCREEN_ROWS; i++)
456  {
457  if (CHECK_FLAG(screen->row_used, i)) {
458  const char *row = screen->characters[i];
459  const char *font = screen->fonts[i];
460  const char *charset = screen->charsets[i];
461  const char *override;
462  int x, y, seen_char = 0;
463  j = 0;
464 
465  /* skip leading space */
466  while (row[j] == ' ' && charset[j] == CCSET_BASIC_AMERICAN && j < tab)
467  j++;
468 
469  x = ASS_DEFAULT_PLAYRESX * (0.1 + 0.0250 * j);
470  y = ASS_DEFAULT_PLAYRESY * (0.1 + 0.0533 * i);
471  av_bprintf(&ctx->buffer, "{\\an7}{\\pos(%d,%d)}", x, y);
472 
473  for (; j < SCREEN_COLUMNS; j++) {
474  const char *e_tag = "", *s_tag = "";
475 
476  if (row[j] == 0)
477  break;
478 
479  if (prev_font != font[j]) {
480  switch (prev_font) {
481  case CCFONT_ITALICS:
482  e_tag = "{\\i0}";
483  break;
484  case CCFONT_UNDERLINED:
485  e_tag = "{\\u0}";
486  break;
488  e_tag = "{\\u0}{\\i0}";
489  break;
490  }
491  switch (font[j]) {
492  case CCFONT_ITALICS:
493  s_tag = "{\\i1}";
494  break;
495  case CCFONT_UNDERLINED:
496  s_tag = "{\\u1}";
497  break;
499  s_tag = "{\\u1}{\\i1}";
500  break;
501  }
502  }
503  prev_font = font[j];
504  override = charset_overrides[(int)charset[j]][(int)row[j]];
505  if (override) {
506  av_bprintf(&ctx->buffer, "%s%s%s", e_tag, s_tag, override);
507  seen_char = 1;
508  } else if (row[j] == ' ' && !seen_char) {
509  av_bprintf(&ctx->buffer, "%s%s\\h", e_tag, s_tag);
510  } else {
511  av_bprintf(&ctx->buffer, "%s%s%c", e_tag, s_tag, row[j]);
512  seen_char = 1;
513  }
514 
515  }
516  av_bprintf(&ctx->buffer, "\\N");
517  }
518  }
519  if (!av_bprint_is_complete(&ctx->buffer))
520  return AVERROR(ENOMEM);
521  if (screen->row_used && ctx->buffer.len >= 2) {
522  ctx->buffer.len -= 2;
523  ctx->buffer.str[ctx->buffer.len] = 0;
524  }
525  ctx->buffer_changed = 1;
526  return 0;
527 }
528 
529 static int reap_screen(CCaptionSubContext *ctx, int64_t pts)
530 {
531  ctx->start_time = ctx->startv_time;
532  ctx->startv_time = pts;
533  ctx->end_time = pts;
534  return capture_screen(ctx);
535 }
536 
538 {
539  int i = lo - 0x20;
540  struct Screen *screen = get_writing_screen(ctx);
541 
542  if (i >= 32)
543  return;
544 
545  ctx->cursor_color = pac2_attribs[i][0];
546  ctx->cursor_font = pac2_attribs[i][1];
547 
548  SET_FLAG(screen->row_used, ctx->cursor_row);
549  write_char(ctx, screen, ' ');
550 }
551 
553 {
554  static const int8_t row_map[] = {
555  11, -1, 1, 2, 3, 4, 12, 13, 14, 15, 5, 6, 7, 8, 9, 10
556  };
557  const int index = ( (hi<<1) & 0x0e) | ( (lo>>5) & 0x01 );
558  struct Screen *screen = get_writing_screen(ctx);
559  int indent, i;
560 
561  if (row_map[index] <= 0) {
562  av_log(ctx, AV_LOG_DEBUG, "Invalid pac index encountered\n");
563  return;
564  }
565 
566  lo &= 0x1f;
567 
568  ctx->cursor_row = row_map[index] - 1;
569  ctx->cursor_color = pac2_attribs[lo][0];
570  ctx->cursor_font = pac2_attribs[lo][1];
572  ctx->cursor_column = 0;
573  indent = pac2_attribs[lo][2];
574  for (i = 0; i < indent; i++) {
575  write_char(ctx, screen, ' ');
576  }
577 }
578 
579 /**
580  * @param pts it is required to set end time
581  */
582 static void handle_edm(CCaptionSubContext *ctx, int64_t pts)
583 {
584  struct Screen *screen = ctx->screen + ctx->active_screen;
585 
586  // In buffered mode, keep writing to screen until it is wiped.
587  // Before wiping the display, capture contents to emit subtitle.
588  if (!ctx->real_time)
589  reap_screen(ctx, pts);
590 
591  screen->row_used = 0;
592 
593  // In realtime mode, emit an empty caption so the last one doesn't
594  // stay on the screen.
595  if (ctx->real_time)
596  reap_screen(ctx, pts);
597 }
598 
599 static void handle_eoc(CCaptionSubContext *ctx, int64_t pts)
600 {
601  // In buffered mode, we wait til the *next* EOC and
602  // reap what was already on the screen since the last EOC.
603  if (!ctx->real_time)
604  handle_edm(ctx,pts);
605 
606  ctx->active_screen = !ctx->active_screen;
607  ctx->cursor_column = 0;
608 
609  // In realtime mode, we display the buffered contents (after
610  // flipping the buffer to active above) as soon as EOC arrives.
611  if (ctx->real_time)
612  reap_screen(ctx, pts);
613 }
614 
615 static void handle_delete_end_of_row(CCaptionSubContext *ctx, char hi, char lo)
616 {
617  struct Screen *screen = get_writing_screen(ctx);
618  write_char(ctx, screen, 0);
619 }
620 
621 static void handle_char(CCaptionSubContext *ctx, char hi, char lo, int64_t pts)
622 {
623  struct Screen *screen = get_writing_screen(ctx);
624 
625  SET_FLAG(screen->row_used, ctx->cursor_row);
626 
627  switch (hi) {
628  case 0x11:
630  break;
631  case 0x12:
632  if (ctx->cursor_column > 0)
633  ctx->cursor_column -= 1;
635  break;
636  case 0x13:
637  if (ctx->cursor_column > 0)
638  ctx->cursor_column -= 1;
640  break;
641  default:
643  write_char(ctx, screen, hi);
644  break;
645  }
646 
647  if (lo) {
648  write_char(ctx, screen, lo);
649  }
650  write_char(ctx, screen, 0);
651 
652  if (ctx->mode != CCMODE_POPON)
653  ctx->screen_touched = 1;
654 
655  if (lo)
656  ff_dlog(ctx, "(%c,%c)\n", hi, lo);
657  else
658  ff_dlog(ctx, "(%c)\n", hi);
659 }
660 
661 static void process_cc608(CCaptionSubContext *ctx, int64_t pts, uint8_t hi, uint8_t lo)
662 {
663  if (hi == ctx->prev_cmd[0] && lo == ctx->prev_cmd[1]) {
664  /* ignore redundant command */
665  return;
666  }
667 
668  /* set prev command */
669  ctx->prev_cmd[0] = hi;
670  ctx->prev_cmd[1] = lo;
671 
672  if ( (hi == 0x10 && (lo >= 0x40 && lo <= 0x5f)) ||
673  ( (hi >= 0x11 && hi <= 0x17) && (lo >= 0x40 && lo <= 0x7f) ) ) {
674  handle_pac(ctx, hi, lo);
675  } else if ( ( hi == 0x11 && lo >= 0x20 && lo <= 0x2f ) ||
676  ( hi == 0x17 && lo >= 0x2e && lo <= 0x2f) ) {
677  handle_textattr(ctx, hi, lo);
678  } else if (hi == 0x14 || hi == 0x15 || hi == 0x1c) {
679  switch (lo) {
680  case 0x20:
681  /* resume caption loading */
682  ctx->mode = CCMODE_POPON;
683  break;
684  case 0x24:
685  handle_delete_end_of_row(ctx, hi, lo);
686  break;
687  case 0x25:
688  case 0x26:
689  case 0x27:
690  ctx->rollup = lo - 0x23;
691  ctx->mode = CCMODE_ROLLUP;
692  break;
693  case 0x29:
694  /* resume direct captioning */
695  ctx->mode = CCMODE_PAINTON;
696  break;
697  case 0x2b:
698  /* resume text display */
699  ctx->mode = CCMODE_TEXT;
700  break;
701  case 0x2c:
702  /* erase display memory */
703  handle_edm(ctx, pts);
704  break;
705  case 0x2d:
706  /* carriage return */
707  ff_dlog(ctx, "carriage return\n");
708  if (!ctx->real_time)
709  reap_screen(ctx, pts);
710  roll_up(ctx);
711  ctx->cursor_column = 0;
712  break;
713  case 0x2e:
714  /* erase buffered (non displayed) memory */
715  // Only in realtime mode. In buffered mode, we re-use the inactive screen
716  // for our own buffering.
717  if (ctx->real_time) {
718  struct Screen *screen = ctx->screen + !ctx->active_screen;
719  screen->row_used = 0;
720  }
721  break;
722  case 0x2f:
723  /* end of caption */
724  ff_dlog(ctx, "handle_eoc\n");
725  handle_eoc(ctx, pts);
726  break;
727  default:
728  ff_dlog(ctx, "Unknown command 0x%hhx 0x%hhx\n", hi, lo);
729  break;
730  }
731  } else if (hi >= 0x11 && hi <= 0x13) {
732  /* Special characters */
733  handle_char(ctx, hi, lo, pts);
734  } else if (hi >= 0x20) {
735  /* Standard characters (always in pairs) */
736  handle_char(ctx, hi, lo, pts);
737  ctx->prev_cmd[0] = ctx->prev_cmd[1] = 0;
738  } else if (hi == 0x17 && lo >= 0x21 && lo <= 0x23) {
739  int i;
740  /* Tab offsets (spacing) */
741  for (i = 0; i < lo - 0x20; i++) {
742  handle_char(ctx, ' ', 0, pts);
743  }
744  } else {
745  /* Ignoring all other non data code */
746  ff_dlog(ctx, "Unknown command 0x%hhx 0x%hhx\n", hi, lo);
747  }
748 }
749 
750 static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avpkt)
751 {
752  CCaptionSubContext *ctx = avctx->priv_data;
753  AVSubtitle *sub = data;
754  const int64_t start_time = sub->pts;
755  uint8_t *bptr = NULL;
756  int len = avpkt->size;
757  int ret = 0;
758  int i;
759 
760  av_fast_padded_malloc(&ctx->pktbuf, &ctx->pktbuf_size, len);
761  if (!ctx->pktbuf) {
762  av_log(ctx, AV_LOG_WARNING, "Insufficient Memory of %d truncated to %d\n", len, ctx->pktbuf_size);
763  return AVERROR(ENOMEM);
764  }
765  memcpy(ctx->pktbuf, avpkt->data, len);
766  bptr = ctx->pktbuf;
767 
768  for (i = 0; i < len; i += 3) {
769  uint8_t cc_type = *(bptr + i) & 3;
770  if (validate_cc_data_pair(bptr + i))
771  continue;
772  /* ignoring data field 1 */
773  if(cc_type == 1)
774  continue;
775  else
776  process_cc608(ctx, start_time, *(bptr + i + 1) & 0x7f, *(bptr + i + 2) & 0x7f);
777 
778  if (!ctx->buffer_changed)
779  continue;
780  ctx->buffer_changed = 0;
781 
782  if (*ctx->buffer.str || ctx->real_time)
783  {
784  ff_dlog(ctx, "cdp writing data (%s)\n",ctx->buffer.str);
785  ret = ff_ass_add_rect(sub, ctx->buffer.str, ctx->readorder++, 0, NULL, NULL);
786  if (ret < 0)
787  return ret;
788  sub->pts = ctx->start_time;
789  if (!ctx->real_time)
791  AV_TIME_BASE_Q, ms_tb);
792  else
793  sub->end_display_time = -1;
794  ctx->buffer_changed = 0;
795  ctx->last_real_time = sub->pts;
796  ctx->screen_touched = 0;
797  }
798  }
799 
800  if (ctx->real_time && ctx->screen_touched &&
801  sub->pts > ctx->last_real_time + av_rescale_q(200, ms_tb, AV_TIME_BASE_Q)) {
802  ctx->last_real_time = sub->pts;
803  ctx->screen_touched = 0;
804 
805  capture_screen(ctx);
806  ctx->buffer_changed = 0;
807 
808  ret = ff_ass_add_rect(sub, ctx->buffer.str, ctx->readorder++, 0, NULL, NULL);
809  if (ret < 0)
810  return ret;
811  sub->end_display_time = -1;
812  }
813 
814  *got_sub = sub->num_rects > 0;
815  return ret;
816 }
817 
818 #define OFFSET(x) offsetof(CCaptionSubContext, x)
819 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
820 static const AVOption options[] = {
821  { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
822  {NULL}
823 };
824 
825 static const AVClass ccaption_dec_class = {
826  .class_name = "Closed caption Decoder",
827  .item_name = av_default_item_name,
828  .option = options,
829  .version = LIBAVUTIL_VERSION_INT,
830 };
831 
833  .name = "cc_dec",
834  .long_name = NULL_IF_CONFIG_SMALL("Closed Caption (EIA-608 / CEA-708)"),
835  .type = AVMEDIA_TYPE_SUBTITLE,
836  .id = AV_CODEC_ID_EIA_608,
837  .priv_data_size = sizeof(CCaptionSubContext),
838  .init = init_decoder,
839  .close = close_decoder,
840  .flush = flush_decoder,
841  .decode = decode,
842  .priv_class = &ccaption_dec_class,
843 };
static void handle_edm(CCaptionSubContext *ctx, int64_t pts)
Definition: ccaption_dec.c:582
#define NULL
Definition: coverity.c:32
#define OFFSET(x)
Definition: ccaption_dec.c:818
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
static void process_cc608(CCaptionSubContext *ctx, int64_t pts, uint8_t hi, uint8_t lo)
Definition: ccaption_dec.c:661
AVOption.
Definition: opt.h:246
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:101
static void flush(AVCodecContext *avctx)
enum cc_mode mode
Definition: ccaption_dec.c:241
static void handle_pac(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
Definition: ccaption_dec.c:552
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
int ff_ass_subtitle_header(AVCodecContext *avctx, const char *font, int font_size, int color, int back_color, int bold, int italic, int underline, int border_style, int alignment)
Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS.
Definition: ass.c:29
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
int size
Definition: avcodec.h:1401
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:193
#define SD
Definition: ccaption_dec.c:819
unsigned num_rects
Definition: avcodec.h:3792
void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size)
Same behaviour av_fast_malloc but the buffer has additional AV_INPUT_BUFFER_PADDING_SIZE at the end w...
Definition: utils.c:123
int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, int readorder, int layer, const char *style, const char *speaker)
Add an ASS dialog to a subtitle.
Definition: ass.c:101
static struct Screen * get_writing_screen(CCaptionSubContext *ctx)
Definition: ccaption_dec.c:384
uint8_t colors[SCREEN_ROWS][SCREEN_COLUMNS+1]
Definition: ccaption_dec.c:217
static void handle_delete_end_of_row(CCaptionSubContext *ctx, char hi, char lo)
Definition: ccaption_dec.c:615
AVCodec.
Definition: avcodec.h:3351
static void handle_char(CCaptionSubContext *ctx, char hi, char lo, int64_t pts)
Definition: ccaption_dec.c:621
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
#define CHECK_FLAG(var, val)
Definition: ccaption_dec.c:31
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
static int64_t start_time
Definition: ffplay.c:327
#define ASS_DEFAULT_ALIGNMENT
Definition: ass.h:42
uint8_t
#define av_cold
Definition: attributes.h:82
AVOptions.
cc_mode
Definition: ccaption_dec.c:39
static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch)
Definition: ccaption_dec.c:317
uint8_t * data
Definition: avcodec.h:1400
#define ff_dlog(a,...)
static int reap_screen(CCaptionSubContext *ctx, int64_t pts)
Definition: ccaption_dec.c:529
static void handle_eoc(CCaptionSubContext *ctx, int64_t pts)
Definition: ccaption_dec.c:599
#define AV_CODEC_FLAG2_RO_FLUSH_NOOP
Do not reset ASS ReadOrder field on flush (subtitles decoding)
Definition: avcodec.h:938
#define av_log(a,...)
#define ASS_DEFAULT_PLAYRESY
Definition: ass.h:29
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
#define SCREEN_ROWS
Definition: ccaption_dec.c:26
#define ASS_DEFAULT_BACK_COLOR
Definition: ass.h:38
#define ASS_DEFAULT_UNDERLINE
Definition: ass.h:41
#define AV_BPRINT_SIZE_UNLIMITED
#define AVERROR(e)
Definition: error.h:43
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
const char * name
Name of the codec implementation.
Definition: avcodec.h:3358
AVCodec ff_ccaption_decoder
Definition: ccaption_dec.c:832
uint8_t fonts[SCREEN_ROWS][SCREEN_COLUMNS+1]
Definition: ccaption_dec.c:218
int16_t row_used
Definition: ccaption_dec.c:225
#define ASS_DEFAULT_FONT_SIZE
Definition: ass.h:36
uint32_t end_display_time
Definition: avcodec.h:3791
int64_t pts
Same as packet pts, in AV_TIME_BASE.
Definition: avcodec.h:3794
#define SET_FLAG(var, val)
Definition: ccaption_dec.c:29
static av_cold int init_decoder(AVCodecContext *avctx)
Definition: ccaption_dec.c:256
#define SCREEN_COLUMNS
Definition: ccaption_dec.c:27
#define FFMIN(a, b)
Definition: common.h:96
cc_charset
Definition: ccaption_dec.c:66
static int capture_screen(CCaptionSubContext *ctx)
Definition: ccaption_dec.c:435
AVFormatContext * ctx
Definition: movenc.c:48
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:185
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:62
Libavcodec external API header.
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
Definition: avutil.h:260
static const unsigned char pac2_attribs[32][3]
Definition: ccaption_dec.c:176
main external API structure.
Definition: avcodec.h:1488
static void flush_decoder(AVCodecContext *avctx)
Definition: ccaption_dec.c:291
uint8_t charsets[SCREEN_ROWS][SCREEN_COLUMNS+1]
Definition: ccaption_dec.c:216
uint8_t characters[SCREEN_ROWS][SCREEN_COLUMNS+1]
Definition: ccaption_dec.c:215
Describe the class of an AVClass context structure.
Definition: log.h:67
int index
Definition: gxfenc.c:89
Rational number (pair of numerator and denominator).
Definition: rational.h:58
static int64_t pts
static const AVClass ccaption_dec_class
Definition: ccaption_dec.c:825
static const AVRational ms_tb
Definition: ccaption_dec.c:33
cc_font
Definition: ccaption_dec.c:59
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
Definition: bprint.c:227
#define av_parity
Definition: intmath.h:158
static void roll_up(CCaptionSubContext *ctx)
Definition: ccaption_dec.c:400
static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avpkt)
Definition: ccaption_dec.c:750
int
#define ASS_DEFAULT_COLOR
Definition: ass.h:37
static const char * charset_overrides[4][128]
Definition: ccaption_dec.c:73
static const AVOption options[]
Definition: ccaption_dec.c:820
#define UNSET_FLAG(var, val)
Definition: ccaption_dec.c:30
cc_color_code
Definition: ccaption_dec.c:46
void * priv_data
Definition: avcodec.h:1515
#define ASS_DEFAULT_ITALIC
Definition: ass.h:40
int len
#define ASS_DEFAULT_BOLD
Definition: ass.h:39
static void handle_textattr(CCaptionSubContext *ctx, uint8_t hi, uint8_t lo)
Definition: ccaption_dec.c:537
int flags2
AV_CODEC_FLAG2_*.
Definition: avcodec.h:1575
static const struct twinvq_data tab
#define av_freep(p)
static av_cold int close_decoder(AVCodecContext *avctx)
Definition: ccaption_dec.c:282
#define ASS_DEFAULT_PLAYRESX
Definition: ass.h:28
static int validate_cc_data_pair(uint8_t *cc_data_pair)
This function after validating parity bit, also remove it from data pair.
Definition: ccaption_dec.c:350
This structure stores compressed data.
Definition: avcodec.h:1377
struct Screen screen[2]
Definition: ccaption_dec.c:231
uint8_t pi<< 24) CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_U8,(uint64_t)((*(constuint8_t *) pi-0x80U))<< 56) CONV_FUNC(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8,(*(constuint8_t *) pi-0x80)*(1.0f/(1<< 7))) CONV_FUNC(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8,(*(constuint8_t *) pi-0x80)*(1.0/(1<< 7))) CONV_FUNC(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16,(*(constint16_t *) pi >>8)+0x80) CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_S16,(uint64_t)(*(constint16_t *) pi)<< 48) CONV_FUNC(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16,*(constint16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16,*(constint16_t *) pi *(1.0/(1<< 15))) CONV_FUNC(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32,(*(constint32_t *) pi >>24)+0x80) CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_S32,(uint64_t)(*(constint32_t *) pi)<< 32) CONV_FUNC(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32,*(constint32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32,*(constint32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S64,(*(constint64_t *) pi >>56)+0x80) CONV_FUNC(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S64,*(constint64_t *) pi *(1.0f/(INT64_C(1)<< 63))) CONV_FUNC(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S64,*(constint64_t *) pi *(1.0/(INT64_C(1)<< 63))) CONV_FUNC(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, av_clip_uint8(lrintf(*(constfloat *) pi *(1<< 7))+0x80)) CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, av_clip_int16(lrintf(*(constfloat *) pi *(1<< 15)))) CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, av_clipl_int32(llrintf(*(constfloat *) pi *(1U<< 31)))) CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_FLT, llrintf(*(constfloat *) pi *(INT64_C(1)<< 63))) CONV_FUNC(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, av_clip_uint8(lrint(*(constdouble *) pi *(1<< 7))+0x80)) CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, av_clip_int16(lrint(*(constdouble *) pi *(1<< 15)))) CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, av_clipl_int32(llrint(*(constdouble *) pi *(1U<< 31)))) CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_DBL, llrint(*(constdouble *) pi *(INT64_C(1)<< 63)))#defineFMT_PAIR_FUNC(out, in) staticconv_func_type *constfmt_pair_to_conv_functions[AV_SAMPLE_FMT_NB *AV_SAMPLE_FMT_NB]={FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_U8), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_U8), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_U8), FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_U8), FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_U8), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_U8), FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S16), FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16), FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S16), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_S16), FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S32), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32), FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S32), FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S32), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_S32), FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_FLT), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT), FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT), FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_FLT), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_FLT), FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_DBL), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_DBL), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_DBL), FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL), FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_DBL), FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S64), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S64), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S64), FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S64), FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S64), FMT_PAIR_FUNC(AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_S64),};staticvoidcpy1(uint8_t **dst, constuint8_t **src, intlen){memcpy(*dst,*src, len);}staticvoidcpy2(uint8_t **dst, constuint8_t **src, intlen){memcpy(*dst,*src, 2 *len);}staticvoidcpy4(uint8_t **dst, constuint8_t **src, intlen){memcpy(*dst,*src, 4 *len);}staticvoidcpy8(uint8_t **dst, constuint8_t **src, intlen){memcpy(*dst,*src, 8 *len);}AudioConvert *swri_audio_convert_alloc(enumAVSampleFormatout_fmt, enumAVSampleFormatin_fmt, intchannels, constint *ch_map, intflags){AudioConvert *ctx;conv_func_type *f=fmt_pair_to_conv_functions[av_get_packed_sample_fmt(out_fmt)+AV_SAMPLE_FMT_NB *av_get_packed_sample_fmt(in_fmt)];if(!f) returnNULL;ctx=av_mallocz(sizeof(*ctx));if(!ctx) returnNULL;if(channels==1){in_fmt=av_get_planar_sample_fmt(in_fmt);out_fmt=av_get_planar_sample_fmt(out_fmt);}ctx->channels=channels;ctx->conv_f=f;ctx->ch_map=ch_map;if(in_fmt==AV_SAMPLE_FMT_U8||in_fmt==AV_SAMPLE_FMT_U8P) memset(ctx->silence, 0x80, sizeof(ctx->silence));if(out_fmt==in_fmt &&!ch_map){switch(av_get_bytes_per_sample(in_fmt)){case1:ctx->simd_f=cpy1;break;case2:ctx->simd_f=cpy2;break;case4:ctx->simd_f=cpy4;break;case8:ctx->simd_f=cpy8;break;}}if(HAVE_X86ASM &&1) swri_audio_convert_init_x86(ctx, out_fmt, in_fmt, channels);if(ARCH_ARM) swri_audio_convert_init_arm(ctx, out_fmt, in_fmt, channels);if(ARCH_AARCH64) swri_audio_convert_init_aarch64(ctx, out_fmt, in_fmt, channels);returnctx;}voidswri_audio_convert_free(AudioConvert **ctx){av_freep(ctx);}intswri_audio_convert(AudioConvert *ctx, AudioData *out, AudioData *in, intlen){intch;intoff=0;constintos=(out->planar?1:out->ch_count)*out->bps;unsignedmisaligned=0;av_assert0(ctx->channels==out->ch_count);if(ctx->in_simd_align_mask){intplanes=in->planar?in->ch_count:1;unsignedm=0;for(ch=0;ch< planes;ch++) m|=(intptr_t) in->ch[ch];misaligned|=m &ctx->in_simd_align_mask;}if(ctx->out_simd_align_mask){intplanes=out->planar?out->ch_count:1;unsignedm=0;for(ch=0;ch< planes;ch++) m|=(intptr_t) out->ch[ch];misaligned|=m &ctx->out_simd_align_mask;}if(ctx->simd_f &&!ctx->ch_map &&!misaligned){off=len &~15;av_assert1(off >=0);av_assert1(off<=len);av_assert2(ctx->channels==SWR_CH_MAX||!in->ch[ctx->channels]);if(off >0){if(out->planar==in->planar){intplanes=out->planar?out->ch_count:1;for(ch=0;ch< planes;ch++){ctx->simd_f(out-> ch ch
Definition: audioconvert.c:56