提交 a8517f36 编写于 作者: M Matthew Szatmary 提交者: jp9000

libobs: Update libcaption library

(This commit also modifies deps/libcaption)

Closes obsproject/obs-studio#1354
上级 f84e490e
......@@ -12,19 +12,19 @@ set(CAPTION_SOURCES
src/utf8.c
src/srt.c
src/scc.c
src/avc.c
src/xds.c
src/mpeg.c
src/cea708.c
src/xds.c
src/caption.c
src/eia608_charmap.c
src/eia608_from_utf8.c
src/eia608.c
)
set(CAPTION_HEADERS
caption/utf8.h
caption/sei.h
caption/scc.h
caption/avc.c
caption/mpeg.h
caption/cea708.h
caption/eia608.h
caption/caption.h
......
The MIT License
Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.
Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
# version
v0.6
v0.8
Matthew Szatmary m3u8@twitch.tv / matt@szatmary.org
# libcaption
libcaption is a small library written in C to aid in the creating and parsing of closed caption data for use in the twitch player, open sourced to use within community developed broadcast tools. To maintain consistency across platforms libcaption aims to implement a subset of EIA608, CEA708 as supported by the Apple iOS platform.
libcaption is a library written in C to aid in the creating and parsing of closed caption data, open sourced under the MIT license to use within community developed broadcast tools. To maintain consistency across platforms libcaption aims to implement a subset of EIA608, CEA708 as supported by the Apple iOS platform.
608 support is currently limited to encoding and decoding the necessary control and preamble codes as well as support for the Basic North American, Special North American and Extended Western European character sets.
......@@ -23,10 +23,10 @@ H.264 utility functions are limited to wrapping the 708 payload into a SEI NALU.
|BNA|P|Q|R|S|T|U|V|W|X|Y|Z|[|é|]|í|ó|
|BNA|ú|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|
|BNA|p|q|r|s|t|u|v|w|x|y|z|ç|÷|Ñ|ñ|█|
|SNA|®|°|½|¿|™|¢|£|♪|à| |è|â|ê|î|ô|û|
|SNA|®|°|½|¿|™|¢|£|♪|à| |è|â|ê|î|ô|û|
|WES|Á|É|Ó|Ú|Ü|ü|‘|¡|*|'|—|©|℠|•|“|”|
|WEF|À|Â|Ç|È|Ê|Ë|ë|Î|Ï|ï|Ô|Ù|ù|Û|«|»|
|WEP|Ã|ã|Í|Ì|ì|Ò|ò|Õ|õ|{|}|\\|^|_|\||~|
|WEP|Ã|ã|Í|Ì|ì|Ò|ò|Õ|õ|{|}|\|^|_|||~|
|WEG|Ä|ä|Ö|ö|ß|¥|¤|¦|Å|å|Ø|ø|┌|┐|└|┘|
* BNA = Basic North American character set
......@@ -36,64 +36,16 @@ H.264 utility functions are limited to wrapping the 708 payload into a SEI NALU.
* WEP = Extended Western European character set : Portuguese
* WEG = Extended Western European character set : German/Danish
------
eia608_screen_t is the default internal representation. `screens` can be
converted to and from SEI messages 608/708 buffers and SRT files
## JSON format
A JSON rendered format is provided for convenience of integration. The JSON
format is as follows:
```
{
"format": "eia608",
"mode": "pop-on",
"roll-up": 0,
"data": [
{ "row":0, "col":0, "char":"A", "style":"white" },
{ "row":0, "col":1, "char":"B", "style":"white" },
// ...
]
}
```
### `format`
The only current valid value is `"eia608"`. This field exists for
future expansion. Any values other than `"eia608"`, and the object should be
ignored for now.
### `mode`
Defines the method by which screen contents are adjusted in response to
new data, such as content in excess of the normal grid size.
Possible modes are:
`"clear"`: Generated when a screen is initialized, but not entered a "loading"
state. `"clear"` mode occurs frequently when the display is to be erased.
`"pop-on"`: Normally used for pre recorded content where. A pop-on screen should
completely replace all previous screen contents.
`"paint-on"`: Normally used for live content. In this mode, new characters
appear one or two at a time. The `data` array does include the complete state of
the screen. In practice, it may not be different that `"pop-on"`.
An internal "loading" mode also exists.
### `roll-up`
Normally used for live content such as news broadcasts. Text is moved up the
screen as new rows appear. The number of rows to be displayed is also encoded in
the JSON key `"roll-up"` where value is an integer of `0`, `1`, `2`, `3`, or `4`.
Like `"paint-on"`, the `data` array does include the complete state of the screen.
### `data`
Contains a object for every character to be displayed. The screen is a grid of
15 rows and 32 columns. The position of the character is available in the `row`
and `col` fields. The character itself is in the `char` field. The full
character set is available in the document, but any valid UTF-8 should be
supported for future. The style field will contain one of the following values:
`"white"`, `"green"`, `"blue"`, `"cyan"`, `"red"`, `"yellow"`, `"magenta"`, `"italics"`
Characters with the `"italics"` style should be displayed in white.
## Limitations
Current B-frame support for caption creation is minimal. libcaption ensures no re-ordering of captions is required
on playback.
## Build Directions
# Mac Os/Linux
Install build dependencies (git, cmake, a compiler such as xcode, gcc or clang and optionally re2c and ffmpeg)
* run `cmake . && make`
* or to compile without re2c `cmake -DENABLE_RE2C=OFF . && make`
* finally `sudo make install` to install
# Windows
I have never tested libcaption in windows. It is written in pure C with no dependencies,
so there is no reason it would not work.
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -23,9 +23,13 @@
/**********************************************************************************************/
#ifndef LIBCAPTION_H
#define LIBCAPTION_H
#ifdef __cplusplus
extern "C" {
#endif
#include "eia608.h"
#include "utf8.h"
#include "xds.h"
#include "eia608.h"
// ssize_t is POSIX and does not exist on Windows
#if defined(_MSC_VER)
......@@ -37,16 +41,15 @@ typedef signed int ssize_t;
#endif
typedef enum {
LIBCAPTION_OK = 1,
LIBCAPTION_ERROR = 0,
LIBCAPTION_OK = 1,
LIBCAPTION_READY = 2
} libcaption_stauts_t;
/*! \brief
\param
*/
static inline libcaption_stauts_t libcaption_status_update (libcaption_stauts_t old_stat, libcaption_stauts_t new_stat) { return (LIBCAPTION_ERROR == old_stat || LIBCAPTION_ERROR == new_stat) ? LIBCAPTION_ERROR : (LIBCAPTION_READY == old_stat) ? LIBCAPTION_READY : new_stat; }
static inline libcaption_stauts_t libcaption_status_update(libcaption_stauts_t old_stat, libcaption_stauts_t new_stat)
{
return (LIBCAPTION_ERROR == old_stat || LIBCAPTION_ERROR == new_stat) ? LIBCAPTION_ERROR : (LIBCAPTION_READY == old_stat) ? LIBCAPTION_READY : new_stat;
}
#define SCREEN_ROWS 15
#define SCREEN_COLS 32
......@@ -54,47 +57,54 @@ static inline libcaption_stauts_t libcaption_status_update (libcaption_stauts_t
typedef struct {
unsigned int uln : 1; //< underline
unsigned int sty : 3; //< style
utf8_char_t data[5]; //< 4 byte utf8 values plus null term
} caption_frame_cell_t;
utf8_char_t data[5]; //< 4 byte utf8 values plus null term
} caption_frame_cell_t;
typedef struct {
caption_frame_cell_t cell[SCREEN_ROWS][SCREEN_COLS];
} caption_frame_buffer_t;
typedef struct {
typedef struct {
unsigned int uln : 1; //< underline
unsigned int sty : 3; //< style
unsigned int mod : 3; //< current mode
unsigned int rup : 2; //< roll-up line count minus 1
uint16_t row, col, cc_data;
int8_t row, col;
uint16_t cc_data;
} caption_frame_state_t;
// timestamp and duration are in seconds
typedef struct {
double timestamp;
double duration;
xds_t xds;
caption_frame_state_t state;
caption_frame_buffer_t front;
caption_frame_buffer_t back;
caption_frame_buffer_t* write;
libcaption_stauts_t status;
} caption_frame_t;
// typedef enum {
// eia608_paint_on = 0,
// eia608_pop_on = 1,
// eia608_rollup_2 = 2,
// eia608_rollup_3 = 3,
// eia608_rollup_4 = 4,
// } eia608_display_mode_t;
// eia608_display_mode_t caption_frame_mode (caption_frame_t* frame);
/*!
\brief Initializes an allocated caption_frame_t instance
\param frame Pointer to prealocated caption_frame_t object
*/
void caption_frame_init (caption_frame_t* frame);
void caption_frame_init(caption_frame_t* frame);
/*! \brief
\param
*/
static inline int caption_frame_popon(caption_frame_t* frame) { return (frame->write == &frame->back) ? 1 : 0; }
/*! \brief
\param
*/
static inline int caption_frame_painton(caption_frame_t* frame) { return (frame->write == &frame->front) ? 1 : 0; }
/*! \brief
\param
*/
const static int _caption_frame_rollup[] = { 0, 2, 3, 4 };
static inline int caption_frame_rollup(caption_frame_t* frame) { return _caption_frame_rollup[frame->state.rup]; }
/*! \brief
\param
*/
static inline double caption_frame_timestamp(caption_frame_t* frame) { return frame->timestamp; }
/*! \brief Writes a single charcter to a caption_frame_t object
\param frame A pointer to an allocted and initialized caption_frame_t object
\param row Row position to write charcter, must be between 0 and SCREEN_ROWS-1
......@@ -103,39 +113,32 @@ void caption_frame_init (caption_frame_t* frame);
\param underline Set underline attribute, 0 = off any other value = on
\param c pointer to a single valid utf8 charcter. Bytes are automatically determined, and a NULL terminator is not required
*/
int caption_frame_write_char (caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const utf8_char_t* c);
int caption_frame_write_char(caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const utf8_char_t* c);
/*! \brief
\param
*/
const utf8_char_t* caption_frame_read_char (caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline);
const utf8_char_t* caption_frame_read_char(caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline);
/*! \brief
\param
*/
libcaption_stauts_t caption_frame_decode(caption_frame_t* frame, uint16_t cc_data, double timestamp);
/*! \brief
\param
*/
libcaption_stauts_t caption_frame_decode (caption_frame_t* frame, uint16_t cc_data, double timestamp);
/*! \brief
\param
*/
int caption_frame_from_text (caption_frame_t* frame, const utf8_char_t* data);
int caption_frame_from_text(caption_frame_t* frame, const utf8_char_t* data);
/*! \brief
\param
*/
#define CAPTION_FRAME_TEXT_BYTES (((2+SCREEN_ROWS)*SCREEN_COLS*4)+1)
void caption_frame_to_text (caption_frame_t* frame, utf8_char_t* data);
#define CAPTION_FRAME_TEXT_BYTES (4 * ((SCREEN_COLS + 2) * SCREEN_ROWS) + 1)
size_t caption_frame_to_text(caption_frame_t* frame, utf8_char_t* data);
/*! \brief
\param
*/
#define CAPTION_FRAME_DUMP_BUF_SIZE 4096
size_t caption_frame_dump_buffer (caption_frame_t* frame, utf8_char_t* buf);
void caption_frame_dump (caption_frame_t* frame);
/*! \brief
\param
*/
#define CAPTION_FRAME_JSON_BUF_SIZE 32768
size_t caption_frame_json (caption_frame_t* frame, utf8_char_t* buf);
#define CAPTION_FRAME_DUMP_BUF_SIZE 8192
size_t caption_frame_dump_buffer(caption_frame_t* frame, utf8_char_t* buf);
void caption_frame_dump(caption_frame_t* frame);
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -23,6 +23,9 @@
/**********************************************************************************************/
#ifndef LIBCAPTION_CEA708_H
#define LIBCAPTION_CEA708_H
#ifdef __cplusplus
extern "C" {
#endif
#include "caption.h"
#define CEA608_MAX_SIZE (255)
......@@ -54,15 +57,15 @@ typedef struct {
/*! \brief
\param
*/
cc_data_t cea708_encode_cc_data (int cc_valid, cea708_cc_type_t type, uint16_t cc_data);
cc_data_t cea708_encode_cc_data(int cc_valid, cea708_cc_type_t type, uint16_t cc_data);
/*! \brief
\param
*/
int cea708_cc_count (user_data_t* data);
int cea708_cc_count(user_data_t* data);
/*! \brief
\param
*/
uint16_t cea708_cc_data (user_data_t* data, int index, int* valid, cea708_cc_type_t* type);
uint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_cc_type_t* type);
////////////////////////////////////////////////////////////////////////////////
typedef enum {
......@@ -78,33 +81,44 @@ typedef struct {
itu_t_t35_country_code_t country;
itu_t_t35_provider_code_t provider;
uint32_t user_identifier;
uint8_t atsc1_data_user_data_type_code;
uint8_t user_data_type_code;
uint8_t directv_user_data_length;
user_data_t user_data;
double timestamp;
} cea708_t;
const static uint32_t GA94 = (('G' << 24) | ('A' << 16) | ('9' << 8) | '4');
const static uint32_t DTG1 = (('D' << 24) | ('T' << 16) | ('G' << 8) | '1');
/*! \brief
\param
*/
int cea708_init(cea708_t* cea708, double timestamp); // will confgure using HLS compatiable defaults
/*! \brief
\param
*/
int cea708_init (cea708_t* cea708); // will confgure using HLS compatiable defaults
libcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708);
/*! \brief
\param
*/
int cea708_parse (uint8_t* data, size_t size, cea708_t* cea708);
libcaption_stauts_t cea708_parse_h262(const uint8_t* data, size_t size, cea708_t* cea708);
/*! \brief
\param
*/
libcaption_stauts_t cea708_to_caption_frame (caption_frame_t* frame, cea708_t* cea708, double pts);
libcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708);
/*! \brief
\param
*/
int cea708_add_cc_data (cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data);
int cea708_add_cc_data(cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data);
/*! \brief
\param
*/
int cea708_render (cea708_t* cea708, uint8_t* data, size_t size);
int cea708_render(cea708_t* cea708, uint8_t* data, size_t size);
/*! \brief
\param
*/
void cea708_dump (cea708_t* cea708);
void cea708_dump(cea708_t* cea708);
#ifdef __cplusplus
}
#endif
#endif
#ifndef LIBCAPTION_CEA708_H
#define LIBCAPTION_CEA708_H
#ifdef __cplusplus
extern "C" {
#endif
////////////////////////////////////////////////////////////////////////////////
struct dtvcc_packet_t {
unsigned int sequence_number;
unsigned int packet_size;
unsigned int serice_number;
};
#defing DVTCC_SERVICE_NUMBER_UNKNOWN
// static inline size_t dvtvcc_packet_size_bytes(const struct dtvcc_packet_t *dvtcc) { return dvtcc->packet_size*2-1;}
////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -23,18 +23,20 @@
/**********************************************************************************************/
#ifndef LIBCAPTION_EIA608_H
#define LIBCAPTION_EIA608_H
#ifdef __cplusplus
extern "C" {
#endif
#include "utf8.h"
#include "eia608_charmap.h"
#include "utf8.h"
////////////////////////////////////////////////////////////////////////////////
// Parity
#define EIA608_BX(B,X) (((B)>>X)&0x01)
#define EIA608_BP(B) ((B)&0x7F) | ((EIA608_BX((B),0)^EIA608_BX(B,1)^EIA608_BX((B),2)^EIA608_BX((B),3)^EIA608_BX((B),4)^EIA608_BX((B),5)^EIA608_BX((B),6)^(0x01))<<7)
#define EIA608_B2(B) EIA608_BP((B)+0), EIA608_BP((B)+1), EIA608_BP((B)+2), EIA608_BP((B)+3), EIA608_BP((B)+4), EIA608_BP((B)+5), EIA608_BP((B)+6), EIA608_BP((B)+7)
#define EIA608_B1(B) EIA608_B2((B)+0), EIA608_B2((B)+8), EIA608_B2((B)+16), EIA608_B2((B)+24), EIA608_B2((B)+32), EIA608_B2((B)+40), EIA608_B2((B)+48), EIA608_B2((B)+56)
#define EIA608_BX(B, X) (((B) << (X)) & 0x80)
#define EIA608_BP(B) ((B)&0x7F) | (0x80 ^ EIA608_BX((B), 1) ^ EIA608_BX((B), 2) ^ EIA608_BX((B), 3) ^ EIA608_BX((B), 4) ^ EIA608_BX((B), 5) ^ EIA608_BX((B), 6) ^ EIA608_BX((B), 7))
#define EIA608_B2(B) EIA608_BP((B) + 0), EIA608_BP((B) + 1), EIA608_BP((B) + 2), EIA608_BP((B) + 3), EIA608_BP((B) + 4), EIA608_BP((B) + 5), EIA608_BP((B) + 6), EIA608_BP((B) + 7)
#define EIA608_B1(B) EIA608_B2((B) + 0), EIA608_B2((B) + 8), EIA608_B2((B) + 16), EIA608_B2((B) + 24), EIA608_B2((B) + 32), EIA608_B2((B) + 40), EIA608_B2((B) + 48), EIA608_B2((B) + 56)
static const uint8_t eia608_parity_table[] = { EIA608_B1 (0), EIA608_B1 (64) };
extern const char* eia608_mode_map[];
static const uint8_t eia608_parity_table[] = { EIA608_B1(0), EIA608_B1(64) };
extern const char* eia608_style_map[];
#ifdef _MSC_VER
......@@ -46,66 +48,66 @@ extern const char* eia608_style_map[];
/*! \brief
\param
*/
static inline uint8_t eia608_parity_byte (uint8_t cc_data) { return eia608_parity_table[0x7F & cc_data]; }
static inline uint8_t eia608_parity_byte(uint8_t cc_data) { return eia608_parity_table[0x7F & cc_data]; }
/*! \brief
\param
*/
static inline uint16_t eia608_parity_word (uint16_t cc_data) { return (uint16_t) ( (eia608_parity_byte ( (uint8_t) (cc_data>>8)) <<8) | eia608_parity_byte ( (uint8_t) cc_data)); }
static inline uint16_t eia608_parity_word(uint16_t cc_data) { return (uint16_t)((eia608_parity_byte((uint8_t)(cc_data >> 8)) << 8) | eia608_parity_byte((uint8_t)cc_data)); }
/*! \brief
\param
*/
static inline uint16_t eia608_parity (uint16_t cc_data) { return eia608_parity_word (cc_data); }
static inline uint16_t eia608_parity(uint16_t cc_data) { return eia608_parity_word(cc_data); }
/*! \brief
\param
*/
static inline int eia608_parity_varify (uint16_t cc_data) { return eia608_parity_word (cc_data) == cc_data ? 1 : 0; }
static inline int eia608_parity_varify(uint16_t cc_data) { return eia608_parity_word(cc_data) == cc_data ? 1 : 0; }
/*! \brief
\param
*/
static inline int eia608_parity_strip (uint16_t cc_data) { return cc_data & 0x7F7F; }
static inline int eia608_parity_strip(uint16_t cc_data) { return cc_data & 0x7F7F; }
/*! \brief
\param
*/
static inline int eia608_test_second_channel_bit (uint16_t cc_data) { return (cc_data & 0x0800); }
static inline int eia608_test_second_channel_bit(uint16_t cc_data) { return (cc_data & 0x0800); }
////////////////////////////////////////////////////////////////////////////////
// cc_data types
/*! \brief
\param
*/
static inline int eia608_is_basicna (uint16_t cc_data) { return 0x0000 != (0x6000 & cc_data); /*&& 0x1F00 < (0x7F00 & cc_data);*/ }
static inline int eia608_is_basicna(uint16_t cc_data) { return 0x0000 != (0x6000 & cc_data); /*&& 0x1F00 < (0x7F00 & cc_data);*/ }
/*! \brief
\param
*/
static inline int eia608_is_preamble (uint16_t cc_data) { return 0x1040 == (0x7040 & cc_data); }
static inline int eia608_is_preamble(uint16_t cc_data) { return 0x1040 == (0x7040 & cc_data); }
/*! \brief
\param
*/
static inline int eia608_is_midrowchange (uint16_t cc_data) { return 0x1120 == (0x7770 & cc_data); }
static inline int eia608_is_midrowchange(uint16_t cc_data) { return 0x1120 == (0x7770 & cc_data); }
/*! \brief
\param
*/
static inline int eia608_is_specialna (uint16_t cc_data) { return 0x1130 == (0x7770 & cc_data); }
static inline int eia608_is_specialna(uint16_t cc_data) { return 0x1130 == (0x7770 & cc_data); }
/*! \brief
\param
*/
static inline int eia608_is_xds (uint16_t cc_data) { return 0x0000 == (0x7070 & cc_data) && 0x0000 != (0x0F0F & cc_data); }
static inline int eia608_is_xds(uint16_t cc_data) { return 0x0000 == (0x7070 & cc_data) && 0x0000 != (0x0F0F & cc_data); }
/*! \brief
\param
*/
static inline int eia608_is_westeu (uint16_t cc_data) { return 0x1220 == (0x7660 & cc_data); }
static inline int eia608_is_westeu(uint16_t cc_data) { return 0x1220 == (0x7660 & cc_data); }
/*! \brief
\param
*/
static inline int eia608_is_control (uint16_t cc_data) { return 0x1420 == (0x7670 & cc_data) || 0x1720 == (0x7770 & cc_data); }
static inline int eia608_is_control(uint16_t cc_data) { return 0x1420 == (0x7670 & cc_data) || 0x1720 == (0x7770 & cc_data); }
/*! \brief
\param
*/
static inline int eia608_is_norpak (uint16_t cc_data) { return 0x1724 == (0x777C & cc_data) || 0x1728 == (0x777C & cc_data); }
static inline int eia608_is_norpak(uint16_t cc_data) { return 0x1724 == (0x777C & cc_data) || 0x1728 == (0x777C & cc_data); }
/*! \brief
\param
*/
static inline int eia608_is_padding (uint16_t cc_data) { return 0x8080 == cc_data; }
static inline int eia608_is_padding(uint16_t cc_data) { return 0x8080 == cc_data; }
////////////////////////////////////////////////////////////////////////////////
// preamble
......@@ -123,23 +125,26 @@ typedef enum {
/*! \brief
\param
*/
int eia608_parse_preamble (uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline);
int eia608_parse_preamble(uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline);
/*! \brief
\param
*/
int eia608_parse_midrowchange (uint16_t cc_data, int* chan, eia608_style_t* style, int* underline);
int eia608_parse_midrowchange(uint16_t cc_data, int* chan, eia608_style_t* style, int* underline);
/*! \brief
\param
*/
uint16_t eia608_row_column_pramble (int row, int col, int chan, int underline);
uint16_t eia608_row_column_pramble(int row, int col, int chan, int underline);
/*! \brief
\param
*/
uint16_t eia608_row_style_pramble (int row, eia608_style_t style, int chan, int underline);
uint16_t eia608_row_style_pramble(int row, int chan, eia608_style_t style, int underline);
/*! \brief
\param
*/
uint16_t eia608_midrow_change(int chan, eia608_style_t style, int underline);
////////////////////////////////////////////////////////////////////////////////
// control command
typedef enum { // yes, no?
typedef enum {
eia608_tab_offset_0 = 0x1720,
eia608_tab_offset_1 = 0x1721,
eia608_tab_offset_2 = 0x1722,
......@@ -161,46 +166,43 @@ typedef enum { // yes, no?
eia608_control_end_of_caption = 0x142F,
} eia608_control_t;
#define eia608_control_popon eia608_control_resume_caption_loading
#define eia608_control_painton eia608_control_resume_direct_captioning
/*! \brief
\param
*/
uint16_t eia608_control_command (eia608_control_t cmd, int cc);
uint16_t eia608_control_command(eia608_control_t cmd, int cc);
/*! \brief
\param
*/
static inline uint16_t eia608_tab (int size, int cc) { return eia608_control_command ( (eia608_control_t) (eia608_tab_offset_0 | (size&0x0F)),cc); }
static inline uint16_t eia608_tab(int size, int cc) { return eia608_control_command((eia608_control_t)(eia608_tab_offset_0 | (size & 0x0F)), cc); }
/*! \brief
\param
*/
eia608_control_t eia608_parse_control (uint16_t cc_data, int* cc);
eia608_control_t eia608_parse_control(uint16_t cc_data, int* cc);
////////////////////////////////////////////////////////////////////////////////
// text
/*! \brief
\param c
*/
uint16_t eia608_from_utf8_1 (const utf8_char_t* c, int chan);
uint16_t eia608_from_utf8_1(const utf8_char_t* c, int chan);
/*! \brief
\param
*/
uint16_t eia608_from_utf8_2 (const utf8_char_t* c1, const utf8_char_t* c2);
uint16_t eia608_from_utf8_2(const utf8_char_t* c1, const utf8_char_t* c2);
/*! \brief
\param
*/
uint16_t eia608_from_basicna (uint16_t bna1, uint16_t bna2);
uint16_t eia608_from_basicna(uint16_t bna1, uint16_t bna2);
/*! \brief
\param
*/
int eia608_to_utf8 (uint16_t c, int* chan, utf8_char_t* char1, utf8_char_t* char2);
int eia608_to_utf8(uint16_t c, int* chan, utf8_char_t* char1, utf8_char_t* char2);
////////////////////////////////////////////////////////////////////////////////
/*! \brief
\param
*/
void eia608_dump (uint16_t cc_data);
void eia608_dump(uint16_t cc_data);
////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -21,26 +21,56 @@
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#ifndef LIBCAPTION_AVC_H
#define LIBCAPTION_AVC_H
#include "cea708.h"
#ifndef LIBCAPTION_MPEG_H
#define LIBCAPTION_MPEG_H
#ifdef __cplusplus
extern "C" {
#endif
#include "caption.h"
#include "cea708.h"
#include "scc.h"
#include <float.h>
#include <stddef.h>
////////////////////////////////////////////////////////////////////////////////
#define MAX_NALU_SIZE (4*1024*1024)
#define STREAM_TYPE_H262 0x02
#define STREAM_TYPE_H264 0x1B
#define STREAM_TYPE_H265 0x24
#define H262_SEI_PACKET 0xB2
#define H264_SEI_PACKET 0x06
#define H265_SEI_PACKET 0x27 // There is also 0x28
#define MAX_NALU_SIZE (6 * 1024 * 1024)
#define MAX_REFRENCE_FRAMES 64
typedef struct {
size_t size;
uint8_t data[MAX_NALU_SIZE];
} avcnalu_t;
uint8_t data[MAX_NALU_SIZE + 1];
double dts, cts;
libcaption_stauts_t status;
// Priority queue for out of order frame processing
// Should probablly be a linked list
size_t front;
size_t latent;
cea708_t cea708[MAX_REFRENCE_FRAMES];
} mpeg_bitstream_t;
void avcnalu_init (avcnalu_t* nalu);
int avcnalu_parse_annexb (avcnalu_t* nalu, const uint8_t** data, size_t* size);
static inline uint8_t avcnalu_type (avcnalu_t* nalu) { return nalu->data[0] & 0x1F; }
static inline uint8_t* avcnalu_data (avcnalu_t* nalu) { return &nalu->data[0]; }
static inline size_t avcnalu_size (avcnalu_t* nalu) { return nalu->size; }
void mpeg_bitstream_init(mpeg_bitstream_t* packet);
////////////////////////////////////////////////////////////////////////////////
// TODO make convenience functions for flv/mp4
/*! \brief
\param
*/
size_t mpeg_bitstream_parse(mpeg_bitstream_t* packet, caption_frame_t* frame, const uint8_t* data, size_t size, unsigned stream_type, double dts, double cts);
/*! \brief
\param
*/
static inline libcaption_stauts_t mpeg_bitstream_status(mpeg_bitstream_t* packet) { return packet->status; }
/*! \brief
Flushes latent packets caused by out or order frames.
Returns number of latent frames remaining, 0 when complete;
\param
*/
size_t mpeg_bitstream_flush(mpeg_bitstream_t* packet, caption_frame_t* frame);
////////////////////////////////////////////////////////////////////////////////
typedef struct _sei_message_t sei_message_t;
typedef enum {
sei_type_buffering_period = 0,
sei_type_pic_timing = 1,
......@@ -66,10 +96,14 @@ typedef enum {
sei_type_stereo_video_info = 21,
} sei_msgtype_t;
////////////////////////////////////////////////////////////////////////////////
// time in seconds
typedef struct {
double dts;
double cts;
typedef struct _sei_message_t {
size_t size;
sei_msgtype_t type;
struct _sei_message_t* next;
} sei_message_t;
typedef struct {
double timestamp;
sei_message_t* head;
sei_message_t* tail;
} sei_t;
......@@ -77,64 +111,57 @@ typedef struct {
/*! \brief
\param
*/
void sei_init (sei_t* sei);
/*! \brief
\param
*/
void sei_free (sei_t* sei);
void sei_init(sei_t* sei, double timestamp);
/*! \brief
\param
*/
static inline double sei_dts (sei_t* sei) { return sei->dts; }
static inline double sei_cts (sei_t* sei) { return sei->cts; }
static inline double sei_pts (sei_t* sei) { return sei->dts + sei->cts; }
void sei_free(sei_t* sei);
/*! \brief
\param
*/
int sei_parse_nalu (sei_t* sei, const uint8_t* data, size_t size, double dts, double cts);
void sei_cat(sei_t* to, sei_t* from, int itu_t_t35);
/*! \brief
\param
*/
// TODO add dts,cts to nalu
static inline int sei_parse_avcnalu (sei_t* sei, avcnalu_t* nalu, double dts, double cts) { return sei_parse_nalu (sei,avcnalu_data (nalu),avcnalu_size (nalu),dts,cts); }
void sei_message_append(sei_t* sei, sei_message_t* msg);
/*! \brief
\param
*/
static inline int sei_finish (sei_t* sei) { return sei_parse_nalu (sei,0,0,0.0,DBL_MAX); }
libcaption_stauts_t sei_parse(sei_t* sei, const uint8_t* data, size_t size, double timestamp);
/*! \brief
\param
*/
static inline sei_message_t* sei_message_head (sei_t* sei) { return sei->head; }
static inline sei_message_t* sei_message_head(sei_t* sei) { return sei->head; }
/*! \brief
\param
*/
static inline sei_message_t* sei_message_tail (sei_t* sei) { return sei->tail; }
static inline sei_message_t* sei_message_tail(sei_t* sei) { return sei->tail; }
/*! \brief
\param
*/
sei_message_t* sei_message_next (sei_message_t* msg);
sei_message_t* sei_message_next(sei_message_t* msg);
/*! \brief
\param
*/
sei_msgtype_t sei_message_type (sei_message_t* msg);
sei_msgtype_t sei_message_type(sei_message_t* msg);
/*! \brief
\param
*/
size_t sei_message_size (sei_message_t* msg);
size_t sei_message_size(sei_message_t* msg);
/*! \brief
\param
*/
uint8_t* sei_message_data (sei_message_t* msg);
uint8_t* sei_message_data(sei_message_t* msg);
/*! \brief
\param
*/
sei_message_t* sei_message_new (sei_msgtype_t type, uint8_t* data, size_t size);
sei_message_t* sei_message_new(sei_msgtype_t type, uint8_t* data, size_t size);
/*! \brief
\param
*/
static inline sei_message_t* sei_message_copy (sei_message_t* msg)
static inline sei_message_t* sei_message_copy(sei_message_t* msg)
{
return sei_message_new (sei_message_type (msg), sei_message_data (msg), sei_message_size (msg));
return sei_message_new(sei_message_type(msg), sei_message_data(msg), sei_message_size(msg));
}
/**
Free message and all accoiated data. Messaged added to sei_t by using sei_append_message MUST NOT be freed
......@@ -143,56 +170,43 @@ These messages will be freed by calling sei_free()
/*! \brief
\param
*/
void sei_message_free (sei_message_t* msg);
void sei_message_free(sei_message_t* msg);
////////////////////////////////////////////////////////////////////////////////
/*! \brief
\param
*/
static inline int sei_decode_cea708 (sei_message_t* msg, cea708_t* cea708)
{
if (sei_type_user_data_registered_itu_t_t35 == sei_message_type (msg)) {
return cea708_parse (sei_message_data (msg), sei_message_size (msg), cea708);
} else {
return 0;
}
}
////////////////////////////////////////////////////////////////////////////////
size_t sei_render_size(sei_t* sei);
/*! \brief
\param
*/
size_t sei_render_size (sei_t* sei);
size_t sei_render(sei_t* sei, uint8_t* data);
/*! \brief
\param
*/
size_t sei_render (sei_t* sei, uint8_t* data);
void sei_dump(sei_t* sei);
/*! \brief
\param
*/
void sei_dump (sei_t* sei);
void sei_dump_messages(sei_message_t* head, double timestamp);
////////////////////////////////////////////////////////////////////////////////
/*! \brief
\param
*/
void sei_dump_messages (sei_message_t* head);
////////////////////////////////////////////////////////////////////////////////
libcaption_stauts_t sei_from_scc(sei_t* sei, const scc_t* scc);
/*! \brief
\param
*/
int sei_from_caption_frame (sei_t* sei, caption_frame_t* frame);
libcaption_stauts_t sei_from_caption_frame(sei_t* sei, caption_frame_t* frame);
/*! \brief
\param
*/
libcaption_stauts_t sei_to_caption_frame (sei_t* sei, caption_frame_t* frame);
libcaption_stauts_t sei_from_caption_clear(sei_t* sei);
/*! \brief
\param
*/
static inline int nalu_to_caption_frame (caption_frame_t* frame, const uint8_t* data, size_t size, double pts, double dts)
{
sei_t sei;
sei_init (&sei);
sei_parse_nalu (&sei, data, size, pts, dts);
sei_to_caption_frame (&sei,frame);
sei_free (&sei);
return 1;
}
libcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame);
////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -23,9 +23,25 @@
/**********************************************************************************************/
#ifndef LIBCAPTION_SCC_H
#define LIBCAPTION_SCC_H
#ifdef __cplusplus
extern "C" {
#endif
#include "eia608.h"
int scc_to_608 (const char* line, double* pts, uint16_t* cc, int cc_max);
typedef struct _scc_t {
double timestamp;
unsigned int cc_aloc;
unsigned int cc_size;
uint16_t cc_data[];
} scc_t;
scc_t* scc_new(int cc_count);
scc_t* scc_free(scc_t* scc);
size_t scc_to_608(scc_t** scc, const utf8_char_t* data);
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -23,65 +23,73 @@
/**********************************************************************************************/
#ifndef LIBCAPTION_SRT_H
#define LIBCAPTION_SRT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "eia608.h"
#include "caption.h"
#include "eia608.h"
#include "vtt.h"
// timestamp and duration are in seconds
typedef struct _srt_t {
struct _srt_t* next;
double timestamp;
double duration;
size_t aloc;
} srt_t;
typedef vtt_t srt_t;
typedef vtt_block_t srt_cue_t;
/*! \brief
\param
*/
srt_t* srt_new (const utf8_char_t* data, size_t size, double timestamp, srt_t* prev, srt_t** head);
srt_t* srt_new();
/*! \brief
\param
*/
srt_t* srt_free_head (srt_t* head);
srt_t* srt_free_head(srt_t* head);
// returns the head of the link list. must bee freed when done
/*! \brief
\param
*/
srt_t* srt_parse (const utf8_char_t* data, size_t size);
srt_t* srt_parse(const utf8_char_t* data, size_t size);
/*! \brief
\param
*/
void srt_free (srt_t* srt);
void srt_free(srt_t* srt);
/*! \brief
\param
*/
static inline srt_t* srt_next (srt_t* srt) { return srt->next; }
static inline vtt_block_t* srt_next(vtt_block_t* srt) { return srt->next; }
/*! \brief
\param
*/
static inline utf8_char_t* srt_data (srt_t* srt) { return (utf8_char_t*) (srt) + sizeof (srt_t); }
// This only converts teh surrent SRT, It does not walk the list
static inline utf8_char_t* srt_cue_data(srt_cue_t* cue) { return vtt_block_data(cue); }
/*! \brief
\param
*/
int srt_to_caption_frame (srt_t* srt, caption_frame_t* frame);
static inline srt_cue_t* srt_cue_from_caption_frame(caption_frame_t* frame, srt_t* srt) { return vtt_cue_from_caption_frame(frame, srt); };
// returns teh new srt. Head is not tracher internally.
/*! \brief
\param
*/
srt_t* srt_from_caption_frame (caption_frame_t* frame, srt_t* prev, srt_t** head);
static inline void srt_cue_free_head(srt_t* srt) { vtt_cue_free_head(srt); };
/*! \brief
\param
*/
void srt_dump (srt_t* srt);
static inline srt_cue_t* srt_cue_new(srt_t* srt, const utf8_char_t* data, size_t size) { return vtt_block_new(srt, data, size, VTT_CUE); };
/*! \brief
\param
*/
void vtt_dump (srt_t* srt);
static inline int srt_cue_to_caption_frame(srt_cue_t* cue, caption_frame_t* frame) { return vtt_cue_to_caption_frame(cue, frame); };
void srt_dump(srt_t* srt);
/*! \brief
\param
*/
void vtt_dump(srt_t* srt);
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -23,9 +23,12 @@
/**********************************************************************************************/
#ifndef LIBCAPTION_UTF8_H
#define LIBCAPTION_UTF8_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <inttypes.h>
#include <stddef.h>
// These types exist to make the code more self dcoumenting
// utf8_char_t point is a null teminate string of utf8 encodecd chars
......@@ -39,13 +42,21 @@ typedef size_t utf8_size_t;
Skiped continuation bytes
*/
const utf8_char_t* utf8_char_next (const char* s);
const utf8_char_t* utf8_char_next(const utf8_char_t* c);
/*! \brief
\param
returnes the length of the char in bytes
*/
size_t utf8_char_length (const utf8_char_t* c);
size_t utf8_char_length(const utf8_char_t* c);
/*! \brief
\param
returns 1 if first charcter is white space
*/
int utf8_char_whitespace(const utf8_char_t* c);
/*! \brief
\param
......@@ -53,11 +64,11 @@ size_t utf8_char_length (const utf8_char_t* c);
returns length of the string in bytes
size is number of charcter to count (0 to count until NULL term)
*/
size_t utf8_string_length (const utf8_char_t* data, utf8_size_t size);
size_t utf8_string_length(const utf8_char_t* data, utf8_size_t size);
/*! \brief
\param
*/
size_t utf8_char_copy (utf8_char_t* dst, const utf8_char_t* src);
size_t utf8_char_copy(utf8_char_t* dst, const utf8_char_t* src);
/*! \brief
\param
......@@ -65,33 +76,53 @@ size_t utf8_char_copy (utf8_char_t* dst, const utf8_char_t* src);
returnes the number of utf8 charcters in a string givne the numbe of bytes
to coutn until the a null terminator, pass 0 for size
*/
utf8_size_t utf8_char_count (const char* data, size_t size);
utf8_size_t utf8_char_count(const char* data, size_t size);
/*! \brief
\param
returnes the length of the line in bytes triming not printable charcters at the end
returnes the length of the line in bytes triming not printable characters at the end
*/
size_t utf8_trimmed_length (const char* data, size_t size);
utf8_size_t utf8_trimmed_length(const utf8_char_t* data, utf8_size_t charcters);
/*! \brief
\param
returns the length in bytes of the line including the new line charcter(s)
auto detects between windows(CRLF), unix(LF), mac(CR) and riscos (LFCR) line endings
*/
size_t utf8_line_length (const char* data);
size_t utf8_line_length(const utf8_char_t* data);
/*! \brief
\param
returns number of chars to include before split
*/
utf8_size_t utf8_wrap_length (const utf8_char_t* data, utf8_size_t size);
utf8_size_t utf8_wrap_length(const utf8_char_t* data, utf8_size_t size);
/*! \brief
\param
returns number of new lines in the string
*/
int utf8_line_count(const utf8_char_t* data);
/*! \brief
\param
size in/out. In the the max seize, out is the size read;
returns number of new lins in teh string
*/
int utf8_line_count (const utf8_char_t* data);
#define UFTF_DEFAULT_MAX_FILE_SIZE = (50 * 1024 * 1024);
utf8_char_t* utf8_load_text_file(const char* path, size_t* size);
/*! \brief
\param
Compares 2 strings up to max len
*/
#ifndef strnstr
char* strnstr(const char* string1, const char* string2, size_t len);
#endif
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -21,29 +21,125 @@
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#ifndef LIBCAPTION_TS_H
#define LIBCAPTION_TS_H
#ifndef LIBCAPTION_VTT_H
#define LIBCAPTION_VTT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "caption.h"
typedef struct {
int16_t pmtpid;
int16_t avcpid;
int64_t pts;
int64_t dts;
size_t size;
const uint8_t* data;
} ts_t;
#include "eia608.h"
enum VTT_BLOCK_TYPE {
VTT_REGION = 0,
VTT_STYLE = 1,
VTT_NOTE = 2,
VTT_CUE = 3
};
// CUE represents a block of caption text
typedef struct _vtt_block_t {
struct _vtt_block_t* next;
enum VTT_BLOCK_TYPE type;
// CUE-Only
double timestamp;
double duration; // -1.0 for no duration
char* cue_settings;
char* cue_id;
// Standard block data
size_t text_size;
char* block_text;
} vtt_block_t;
// VTT files are a collection of REGION, STYLE and CUE blocks.
// XXX: Comments (NOTE blocks) are ignored
typedef struct _vtt_t {
vtt_block_t* region_head;
vtt_block_t* region_tail;
vtt_block_t* style_head;
vtt_block_t* style_tail;
vtt_block_t* cue_head;
vtt_block_t* cue_tail;
} vtt_t;
/*! \brief
\param
*/
vtt_t* vtt_new();
/*! \brief
\param
*/
void vtt_free(vtt_t* vtt);
/*! \brief
\param
*/
vtt_block_t* vtt_block_new(vtt_t* vtt, const utf8_char_t* data, size_t size, enum VTT_BLOCK_TYPE type);
/*! \brief
\param
*/
void vtt_cue_free_head(vtt_t* vtt);
/*! \brief
\param
*/
void vtt_style_free_head(vtt_t* vtt);
/*! \brief
\param
*/
void vtt_region_free_head(vtt_t* vtt);
Expects 188 byte TS packet
// returns a vtt_t, containing linked lists of blocks. must be freed when done
/*! \brief
\param
*/
#define TS_PACKET_SIZE 188
void ts_init (ts_t* ts);
int ts_parse_packet (ts_t* ts, const uint8_t* data);
// return timestamp in seconds
static inline double ts_dts_seconds (ts_t* ts) { return ts->dts / 90000.0; }
static inline double ts_pts_seconds (ts_t* ts) { return ts->pts / 90000.0; }
static inline double ts_cts_seconds (ts_t* ts) { return (ts->dts - ts->pts) / 90000.0; }
vtt_t* vtt_parse(const utf8_char_t* data, size_t size);
/*! \brief
\param
*/
vtt_t* _vtt_parse(const utf8_char_t* data, size_t size, int srt_mode);
/*! \brief
\param
*/
static inline vtt_block_t* vtt_cue_next(vtt_block_t* block) { return block->next; }
/*! \brief
\param
*/
static inline utf8_char_t* vtt_block_data(vtt_block_t* block) { return (utf8_char_t*)(block) + sizeof(vtt_block_t); }
/*! \brief
\param
*/
static inline void vtt_crack_time(double tt, int* hh, int* mm, int* ss, int* ms)
{
(*ms) = (int)((int64_t)(tt * 1000) % 1000);
(*ss) = (int)((int64_t)(tt) % 60);
(*mm) = (int)((int64_t)(tt / (60)) % 60);
(*hh) = (int)((int64_t)(tt / (60 * 60)));
}
// This only converts the current CUE, it does not walk the list
/*! \brief
\param
*/
int vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame);
// returns the new cue
/*! \brief
\param
*/
vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt);
/*! \brief
\param
*/
void vtt_dump(vtt_t* vtt);
#ifdef __cplusplus
}
#endif
#endif
/**********************************************************************************************/
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* The MIT License */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file */
/* except in compliance with the License. A copy of the License is located at */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* http://aws.amazon.com/apache2.0/ */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* or in the "license" file accompanying this file. This file is distributed on an "AS IS" */
/* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the */
/* License for the specific language governing permissions and limitations under the License. */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#ifndef LIBCAPTION_XDS_H
#define LIBCAPTION_XDS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <inttypes.h>
#include <stddef.h>
typedef struct {
int state;
uint8_t class;
uint8_t class_code;
uint8_t type;
uint32_t size;
uint8_t content[32];
uint8_t checksum;
} xds_t;
void xds_init (xds_t* xds);
int xds_decode (xds_t* xds, uint16_t cc);
void xds_init(xds_t* xds);
int xds_decode(xds_t* xds, uint16_t cc);
#ifdef __cplusplus
}
#endif
#endif
#!/usr/bin/env bash
if [ $# -lt 2 ]
then
echo "Need at least 2 arguments."
echo "$0 InputVideo InputSRT [OutputFilename]"
exit 1
fi
VIDEO=$1
SRT=$2
if [ -z "$3" ]
then
OUTFILE="out.flv"
else
OUTFILE="$3"
fi
echo "Video=$VIDEO"
echo "Captions=$SRT"
echo "Outfile=$OUTFILE"
# ffmpeg -i $VIDEO -acodec copy -vcodec copy -f flv - | ./flv+srt - $SRT - | ffmpeg -i - -acodec copy -vcodec copy $OUTFILE
ffmpeg -i $VIDEO -threads 0 -vcodec libx264 -profile:v main -preset:v medium \
-r 30 -g 60 -keyint_min 60 -sc_threshold 0 -b:v 4000k -maxrate 4000k \
-bufsize 4000k -filter:v scale="trunc(oh*a/2)*2:720" \
-sws_flags lanczos+accurate_rnd -strict -2 -acodec aac -b:a 96k -ar 48000 -ac 2 \
-f flv - | ./flv+srt - $SRT - | ffmpeg -i - -acodec copy -vcodec copy -y $OUTFILE
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>
#include "caption.h"
#include "flv.h"
char charcode[] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 0,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']','\n', 0, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\'', '`', 0, '\\', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0,
};
int data_ready (int fd)
{
fd_set set;
struct timeval timeout = {0,0};
FD_ZERO (&set);
FD_SET (fd,&set);
int cnt = select (fd+1, &set, 0, 0, &timeout);
FD_ZERO (&set);
return (0 < cnt);
}
#define MAX_CAP_LENGTH (SCREEN_ROWS*SCREEN_COLS)
int main (int argc, char** argv)
{
int fd;
ssize_t n;
flvtag_t tag;
struct input_event ev;
int has_audio, has_video;
const char* dev = argv[1];
char text[MAX_CAP_LENGTH+1];
memset (text,0,MAX_CAP_LENGTH+1);
FILE* flv = flv_open_read ("-");
FILE* out = flv_open_write ("-");
fd = open (dev, O_RDONLY);
if (fd == -1) {
fprintf (stderr, "Cannot open %s: %s.\n", dev, strerror (errno));
return EXIT_FAILURE;
}
if (!flv_read_header (flv,&has_audio,&has_video)) {
fprintf (stderr,"%s is not an flv file\n", argv[1]);
return EXIT_FAILURE;
}
if (!flv_write_header (out,has_audio,has_video)) {
return EXIT_FAILURE;
}
flvtag_init (&tag);
while (flv_read_tag (flv,&tag)) {
if (flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag)) {
if (data_ready (fd)) {
n = read (fd, &ev, sizeof ev);
if (n == (ssize_t)-1) {
if (errno == EINTR) {
continue;
} else {
break;
}
} else if (n != sizeof ev) {
errno = EIO;
break;
}
int len = strlen (text);
char c = (EV_KEY == ev.type && 1 == ev.value && ev.code < 64) ? charcode[ev.code] : 0;
if (0 < len && '\n' == c) {
fprintf (stderr,"='%s'\n", text);
flvtag_addcaption (&tag, text);
memset (text,0,MAX_CAP_LENGTH+1);
} else if (0 != c && len < MAX_CAP_LENGTH) {
text[len] = c;
}
}
}
flv_write_tag (out,&tag);
}
return EXIT_SUCCESS;
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "srt.h"
#include "flv.h"
#include "avc.h"
// #include "sei.h"
#define MAX_SRT_SIZE (10*1024*1024)
#define MAX_READ_SIZE 4096
srt_t* srt_from_file (const char* path)
{
srt_t* head = 0;
size_t read, totl = 0;
FILE* file = fopen (path,"r");
if (file) {
char* srt_data = malloc (MAX_SRT_SIZE);
size_t srt_size = 0;
size_t size = MAX_SRT_SIZE;
char* data = srt_data;
while (0 < (read = fread (data,1,size,file))) {
totl += read; data += read; size -= read; srt_size += read;
}
head = srt_parse (srt_data,srt_size);
free (srt_data);
}
return head;
}
int main (int argc, char** argv)
{
flvtag_t tag;
FILE* flv = flv_open_read (argv[1]);
FILE* out = flv_open_write (argv[3]);
int has_audio, has_video;
flvtag_init (&tag);
if (!flv_read_header (flv,&has_audio,&has_video)) {
fprintf (stderr,"%s is not an flv file\n", argv[1]);
return EXIT_FAILURE;
}
srt_t* head = srt_from_file (argv[2]);
srt_t* srt = head;
if (! head) {
fprintf (stderr,"%s is not an srt file\n", argv[2]);
return EXIT_FAILURE;
}
flv_write_header (out,has_audio,has_video);
while (flv_read_tag (flv,&tag)) {
// TODO handle B freame!
if (srt && flvtag_pts_seconds (&tag) >= srt->timestamp && flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag)) {
fprintf (stderr,"%f: %s\n", srt->timestamp, srt_data (srt));
flvtag_addcaption (&tag, srt_data (srt));
srt = srt->next;
}
flv_write_tag (out,&tag);
// Write tag out here
}
srt_free (head);
return EXIT_SUCCESS;
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include "flv.h"
#include <stdlib.h>
#include <string.h>
void flvtag_init (flvtag_t* tag)
{
memset (tag,0,sizeof (flvtag_t));
}
void flvtag_free (flvtag_t* tag)
{
if (tag->data) {
free (tag->data);
}
flvtag_init (tag);
}
int flvtag_reserve (flvtag_t* tag, uint32_t size)
{
size += FLV_TAG_HEADER_SIZE + FLV_TAG_FOOTER_SIZE;
if (size > tag->aloc) {
tag->data = realloc (tag->data,size);
tag->aloc = size;
}
return 1;
}
FILE* flv_open_read (const char* flv)
{
if (0 == flv || 0 == strcmp ("-",flv)) {
return stdin;
}
return fopen (flv,"rb");
}
FILE* flv_open_write (const char* flv)
{
if (0 == flv || 0 == strcmp ("-",flv)) {
return stdout;
}
return fopen (flv,"wb");
}
FILE* flv_close (FILE* flv)
{
fclose (flv);
return 0;
}
int flv_read_header (FILE* flv, int* has_audio, int* has_video)
{
uint8_t h[FLV_HEADER_SIZE];
if (FLV_HEADER_SIZE != fread (&h[0],1,FLV_HEADER_SIZE,flv)) {
return 0;
}
if ('F' != h[0] || 'L' != h[1] || 'V' != h[2]) {
return 0;
}
(*has_audio) = h[4]&0x04;
(*has_video) = h[4]&0x01;
return 1;
}
int flv_write_header (FILE* flv, int has_audio, int has_video)
{
uint8_t h[FLV_HEADER_SIZE] = {'F', 'L', 'V', 1, (has_audio?0x04:0x00) | (has_video?0x01:0x00), 0, 0, 0, 9, 0, 0, 0, 0 };
return FLV_HEADER_SIZE == fwrite (&h[0],1,FLV_HEADER_SIZE,flv);
}
int flv_read_tag (FILE* flv, flvtag_t* tag)
{
uint32_t size;
uint8_t h[FLV_TAG_HEADER_SIZE];
if (FLV_TAG_HEADER_SIZE != fread (&h[0],1,FLV_TAG_HEADER_SIZE,flv)) {
return 0;
}
size = ( (h[1]<<16) | (h[2]<<8) |h[3]);
flvtag_reserve (tag, size);
// copy header to buffer
memcpy (tag->data,&h[0],FLV_TAG_HEADER_SIZE);
if (size+FLV_TAG_FOOTER_SIZE != fread (&tag->data[FLV_TAG_HEADER_SIZE],1,size+FLV_TAG_FOOTER_SIZE,flv)) {
return 0;
}
return 1;
}
int flv_write_tag (FILE* flv, flvtag_t* tag)
{
size_t size = flvtag_raw_size (tag);
return size == fwrite (flvtag_raw_data (tag),1,size,flv);
}
////////////////////////////////////////////////////////////////////////////////
size_t flvtag_header_size (flvtag_t* tag)
{
switch (flvtag_type (tag)) {
case flvtag_type_audio:
return FLV_TAG_HEADER_SIZE + (flvtag_soundformat_aac != flvtag_soundformat (tag) ? 1 : 2);
case flvtag_type_video:
// CommandFrame does not have a compositionTime
return FLV_TAG_HEADER_SIZE + (flvtag_codecid_avc != flvtag_codecid (tag) ? 1 : (flvtag_frametype_commandframe != flvtag_frametype (tag) ? 5 : 2));
default:
return FLV_TAG_HEADER_SIZE;
}
}
size_t flvtag_payload_size (flvtag_t* tag)
{
return FLV_TAG_HEADER_SIZE + flvtag_size (tag) - flvtag_header_size (tag);
}
uint8_t* flvtag_payload_data (flvtag_t* tag)
{
size_t payload_offset = flvtag_header_size (tag);
return &tag->data[payload_offset];
}
////////////////////////////////////////////////////////////////////////////////
int flvtag_updatesize (flvtag_t* tag, uint32_t size)
{
tag->data[1] = size>>16; // DataSize
tag->data[2] = size>>8; // DataSize
tag->data[3] = size>>0; // DataSize
size += 11;
tag->data[size+0] = size>>24; // PrevTagSize
tag->data[size+1] = size>>16; // PrevTagSize
tag->data[size+2] = size>>8; // PrevTagSize
tag->data[size+3] = size>>0; // PrevTagSize
return 1;
}
#define FLVTAG_PREALOC 2048
int flvtag_initavc (flvtag_t* tag, uint32_t dts, int32_t cts, flvtag_frametype_t type)
{
flvtag_init (tag);
flvtag_reserve (tag,5+FLVTAG_PREALOC);
tag->data[0] = flvtag_type_video;
tag->data[4] = dts>>16;
tag->data[5] = dts>>8;
tag->data[6] = dts>>0;
tag->data[7] = dts>>24;
tag->data[8] = 0; // StreamID
tag->data[9] = 0; // StreamID
tag->data[10] = 0; // StreamID
// VideoTagHeader
tag->data[11] = ( (type<<4) %0xF0) |0x07; // CodecId
tag->data[12] = 0x01; // AVC NALU
tag->data[13] = cts>>16;
tag->data[14] = cts>>8;
tag->data[15] = cts>>0;
flvtag_updatesize (tag,5);
return 1;
}
int flvtag_initamf (flvtag_t* tag, uint32_t dts)
{
flvtag_init (tag);
flvtag_reserve (tag,FLVTAG_PREALOC);
tag->data[0] = flvtag_type_scriptdata;
tag->data[4] = dts>>16;
tag->data[5] = dts>>8;
tag->data[6] = dts>>0;
tag->data[7] = dts>>24;
tag->data[8] = 0; // StreamID
tag->data[9] = 0; // StreamID
tag->data[10] = 0; // StreamID
flvtag_updatesize (tag,0);
return 1;
}
// shamelessly taken from libtomcrypt, an public domain project
static void base64_encode (const unsigned char* in, unsigned long inlen, unsigned char* out, unsigned long* outlen)
{
static const char* codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned long i, len2, leven;
unsigned char* p;
/* valid output size ? */
len2 = 4 * ( (inlen + 2) / 3);
if (*outlen < len2 + 1) {
*outlen = len2 + 1;
fprintf (stderr,"\n\nHERE\n\n");
return;
}
p = out;
leven = 3* (inlen / 3);
for (i = 0; i < leven; i += 3) {
*p++ = codes[ (in[0] >> 2) & 0x3F];
*p++ = codes[ ( ( (in[0] & 3) << 4) + (in[1] >> 4)) & 0x3F];
*p++ = codes[ ( ( (in[1] & 0xf) << 2) + (in[2] >> 6)) & 0x3F];
*p++ = codes[in[2] & 0x3F];
in += 3;
}
if (i < inlen) {
unsigned a = in[0];
unsigned b = (i+1 < inlen) ? in[1] : 0;
*p++ = codes[ (a >> 2) & 0x3F];
*p++ = codes[ ( ( (a & 3) << 4) + (b >> 4)) & 0x3F];
*p++ = (i+1 < inlen) ? codes[ ( ( (b & 0xf) << 2)) & 0x3F] : '=';
*p++ = '=';
}
/* return ok */
*outlen = p - out;
}
const char onCaptionInfo708[] = { 0x02,0x00,0x0D, 'o','n','C','a','p','t','i','o','n','I','n','f','o',
0x08, 0x00, 0x00, 0x00, 0x02,
0x00, 0x04, 't','y','p','e',
0x02, 0x00, 0x03, '7','0','8',
0x00, 0x04, 'd','a','t','a',
0x02, 0x00,0x00
};
int flvtag_amfcaption_708 (flvtag_t* tag, uint32_t timestamp, sei_message_t* msg)
{
flvtag_initamf (tag,timestamp);
unsigned long size = 1 + (4 * ( (sei_message_size (msg) + 2) / 3));
flvtag_reserve (tag, sizeof (onCaptionInfo708) + size + 3);
memcpy (flvtag_payload_data (tag),onCaptionInfo708,sizeof (onCaptionInfo708));
uint8_t* data = flvtag_payload_data (tag) + sizeof (onCaptionInfo708);
base64_encode (sei_message_data (msg), sei_message_size (msg), data, &size);
// Update the size of the base64 string
data[-2] = size >> 8;
data[-1] = size >> 0;
// write the last array element
data[size+0] = 0x00;
data[size+1] = 0x00;
data[size+2] = 0x09;
flvtag_updatesize (tag, sizeof (onCaptionInfo708) + size + 3);
return 1;
}
const char onCaptionInfoUTF8[] = { 0x02,0x00,0x0D, 'o','n','C','a','p','t','i','o','n','I','n','f','o',
0x08, 0x00, 0x00, 0x00, 0x02,
0x00, 0x04, 't','y','p','e',
0x02, 0x00, 0x04, 'U','T','F','8',
0x00, 0x04, 'd','a','t','a',
0x02, 0x00,0x00
};
#define MAX_AMF_STRING 65636
int flvtag_amfcaption_utf8 (flvtag_t* tag, uint32_t timestamp, const utf8_char_t* text)
{
flvtag_initamf (tag,timestamp);
unsigned long size = strlen (text);
if (MAX_AMF_STRING < size) {
size = MAX_AMF_STRING;
}
flvtag_reserve (tag, sizeof (onCaptionInfoUTF8) + size + 3);
memcpy (flvtag_payload_data (tag),onCaptionInfoUTF8,sizeof (onCaptionInfoUTF8));
uint8_t* data = flvtag_payload_data (tag) + sizeof (onCaptionInfo708);
memcpy (data,text,size);
// Update the size of the string
data[-2] = size >> 8;
data[-1] = size >> 0;
// write the last array element
data[size+0] = 0x00;
data[size+1] = 0x00;
data[size+2] = 0x09;
flvtag_updatesize (tag, sizeof (onCaptionInfoUTF8) + size + 3);
return 1;
}
#define LENGTH_SIZE 4
int flvtag_avcwritenal (flvtag_t* tag, uint8_t* data, size_t size)
{
uint32_t flvsize = flvtag_size (tag);
flvtag_reserve (tag,flvsize+LENGTH_SIZE+size);
uint8_t* payload = tag->data + FLV_TAG_HEADER_SIZE + flvsize;
payload[0] = size>>24; // nalu size
payload[1] = size>>16;
payload[2] = size>>8;
payload[3] = size>>0;
memcpy (&payload[LENGTH_SIZE],data,size);
flvtag_updatesize (tag,flvsize+LENGTH_SIZE+size);
return 1;
}
int flvtag_addcaption (flvtag_t* tag, const utf8_char_t* text)
{
if (flvtag_avcpackettype_nalu != flvtag_avcpackettype (tag)) {
return 0;
}
sei_t sei;
caption_frame_t frame;
sei_init (&sei);
caption_frame_init (&frame);
caption_frame_from_text (&frame, text);
sei_from_caption_frame (&sei, &frame);
uint8_t* sei_data = malloc (sei_render_size (&sei));
size_t sei_size = sei_render (&sei, sei_data);
// rewrite tag
flvtag_t new_tag;
flvtag_initavc (&new_tag, flvtag_dts (tag), flvtag_cts (tag), flvtag_frametype (tag));
uint8_t* data = flvtag_payload_data (tag);
ssize_t size = flvtag_payload_size (tag);
while (0<size) {
uint8_t* nalu_data = &data[LENGTH_SIZE];
uint8_t nalu_type = nalu_data[0]&0x1F;
uint32_t nalu_size = (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3];
data += LENGTH_SIZE + nalu_size;
size -= LENGTH_SIZE + nalu_size;
if (0 < sei_size && 7 != nalu_type && 8 != nalu_type && 9 != nalu_type ) {
// fprintf (stderr,"Wrote SEI %d '%d'\n\n", sei_size, sei_data[3]);
flvtag_avcwritenal (&new_tag,sei_data,sei_size);
sei_size = 0;
}
flvtag_avcwritenal (&new_tag,nalu_data,nalu_size);
}
// On the off chance we have an empty frame,
// We still wish to append the sei
if (0<sei_size) {
// fprintf (stderr,"Wrote SEI %d\n\n", sei_size);
flvtag_avcwritenal (&new_tag,sei_data,sei_size);
sei_size = 0;
}
if (sei_data) {
free (sei_data);
}
free (tag->data);
sei_free (&sei);
tag->data = new_tag.data;
tag->aloc = new_tag.aloc;
return 1;
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#ifndef LIBCAPTION_FLV_H
#define LIBCAPTION_FLV_H
#include <stdio.h>
#include <stddef.h>
#include <inttypes.h>
#define FLV_HEADER_SIZE 13
#define FLV_FOOTER_SIZE 4
#define FLV_TAG_HEADER_SIZE 11
#define FLV_TAG_FOOTER_SIZE 4
////////////////////////////////////////////////////////////////////////////////
#include "avc.h"
////////////////////////////////////////////////////////////////////////////////
typedef struct {
uint8_t* data;
size_t aloc;
} flvtag_t;
void flvtag_init (flvtag_t* tag);
void flvtag_free (flvtag_t* tag);
void flvtag_swap (flvtag_t* tag1, flvtag_t* tag2);
////////////////////////////////////////////////////////////////////////////////
typedef enum {
flvtag_type_audio = 0x08,
flvtag_type_video = 0x09,
flvtag_type_scriptdata = 0x12,
} flvtag_type_t;
static inline flvtag_type_t flvtag_type (flvtag_t* tag) { return (flvtag_type_t) tag->data[0]&0x1F; }
////////////////////////////////////////////////////////////////////////////////
typedef enum {
flvtag_soundformat_unknown = -1,
flvtag_soundformat_linearpcmplatformendian = 0,
flvtag_soundformat_adpcm = 1,
flvtag_soundformat_mp3 = 2,
flvtag_soundformat_linearpcmlittleendian = 3,
flvtag_soundformat_nellymoser_16khzmono = 4,
flvtag_soundformat_nellymoser_8khzmono = 5,
flvtag_soundformat_nellymoser = 6,
flvtag_soundformat_g711alawlogarithmicpcm = 7,
flvtag_soundformat_g711mulawlogarithmicpcm = 8,
flvtag_soundformat_reserved = 9,
flvtag_soundformat_aac = 10,
flvtag_soundformat_speex = 11,
flvtag_soundformat_mp3_8khz = 14,
flvtag_soundformat_devicespecificsound = 15
} flvtag_soundformat_t;
static inline flvtag_soundformat_t flvtag_soundformat (flvtag_t* tag) { return (flvtag_type_audio!=flvtag_type (tag)) ?flvtag_soundformat_unknown: (flvtag_soundformat_t) (tag->data[0]>>4) &0x0F; }
////////////////////////////////////////////////////////////////////////////////
typedef enum {
flvtag_codecid_unknown = -1,
flvtag_codecid_sorensonh263 = 2,
flvtag_codecid_screenvideo = 3,
flvtag_codecid_on2vp6 = 4,
flvtag_codecid_on2vp6withalphachannel = 5,
flvtag_codecid_screenvideoversion2 = 6,
flvtag_codecid_avc = 7
} flvtag_codecid_t;
static inline flvtag_codecid_t flvtag_codecid (flvtag_t* tag) { return (flvtag_type_video!=flvtag_type (tag)) ? (flvtag_codecid_unknown) : (tag->data[11]&0x0F); }
////////////////////////////////////////////////////////////////////////////////
typedef enum {
flvtag_frametype_unknown = -1,
flvtag_frametype_keyframe = 1,
flvtag_frametype_interframe = 2,
flvtag_frametype_disposableinterframe = 3,
flvtag_frametype_generatedkeyframe = 4,
flvtag_frametype_commandframe = 5
} flvtag_frametype_t;
static inline flvtag_frametype_t flvtag_frametype (flvtag_t* tag) { return (flvtag_type_video!=flvtag_type (tag)) ?flvtag_frametype_keyframe: ( (tag->data[11]>>4) &0x0F); }
////////////////////////////////////////////////////////////////////////////////
typedef enum {
flvtag_avcpackettype_unknown = -1,
flvtag_avcpackettype_sequenceheader = 0,
flvtag_avcpackettype_nalu = 1,
flvtag_avcpackettype_endofsequence = 2
} flvtag_avcpackettype_t;
static inline flvtag_avcpackettype_t flvtag_avcpackettype (flvtag_t* tag) { return (flvtag_codecid_avc!=flvtag_codecid (tag)) ?flvtag_avcpackettype_unknown:tag->data[12]; }
////////////////////////////////////////////////////////////////////////////////
static inline size_t flvtag_size (flvtag_t* tag) { return (tag->data[1]<<16) | (tag->data[2]<<8) | tag->data[3]; }
static inline uint32_t flvtag_timestamp (flvtag_t* tag) { return (tag->data[7]<<24) | (tag->data[4]<<16) | (tag->data[5]<<8) | tag->data[6]; }
static inline uint32_t flvtag_dts (flvtag_t* tag) { return flvtag_timestamp (tag); }
static inline uint32_t flvtag_cts (flvtag_t* tag) { return (flvtag_avcpackettype_nalu!=flvtag_avcpackettype (tag)) ?0: (tag->data[13]<<16) | (tag->data[14]<<8) |tag->data[15]; }
static inline uint32_t flvtag_pts (flvtag_t* tag) { return flvtag_dts (tag)+flvtag_cts (tag); }
static inline double flvtag_dts_seconds (flvtag_t* tag) { return flvtag_dts (tag) / 1000.0; }
static inline double flvtag_cts_seconds (flvtag_t* tag) { return flvtag_cts (tag) / 1000.0; }
static inline double flvtag_pts_seconds (flvtag_t* tag) { return flvtag_pts (tag) / 1000.0; }
////////////////////////////////////////////////////////////////////////////////
size_t flvtag_header_size (flvtag_t* tag);
size_t flvtag_payload_size (flvtag_t* tag);
uint8_t* flvtag_payload_data (flvtag_t* tag);
////////////////////////////////////////////////////////////////////////////////
FILE* flv_open_read (const char* flv);
FILE* flv_open_write (const char* flv);
FILE* flv_close (FILE* flv);
////////////////////////////////////////////////////////////////////////////////
static inline const uint8_t* flvtag_raw_data (flvtag_t* tag) { return tag->data; }
static inline const size_t flvtag_raw_size (flvtag_t* tag) { return flvtag_size (tag)+FLV_TAG_HEADER_SIZE+FLV_TAG_FOOTER_SIZE; }
////////////////////////////////////////////////////////////////////////////////
int flv_read_tag (FILE* flv, flvtag_t* tag);
int flv_write_tag (FILE* flv, flvtag_t* tag);
int flv_read_header (FILE* flv, int* has_audio, int* has_video);
int flv_write_header (FILE* flv, int has_audio, int has_video);
////////////////////////////////////////////////////////////////////////////////
// If the tage has more that on sei message, they will be combined into one
sei_t* flv_read_sei (FILE* flv, flvtag_t* tag);
////////////////////////////////////////////////////////////////////////////////
int flvtag_initavc (flvtag_t* tag, uint32_t dts, int32_t cts, flvtag_frametype_t type);
int flvtag_avcwritenal (flvtag_t* tag, uint8_t* data, size_t size);
int flvtag_addcaption (flvtag_t* tag, const utf8_char_t* text);
////////////////////////////////////////////////////////////////////////////////
int flvtag_amfcaption_708 (flvtag_t* tag, uint32_t timestamp, sei_message_t* msg);
////////////////////////////////////////////////////////////////////////////////
// This method is expermental, and not currently available on Twitch
int flvtag_amfcaption_utf8 (flvtag_t* tag, uint32_t timestamp, const utf8_char_t* text);
#endif
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include "flv.h"
#include "srt.h"
#include "avc.h"
#define LENGTH_SIZE 4
int main (int argc, char** argv)
{
const char* path = argv[1];
sei_t sei;
flvtag_t tag;
srt_t* srt = 0, *head = 0;
int i, has_audio, has_video;
caption_frame_t frame;
flvtag_init (&tag);
caption_frame_init (&frame);
FILE* flv = flv_open_read (path);
if (!flv_read_header (flv,&has_audio,&has_video)) {
fprintf (stderr,"'%s' Not an flv file\n", path);
} else {
fprintf (stderr,"Reading from '%s'\n", path);
}
while (flv_read_tag (flv,&tag)) {
if (flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag)) {
ssize_t size = flvtag_payload_size (&tag);
uint8_t* data = flvtag_payload_data (&tag);
while (0<size) {
ssize_t nalu_size = (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3];
uint8_t* nalu_data = &data[4];
uint8_t nalu_type = nalu_data[0]&0x1F;
data += nalu_size + LENGTH_SIZE;
size -= nalu_size + LENGTH_SIZE;
if (6 == nalu_type) {
sei_init (&sei);
sei_parse_nalu (&sei, nalu_data, nalu_size, flvtag_dts (&tag), flvtag_cts (&tag));
cea708_t cea708;
sei_message_t* msg;
cea708_init (&cea708);
// for (msg = sei_message_head (&sei) ; msg ; msg = sei_message_next (msg)) {
// if (sei_type_user_data_registered_itu_t_t35 == sei_message_type (msg)) {
// cea708_parse (sei_message_data (msg), sei_message_size (msg), &cea708);
// cea708_dump (&cea708);
// }
// }
// sei_dump(&sei);
sei_to_caption_frame (&sei,&frame);
sei_free (&sei);
// caption_frame_dump (&frame);
srt = srt_from_caption_frame (&frame,srt,&head);
}
}
}
}
srt_dump (head);
srt_free (head);
return 1;
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "flv.h"
#include "avc.h"
#define CAPTION_METHOD_SEI_708 0 // embedded 708
#define CAPTION_METHOD_AMF_708 1 // onCaptionInfo type = 708
#define CAPTION_METHOD_AMF_UTF8 2 // onCaptionInfo type = utf8
#define CAPTION_METHOD CAPTION_METHOD_SEI_708
void get_dudes (char* str)
{
sprintf (str, " %s%s %s(-_-)%s %s(-_-)%s.%s(-_-)%s %s%s", EIA608_CHAR_EIGHTH_NOTE, EIA608_CHAR_EIGHTH_NOTE,
! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT,
! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT,
! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT,
! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT,
! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT,
! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT,
EIA608_CHAR_EIGHTH_NOTE, EIA608_CHAR_EIGHTH_NOTE);
}
void write_amfcaptions_708 (FILE* out, uint32_t timestamp, const char* text)
{
sei_t sei;
flvtag_t tag;
sei_message_t* msg;
caption_frame_t frame;
sei_init (&sei);
flvtag_init (&tag);
caption_frame_init (&frame);
caption_frame_from_text (&frame, text);
sei_from_caption_frame (&sei, &frame);
// caption_frame_dump (&frame);
for (msg = sei_message_head (&sei); msg; msg=sei_message_next (msg),++timestamp) {
flvtag_amfcaption_708 (&tag,timestamp,msg);
flv_write_tag (out,&tag);
}
sei_free (&sei);
flvtag_free (&tag);
}
void write_amfcaptions_utf8 (FILE* out, uint32_t timestamp, const utf8_char_t* text)
{
flvtag_t tag;
flvtag_init (&tag);
flvtag_amfcaption_utf8 (&tag,timestamp,text);
flv_write_tag (out,&tag);
flvtag_free (&tag);
}
int main (int argc, char** argv)
{
flvtag_t tag;
uint32_t nextParty = 1000;
int has_audio, has_video;
FILE* flv = flv_open_read (argv[1]);
FILE* out = flv_open_write (argv[2]);
char partyDudes[64];
flvtag_init (&tag);
if (!flv_read_header (flv,&has_audio,&has_video)) {
fprintf (stderr,"%s is not an flv file\n", argv[1]);
return EXIT_FAILURE;
}
flv_write_header (out,has_audio,has_video);
while (flv_read_tag (flv,&tag)) {
if (flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag) && nextParty <= flvtag_timestamp (&tag)) {
get_dudes (partyDudes);
if (CAPTION_METHOD == CAPTION_METHOD_SEI_708) {
flvtag_addcaption (&tag, partyDudes);
} else if (CAPTION_METHOD == CAPTION_METHOD_AMF_708) {
write_amfcaptions_708 (out,nextParty,partyDudes);
} else if (CAPTION_METHOD == CAPTION_METHOD_AMF_708) {
write_amfcaptions_utf8 (out, nextParty, partyDudes);
} else {
fprintf (stderr,"Unknnow method\n");
return EXIT_FAILURE;
}
fprintf (stderr,"%d: %s\n",nextParty, partyDudes);
nextParty += 500; // party all the time
}
flv_write_tag (out,&tag);
}
return EXIT_SUCCESS;
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "flv.h"
#include "avc.h"
#include "srt.h"
#include "wonderland.h"
#define SECONDS_PER_LINE 3.0
srt_t* appennd_caption (const utf8_char_t* data, srt_t* prev, srt_t** head)
{
int r, c, chan = 0;
ssize_t size = (ssize_t) strlen (data);
size_t char_count, char_length, line_length = 0, trimmed_length = 0;
for (r = 0 ; 0 < size && SCREEN_ROWS > r ; ++r) {
line_length = utf8_line_length (data);
trimmed_length = utf8_trimmed_length (data,line_length);
char_count = utf8_char_count (data,trimmed_length);
// If char_count is greater than one line can display, split it.
if (SCREEN_COLS < char_count) {
char_count = utf8_wrap_length (data,SCREEN_COLS);
line_length = utf8_string_length (data,char_count+1);
}
// fprintf (stderr,"%.*s\n", line_length, data);
prev = srt_new (data, line_length, prev ? prev->timestamp + SECONDS_PER_LINE : 0, prev, head);
data += line_length;
size -= (ssize_t) line_length;
}
return prev;
}
int main (int argc, char** argv)
{
int i = 0;
flvtag_t tag;
srt_t* head = 0, *tail = 0;
int has_audio, has_video;
FILE* flv = flv_open_read (argv[1]);
FILE* out = flv_open_write (argv[2]);
flvtag_init (&tag);
for (i = 0 ; wonderland[i][0]; ++i) {
tail = appennd_caption (wonderland[i], tail, &head);
}
if (!flv_read_header (flv,&has_audio,&has_video)) {
fprintf (stderr,"%s is not an flv file\n", argv[1]);
return EXIT_FAILURE;
}
flv_write_header (out,has_audio,has_video);
while (flv_read_tag (flv,&tag)) {
if (head && flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag) && head->timestamp <= flvtag_pts_seconds (&tag)) {
fprintf (stderr,"%f %s\n", flvtag_pts_seconds (&tag), srt_data (head));
flvtag_addcaption (&tag, srt_data (head));
head = srt_free_head (head);
}
flv_write_tag (out,&tag);
}
return EXIT_SUCCESS;
}
#include "flv.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <librtmp/log.h>
#include <librtmp/rtmp.h>
int MyRTMP_Write (RTMP* r, const char* buf, int size)
{
RTMPPacket* pkt = &r->m_write;
char* enc;
int s2 = size, ret, num;
pkt->m_nChannel = 0x04; /* source channel */
pkt->m_nInfoField2 = r->m_stream_id;
while (s2) {
if (!pkt->m_nBytesRead) {
if (size < 11) {
/* FLV pkt too small */
return 0;
}
if (buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V') {
buf += 13;
s2 -= 13;
}
pkt->m_packetType = *buf++;
pkt->m_nBodySize = AMF_DecodeInt24 (buf);
buf += 3;
pkt->m_nTimeStamp = AMF_DecodeInt24 (buf);
buf += 3;
pkt->m_nTimeStamp |= *buf++ << 24;
buf += 3;
s2 -= 11;
if ( ( (pkt->m_packetType == RTMP_PACKET_TYPE_AUDIO
|| pkt->m_packetType == RTMP_PACKET_TYPE_VIDEO) &&
!pkt->m_nTimeStamp) || pkt->m_packetType == RTMP_PACKET_TYPE_INFO) {
pkt->m_headerType = RTMP_PACKET_SIZE_LARGE;
} else {
pkt->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}
if (!RTMPPacket_Alloc (pkt, pkt->m_nBodySize)) {
RTMP_Log (RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__);
return FALSE;
}
enc = pkt->m_body;
} else {
enc = pkt->m_body + pkt->m_nBytesRead;
}
num = pkt->m_nBodySize - pkt->m_nBytesRead;
if (num > s2) {
num = s2;
}
memcpy (enc, buf, num);
pkt->m_nBytesRead += num;
s2 -= num;
buf += num;
if (pkt->m_nBytesRead == pkt->m_nBodySize) {
ret = RTMP_SendPacket (r, pkt, FALSE);
RTMPPacket_Free (pkt);
pkt->m_nBytesRead = 0;
if (!ret) {
return -1;
}
buf += 4;
s2 -= 4;
if (s2 < 0) {
break;
}
}
}
return size+s2;
}
int main (int argc, const char** argv)
{
FILE* flv;
RTMP* rtmp;
RTMPPacket rtmpPacket;
flvtag_t tag;
int32_t timestamp = 0;
int has_audio, has_video;
char* url = 0;
if (2 >= argc) {
fprintf (stderr,"Usage %s [input] [url]\n",argv[0]);
}
url = (char*) argv[2];
flv = flv_open_read (argv[1]);
if (! flv) {
fprintf (stderr,"Could not open %s\n",argv[1]);
return EXIT_FAILURE;
}
if (! flv_read_header (flv, &has_audio, &has_video)) {
fprintf (stderr,"Not an flv file %s\n",argv[1]);
return EXIT_FAILURE;
}
flvtag_init (&tag);
rtmp = RTMP_Alloc();
RTMP_Init (rtmp);
fprintf (stderr,"Connecting to %s\n", url);
RTMP_SetupURL (rtmp, url);
RTMP_EnableWrite (rtmp);
RTMP_Connect (rtmp, NULL);
RTMP_ConnectStream (rtmp, 0);
memset (&rtmpPacket, 0, sizeof (RTMPPacket));
if (! RTMP_IsConnected (rtmp)) {
fprintf (stderr,"RTMP_IsConnected() Error\n");
return EXIT_FAILURE;
}
while (flv_read_tag (flv,&tag)) {
if (! RTMP_IsConnected (rtmp) || RTMP_IsTimedout (rtmp)) {
fprintf (stderr,"RTMP_IsConnected() Error\n");
return EXIT_FAILURE;
}
if (flvtag_timestamp (&tag) > timestamp) {
usleep (1000 * (flvtag_timestamp (&tag) - timestamp));
timestamp = flvtag_timestamp (&tag);
}
MyRTMP_Write (rtmp, (const char*) flvtag_raw_data (&tag),flvtag_raw_size (&tag));
// Handle RTMP ping and such
fd_set sockset; struct timeval timeout = {0,0};
FD_ZERO (&sockset); FD_SET (RTMP_Socket (rtmp), &sockset);
register int result = select (RTMP_Socket (rtmp) + 1, &sockset, NULL, NULL, &timeout);
if (result == 1 && FD_ISSET (RTMP_Socket (rtmp), &sockset)) {
RTMP_ReadPacket (rtmp, &rtmpPacket);
if (! RTMPPacket_IsReady (&rtmpPacket)) {
fprintf (stderr,"Received RTMP packet\n");
RTMP_ClientPacket (rtmp,&rtmpPacket);
RTMPPacket_Free (&rtmpPacket);
}
}
}
return EXIT_SUCCESS;
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "srt.h"
#include "scc.h"
#define MAX_SCC_SIZE (10*1024*1024)
#define MAX_READ_SIZE 4096
#define MAX_CC 128
size_t read_file (FILE* file, utf8_char_t* data, size_t size)
{
size_t read, totl = 0;
while (0 < (read = fread (data,1,MAX_READ_SIZE<size?MAX_READ_SIZE:size,file))) {
totl += read; data += read; size -= read;
}
return totl;
}
srt_t* scc2srt (const char* data)
{
double pts;
size_t line_size = 0;
int cc_idx, count, i;
srt_t* srt = 0, *head = 0;
caption_frame_t frame;
uint16_t cc_data[MAX_CC];
while (0 < (line_size = utf8_line_length (data))) {
caption_frame_init (&frame);
int cc_count = scc_to_608 (data, &pts, (uint16_t*) &cc_data, MAX_CC);
data += line_size;
data += utf8_line_length (data); // skip empty line
// fprintf (stderr,"%f, %d| %.*s\n", pts, cc_count, (int) line_size,data);
for (cc_idx = 0 ; cc_idx < cc_count ; ++cc_idx) {
// eia608_dump (cc_data[cc_idx]);
caption_frame_decode (&frame,cc_data[cc_idx],pts);
}
// utf8_char_t buff[CAPTION_FRAME_DUMP_BUF_SIZE];
// size_t size = caption_frame_dump (&frame, buff);
// fprintf (stderr,"%s\n", buff);
srt = srt_from_caption_frame (&frame,srt,&head);
}
return head;
}
int main (int argc, char** argv)
{
char frame_buf[CAPTION_FRAME_DUMP_BUF_SIZE];
if (argc < 2) {
return 0;
}
FILE* file = fopen (argv[1],"r");
if (! file) {
return 0;
}
utf8_char_t* data = malloc (MAX_SCC_SIZE);
read_file (file,data,MAX_SCC_SIZE);
srt_t* srt = scc2srt (data);
srt_dump (srt);
srt_free (srt);
free (data);
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "srt.h"
#define MAX_SRT_SIZE (10*1024*1024)
#define MAX_READ_SIZE 4096
size_t read_file (FILE* file, utf8_char_t* data, size_t size)
{
size_t read, totl = 0;
while (0 < (read = fread (data,1,MAX_READ_SIZE<size?MAX_READ_SIZE:size,file))) {
totl += read; data += read; size -= read;
}
return totl;
}
int main (int argc, char** argv)
{
srt_t* srt;
caption_frame_t frame;
char frame_buf[CAPTION_FRAME_DUMP_BUF_SIZE];
if (argc < 2) {
return 0;
}
FILE* file = fopen (argv[1],"r");
if (! file) {
return 0;
}
utf8_char_t* data = malloc (MAX_SRT_SIZE);
size_t size = read_file (file,data,MAX_SRT_SIZE);
srt_t* head = srt_parse (data,size);
vtt_dump (head);
srt_free (head);
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "srt.h"
#include "avc.h"
// #include "sei.h"
#define MAX_SRT_SIZE (10*1024*1024)
#define MAX_READ_SIZE 4096
size_t read_file (FILE* file, utf8_char_t* data, size_t size)
{
size_t read, totl = 0;
while (0 < (read = fread (data,1,MAX_READ_SIZE<size?MAX_READ_SIZE:size,file))) {
totl += read; data += read; size -= read;
}
return totl;
}
int main (int argc, char** argv)
{
srt_t* srt;
caption_frame_t frame;
if (argc < 2) {
return 0;
}
FILE* file = fopen (argv[1],"r");
if (! file) {
return 0;
}
utf8_char_t* data = (utf8_char_t*) malloc (MAX_SRT_SIZE);
size_t size = read_file (file,data,MAX_SRT_SIZE);
srt_t* head = srt_parse (data,size);
for (srt = head ; srt ; srt = srt->next) {
caption_frame_init (&frame);
srt_to_caption_frame (srt,&frame);
caption_frame_dump (&frame);
}
srt_free (head);
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include "ts.h"
#include <string.h>
void ts_init (ts_t* ts)
{
memset (ts,0,sizeof (ts_t));
}
static int64_t ts_parse_pts (const uint8_t* data)
{
// 0000 1110 1111 1111 1111 1110 1111 1111 1111 1110
uint64_t pts = 0;
pts |= (uint64_t) (data[0] & 0x0E) << 29;
pts |= (uint64_t) (data[1] & 0xFF) << 22;
pts |= (uint64_t) (data[2] & 0xFE) << 14;
pts |= (uint64_t) (data[3] & 0xFF) << 7;
pts |= (uint64_t) (data[4] & 0xFE) >> 1;
return pts;
}
int ts_parse_packet (ts_t* ts, const uint8_t* data)
{
size_t i = 0;
int pusi = !! (data[i + 1] & 0x40); // Payload Unit Start Indicator
int16_t pid = ( (data[i + 1] & 0x1F) << 8) | data[i + 2]; // PID
int adaption_present = !! (data[i + 3] & 0x20); // Adaptation field exist
int payload_present = !! (data[i + 3] & 0x10); // Contains payload
i += 4;
ts->data = 0;
ts->size = 0;
if (adaption_present) {
uint8_t adaption_length = data[i + 0]; // adaption field length
i += 1 + adaption_length;
}
if (pid == 0) {
if (payload_present) {
// Skip the payload.
i += data[i] + 1;
}
ts->pmtpid = ( (data[i + 10] & 0x1F) << 8) | data[i + 11];
} else if (pid == ts->pmtpid) {
// PMT
if (payload_present) {
// Skip the payload.
i += data[i] + 1;
}
uint16_t section_length = ( (data[i + 1] & 0x0F) << 8) | data[i + 2];
int current = data[i + 5] & 0x01;
int16_t program_info_length = ( (data[i + 10] & 0x0F) << 8) | data[i + 11];
int16_t descriptor_loop_length = section_length - (9 + program_info_length + 4); // 4 for the crc
i += 12 + program_info_length;
if (current) {
while (descriptor_loop_length >= 5) {
uint8_t stream_type = data[i];
int16_t elementary_pid = ( (data[i + 1] & 0x1F) << 8) | data[i + 2];
int16_t esinfo_length = ( (data[i + 3] & 0x0F) << 8) | data[i + 4];
if (0x1B == stream_type) {
ts->avcpid = elementary_pid;
}
i += 5 + esinfo_length;
descriptor_loop_length -= 5 + esinfo_length;
}
}
} else if (payload_present && pid == ts->avcpid) {
if (pusi) {
// int data_alignment = !! (data[i + 6] & 0x04);
int has_pts = !! (data[i + 7] & 0x80);
int has_dts = !! (data[i + 7] & 0x40);
uint8_t header_length = data[i + 8];
if (has_pts) {
ts->pts = ts_parse_pts (&data[i + 9]);
ts->dts = has_dts ? ts_parse_pts (&data[i + 14]) : ts->pts;
}
i += 9 + header_length;
}
ts->data = &data[i];
ts->size = TS_PACKET_SIZE-i;
return LIBCAPTION_READY;
}
return LIBCAPTION_OK;
}
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include "ts.h"
#include "srt.h"
#include "avc.h"
#include <stdio.h>
int main (int argc, char** argv)
{
const char* path = argv[1];
ts_t ts;
sei_t sei;
avcnalu_t nalu;
srt_t* srt = 0, *head = 0;
caption_frame_t frame;
uint8_t pkt[TS_PACKET_SIZE];
ts_init (&ts);
avcnalu_init (&nalu);
caption_frame_init (&frame);
FILE* file = fopen (path,"rb+");
while (TS_PACKET_SIZE == fread (&pkt[0],1,TS_PACKET_SIZE, file)) {
switch (ts_parse_packet (&ts,&pkt[0])) {
case LIBCAPTION_OK:
// fprintf (stderr,"read ts packet\n");
break;
case LIBCAPTION_READY: {
// fprintf (stderr,"read ts packet DATA\n");
while (ts.size) {
// fprintf (stderr,"ts.size %d (%02X%02X%02X%02X)\n",ts.size, ts.data[0], ts.data[1], ts.data[2], ts.data[3]);
switch (avcnalu_parse_annexb (&nalu, &ts.data, &ts.size)) {
case LIBCAPTION_OK:
break;
case LIBCAPTION_ERROR:
// fprintf (stderr,"LIBCAPTION_ERROR == avcnalu_parse_annexb()\n");
avcnalu_init (&nalu);
break;
case LIBCAPTION_READY: {
if (6 == avcnalu_type (&nalu)) {
// fprintf (stderr,"NALU %d (%d)\n", avcnalu_type (&nalu), avcnalu_size (&nalu));
sei_init (&sei);
sei_parse_avcnalu (&sei, &nalu, ts_dts_seconds (&ts), ts_cts_seconds (&ts));
// sei_dump (&sei);
if (LIBCAPTION_READY == sei_to_caption_frame (&sei,&frame)) {
// caption_frame_dump (&frame);
srt = srt_from_caption_frame (&frame,srt,&head);
// srt_dump (srt);
}
sei_free (&sei);
}
avcnalu_init (&nalu);
} break;
}
}
} break;
case LIBCAPTION_ERROR:
// fprintf (stderr,"read ts packet ERROR\n");
break;
}
}
srt_dump (head);
srt_free (head);
return 1;
}
此差异已折叠。
此差异已折叠。
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -22,92 +22,138 @@
/* THE SOFTWARE. */
/**********************************************************************************************/
#include "cea708.h"
#include <float.h>
#include <memory.h>
#include <stdio.h>
int cea708_cc_count (user_data_t* data)
int cea708_cc_count(user_data_t* data)
{
return data->cc_count;
}
uint16_t cea708_cc_data (user_data_t* data, int index, int* valid, cea708_cc_type_t* type)
uint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_cc_type_t* type)
{
(*valid) = data->cc_data[index].cc_valid;
(*type) = data->cc_data[index].cc_type;
return data->cc_data[index].cc_data;
}
int cea708_init (cea708_t* cea708)
int cea708_init(cea708_t* cea708, double timestamp)
{
memset (cea708,0,sizeof (cea708_t));
memset(cea708, 0, sizeof(cea708_t));
cea708->country = country_united_states;
cea708->provider = t35_provider_atsc;
cea708->user_identifier = ('G'<<24) | ('A'<<16) | ('9'<<8) | ('4');
cea708->atsc1_data_user_data_type_code = 3; //what does 3 mean here?
cea708->user_identifier = GA94;
cea708->user_data_type_code = 3;
cea708->directv_user_data_length = 0;
///////////
cea708->user_data.process_em_data_flag = 0;
cea708->user_data.process_cc_data_flag = 1;
cea708->user_data.additional_data_flag = 0;
cea708->user_data.em_data = 0xFF;
cea708->user_data.cc_count = 0;
cea708->timestamp = timestamp;
return 1;
}
void cea708_parse_user_data_type_strcture(const uint8_t* data, size_t size, user_data_t* user_data)
{
memset(user_data, 0, sizeof(user_data_t));
user_data->process_em_data_flag = !!(data[0] & 0x80);
user_data->process_cc_data_flag = !!(data[0] & 0x40);
user_data->additional_data_flag = !!(data[0] & 0x20);
user_data->cc_count = (data[0] & 0x1F);
user_data->em_data = data[1];
data += 2, size -= 2;
for (int i = 0; 3 < size && i < (int)user_data->cc_count; ++i, data += 3, size -= 3) {
user_data->cc_data[i].marker_bits = data[0] >> 3;
user_data->cc_data[i].cc_valid = data[0] >> 2;
user_data->cc_data[i].cc_type = data[0] >> 0;
user_data->cc_data[i].cc_data = data[1] << 8 | data[2];
}
}
// 00 00 00 06 C1 FF FC 34 B9 FF : onCaptionInfo.
int cea708_parse (uint8_t* data, size_t size, cea708_t* cea708)
libcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708)
{
int i;
cea708->country = (itu_t_t35_country_code_t) (data[0]);
cea708->provider = (itu_t_t35_provider_code_t) ( (data[1] <<8) | data[2]);
cea708->atsc1_data_user_data_type_code = 0;
if (3 > size) {
goto error;
}
// I think the first few bytes need to be handled in mpeg
cea708->country = (itu_t_t35_country_code_t)(data[0]);
cea708->provider = (itu_t_t35_provider_code_t)((data[1] << 8) | data[2]);
cea708->user_identifier = 0;
data += 3; size -= 3;
cea708->user_data_type_code = 0;
data += 3, size -= 3;
if (t35_provider_atsc == cea708->provider) {
// GA94
cea708->user_identifier = (data[0] <<24) | (data[1] <<16) | (data[2] <<8) | data[3];
data += 4; size -= 4;
if (4 > size) {
goto error;
}
cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
data += 4, size -= 4;
}
// Im not sure what this extra byt is. It sonly seesm to come up in onCaptionInfo
// where country and provider are zero
if (0 == cea708->provider) {
data += 1; size -= 1;
// Im not sure what this extra byte is. It sonly seesm to come up in onCaptionInfo
// h264 spec seems to describe this
if (0 == cea708->provider && 0 == cea708->country) {
if (1 > size) {
goto error;
}
data += 1, size -= 1;
} else if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) {
cea708->atsc1_data_user_data_type_code = data[0];
data += 1; size -= 1;
if (1 > size) {
goto error;
}
cea708->user_data_type_code = data[0];
data += 1, size -= 1;
}
if (t35_provider_direct_tv == cea708->provider) {
if (1 > size) {
goto error;
}
cea708->directv_user_data_length = data[0];
data += 1; size -= 1;
data += 1, size -= 1;
}
// TODO I believe this is condational on the above.
cea708->user_data.process_em_data_flag = !! (data[0]&0x80);
cea708->user_data.process_cc_data_flag = !! (data[0]&0x40);
cea708->user_data.additional_data_flag = !! (data[0]&0x20);
cea708->user_data.cc_count = (data[0]&0x1F);
cea708->user_data.em_data = data[1];
data += 2; size -= 2;
if (3 == cea708->user_data_type_code && 2 <= size) {
cea708_parse_user_data_type_strcture(data, size, &cea708->user_data);
} else if (4 == cea708->user_data_type_code) {
// additional_CEA_608_data
} else if (5 == cea708->user_data_type_code) {
// luma_PAM_data
} else {
// ATSC_reserved_user_data
}
if (size < 3 * cea708->user_data.cc_count) {
cea708_init (cea708);
return 0;
return LIBCAPTION_OK;
error:
return LIBCAPTION_ERROR;
}
libcaption_stauts_t cea708_parse_h262(const uint8_t* data, size_t size, cea708_t* cea708)
{
if (!data || 7 > size) {
return LIBCAPTION_ERROR;
}
for (i = 0 ; i < (int) cea708->user_data.cc_count ; ++i) {
cea708->user_data.cc_data[i].marker_bits = data[0]>>3;
cea708->user_data.cc_data[i].cc_valid = data[0]>>2;
cea708->user_data.cc_data[i].cc_type = data[0]>>0;
cea708->user_data.cc_data[i].cc_data = data[1]<<8|data[2];
data += 3; size -= 3;
cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
cea708->user_data_type_code = data[4];
if (3 == cea708->user_data_type_code) {
cea708_parse_user_data_type_strcture(data + 5, size - 5, &cea708->user_data);
}
return 1;
return LIBCAPTION_OK;
}
int cea708_add_cc_data (cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data)
int cea708_add_cc_data(cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data)
{
if (31 <= cea708->user_data.cc_count) {
return 0;
......@@ -121,13 +167,16 @@ int cea708_add_cc_data (cea708_t* cea708, int valid, cea708_cc_type_t type, uint
return 1;
}
int cea708_render (cea708_t* cea708, uint8_t* data, size_t size)
int cea708_render(cea708_t* cea708, uint8_t* data, size_t size)
{
int i; size_t total = 0;
int i;
size_t total = 0;
data[0] = cea708->country;
data[1] = cea708->provider>>8;
data[2] = cea708->provider>>0;
total += 3; data += 3; size -= 3;
data[1] = cea708->provider >> 8;
data[2] = cea708->provider >> 0;
total += 3;
data += 3;
size -= 3;
if (t35_provider_atsc == cea708->provider) {
......@@ -135,71 +184,98 @@ int cea708_render (cea708_t* cea708, uint8_t* data, size_t size)
data[1] = cea708->user_identifier >> 16;
data[2] = cea708->user_identifier >> 8;
data[3] = cea708->user_identifier >> 0;
total += 4; data += 4; size -= 4;
total += 4;
data += 4;
size -= 4;
}
if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) {
data[0] = cea708->atsc1_data_user_data_type_code;
total += 1; data += 1; size -= 1;
data[0] = cea708->user_data_type_code;
total += 1;
data += 1;
size -= 1;
}
if (t35_provider_direct_tv == cea708->provider) {
data[0] = cea708->directv_user_data_length;
total += 1; data += 1; size -= 1;
total += 1;
data += 1;
size -= 1;
}
data[1] = cea708->user_data.em_data;
data[0] = (cea708->user_data.process_em_data_flag?0x80:0x00)
| (cea708->user_data.process_cc_data_flag?0x40:0x00)
| (cea708->user_data.additional_data_flag?0x20:0x00)
| (cea708->user_data.cc_count & 0x1F);
total += 2; data += 2; size -= 2;
for (i = 0 ; i < (int) cea708->user_data.cc_count ; ++i) {
data[0] = (cea708->user_data.cc_data[i].marker_bits<<3)
| (data[0] = cea708->user_data.cc_data[i].cc_valid<<2)
| (data[0] = cea708->user_data.cc_data[i].cc_type);
data[1] = cea708->user_data.cc_data[i].cc_data>>8;
data[2] = cea708->user_data.cc_data[i].cc_data>>0;
total += 3; data += 3; size -= 3;
data[0] = (cea708->user_data.process_em_data_flag ? 0x80 : 0x00)
| (cea708->user_data.process_cc_data_flag ? 0x40 : 0x00)
| (cea708->user_data.additional_data_flag ? 0x20 : 0x00)
| (cea708->user_data.cc_count & 0x1F);
total += 2;
data += 2;
size -= 2;
for (i = 0; i < (int)cea708->user_data.cc_count; ++i) {
data[0] = (cea708->user_data.cc_data[i].marker_bits << 3) | (cea708->user_data.cc_data[i].cc_valid << 2) | cea708->user_data.cc_data[i].cc_type;
data[1] = cea708->user_data.cc_data[i].cc_data >> 8;
data[2] = cea708->user_data.cc_data[i].cc_data >> 0;
total += 3;
data += 3;
size -= 3;
}
data[0] = 0xFF;
return (int) (total + 1);
data[0] = 0xFF; //marker bits
return (int)(total + 1);
}
cc_data_t cea708_encode_cc_data (int cc_valid, cea708_cc_type_t type, uint16_t cc_data)
cc_data_t cea708_encode_cc_data(int cc_valid, cea708_cc_type_t type, uint16_t cc_data)
{
cc_data_t data = { 0x1F, cc_valid,type,cc_data};
cc_data_t data = { 0x1F, cc_valid, type, cc_data };
return data;
}
void cea708_dump (cea708_t* cea708)
void cea708_dump(cea708_t* cea708)
{
int i;
for (i = 0 ; i < (int) cea708->user_data.cc_count ; ++i) {
cea708_cc_type_t type; int valid;
uint16_t cc_data = cea708_cc_data (&cea708->user_data, i, &valid, &type);
fprintf(stderr, "itu_t_t35_country_code_t %d\n", cea708->country);
fprintf(stderr, "itu_t_t35_provider_code_t %d\n", cea708->provider);
fprintf(stderr, "user_identifier %c%c%c%c\n",
(cea708->user_identifier >> 24) & 0xFF, (cea708->user_identifier >> 16) & 0xFF,
(cea708->user_identifier >> 8) & 0xFF, cea708->user_identifier & 0xFF);
fprintf(stderr, "user_data_type_code %d\n", cea708->user_data_type_code);
fprintf(stderr, "directv_user_data_length %d\n", cea708->directv_user_data_length);
fprintf(stderr, "user_data.process_em_data_flag %d\n", cea708->user_data.process_em_data_flag);
fprintf(stderr, "user_data.process_cc_data_flag %d\n", cea708->user_data.process_cc_data_flag);
fprintf(stderr, "user_data.additional_data_flag %d\n", cea708->user_data.additional_data_flag);
fprintf(stderr, "user_data.cc_count %d\n", cea708->user_data.cc_count);
fprintf(stderr, "user_data.em_data %d\n", cea708->user_data.em_data);
for (i = 0; i < (int)cea708->user_data.cc_count; ++i) {
int valid;
cea708_cc_type_t type;
uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type);
if (valid && cc_type_ntsc_cc_field_1 == type) {
eia608_dump (cc_data);
eia608_dump(cc_data);
} else {
fprintf(stderr, "user_data.cc_data[%d] cc_valid: %s, cc_type: %d, cc_data: %04x\n", i, cea708->user_data.cc_data[i].cc_valid ? "true" : "false", cea708->user_data.cc_data[i].cc_type, cea708->user_data.cc_data[i].cc_data);
}
}
}
libcaption_stauts_t cea708_to_caption_frame (caption_frame_t* frame, cea708_t* cea708, double pts)
libcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708)
{
int i, count = cea708_cc_count (&cea708->user_data);
int i, count = cea708_cc_count(&cea708->user_data);
libcaption_stauts_t status = LIBCAPTION_OK;
for (i = 0 ; i < count ; ++i) {
cea708_cc_type_t type; int valid;
uint16_t cc_data = cea708_cc_data (&cea708->user_data, i, &valid, &type);
if (GA94 == cea708->user_identifier) {
for (i = 0; i < count; ++i) {
int valid;
cea708_cc_type_t type;
uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type);
if (valid && cc_type_ntsc_cc_field_1 == type) {
status = libcaption_status_update (status,caption_frame_decode (frame,cc_data, pts));
if (valid && cc_type_ntsc_cc_field_1 == type) {
status = libcaption_status_update(status, caption_frame_decode(frame, cc_data, cea708->timestamp));
}
}
}
......
struct dtvcc_packet_t* dtvcc_packet_start(uint8_t cc_data1, uint8_t cc_data2)
{
unsigned int packet_size = cc_data1 & 0x3F;
packet_size = (0 == packet_size) ? 64 * 8 - 1 : (packet_size * 8 - 1)
unsigned int packet_size_bytes
= dtvcc_packet_t* dvtcc = malloc(sizeof(dtvcc_packet_t) + packet_size * 2 - 1);
dvtcc->service_number = (cc_data1 0xC0) >> 6;
dvtcc->packet_size = packet_size;
dvtcc->service_number = DVTCC_SERVICE_NUMBER_UNKNOWN;
}
void dtvcc_packet_data(struct dtvcc_packet_t* dvtcc, uint8_t cc_data1, uint8_t cc_data2)
{
if (dvtcc->service_number) {
if (7 == dvtcc->service_number) {
dvtcc->service_number
}
}
}
此差异已折叠。
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -29,26 +29,180 @@
// 144 - 159: Extended Western European character set : Portuguese
// 160 - 175: Extended Western European character set : German/Danish
const char* eia608_char_map[] = {
EIA608_CHAR_SPACE, EIA608_CHAR_EXCLAMATION_MARK, EIA608_CHAR_QUOTATION_MARK, EIA608_CHAR_NUMBER_SIGN, EIA608_CHAR_DOLLAR_SIGN, EIA608_CHAR_PERCENT_SIGN, EIA608_CHAR_AMPERSAND, EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK,
EIA608_CHAR_LEFT_PARENTHESIS, EIA608_CHAR_RIGHT_PARENTHESIS, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE, EIA608_CHAR_PLUS_SIGN, EIA608_CHAR_COMMA, EIA608_CHAR_HYPHEN_MINUS, EIA608_CHAR_FULL_STOP, EIA608_CHAR_SOLIDUS,
EIA608_CHAR_DIGIT_ZERO, EIA608_CHAR_DIGIT_ONE, EIA608_CHAR_DIGIT_TWO, EIA608_CHAR_DIGIT_THREE, EIA608_CHAR_DIGIT_FOUR, EIA608_CHAR_DIGIT_FIVE, EIA608_CHAR_DIGIT_SIX, EIA608_CHAR_DIGIT_SEVEN, EIA608_CHAR_DIGIT_EIGHT,
EIA608_CHAR_DIGIT_NINE, EIA608_CHAR_COLON, EIA608_CHAR_SEMICOLON, EIA608_CHAR_LESS_THAN_SIGN, EIA608_CHAR_EQUALS_SIGN, EIA608_CHAR_GREATER_THAN_SIGN, EIA608_CHAR_QUESTION_MARK, EIA608_CHAR_COMMERCIAL_AT,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A, EIA608_CHAR_LATIN_CAPITAL_LETTER_B, EIA608_CHAR_LATIN_CAPITAL_LETTER_C, EIA608_CHAR_LATIN_CAPITAL_LETTER_D, EIA608_CHAR_LATIN_CAPITAL_LETTER_E, EIA608_CHAR_LATIN_CAPITAL_LETTER_F, EIA608_CHAR_LATIN_CAPITAL_LETTER_G, EIA608_CHAR_LATIN_CAPITAL_LETTER_H,
EIA608_CHAR_LATIN_CAPITAL_LETTER_I, EIA608_CHAR_LATIN_CAPITAL_LETTER_J, EIA608_CHAR_LATIN_CAPITAL_LETTER_K, EIA608_CHAR_LATIN_CAPITAL_LETTER_L, EIA608_CHAR_LATIN_CAPITAL_LETTER_M, EIA608_CHAR_LATIN_CAPITAL_LETTER_N, EIA608_CHAR_LATIN_CAPITAL_LETTER_O, EIA608_CHAR_LATIN_CAPITAL_LETTER_P,
EIA608_CHAR_LATIN_CAPITAL_LETTER_Q, EIA608_CHAR_LATIN_CAPITAL_LETTER_R, EIA608_CHAR_LATIN_CAPITAL_LETTER_S, EIA608_CHAR_LATIN_CAPITAL_LETTER_T, EIA608_CHAR_LATIN_CAPITAL_LETTER_U, EIA608_CHAR_LATIN_CAPITAL_LETTER_V, EIA608_CHAR_LATIN_CAPITAL_LETTER_W, EIA608_CHAR_LATIN_CAPITAL_LETTER_X,
EIA608_CHAR_LATIN_CAPITAL_LETTER_Y, EIA608_CHAR_LATIN_CAPITAL_LETTER_Z, EIA608_CHAR_LEFT_SQUARE_BRACKET, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE, EIA608_CHAR_RIGHT_SQUARE_BRACKET, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE,
EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE, EIA608_CHAR_LATIN_SMALL_LETTER_A, EIA608_CHAR_LATIN_SMALL_LETTER_B, EIA608_CHAR_LATIN_SMALL_LETTER_C, EIA608_CHAR_LATIN_SMALL_LETTER_D, EIA608_CHAR_LATIN_SMALL_LETTER_E, EIA608_CHAR_LATIN_SMALL_LETTER_F, EIA608_CHAR_LATIN_SMALL_LETTER_G, EIA608_CHAR_LATIN_SMALL_LETTER_H,
EIA608_CHAR_LATIN_SMALL_LETTER_I, EIA608_CHAR_LATIN_SMALL_LETTER_J, EIA608_CHAR_LATIN_SMALL_LETTER_K, EIA608_CHAR_LATIN_SMALL_LETTER_L, EIA608_CHAR_LATIN_SMALL_LETTER_M, EIA608_CHAR_LATIN_SMALL_LETTER_N, EIA608_CHAR_LATIN_SMALL_LETTER_O, EIA608_CHAR_LATIN_SMALL_LETTER_P,
EIA608_CHAR_LATIN_SMALL_LETTER_Q, EIA608_CHAR_LATIN_SMALL_LETTER_R, EIA608_CHAR_LATIN_SMALL_LETTER_S, EIA608_CHAR_LATIN_SMALL_LETTER_T, EIA608_CHAR_LATIN_SMALL_LETTER_U, EIA608_CHAR_LATIN_SMALL_LETTER_V, EIA608_CHAR_LATIN_SMALL_LETTER_W, EIA608_CHAR_LATIN_SMALL_LETTER_X,
EIA608_CHAR_LATIN_SMALL_LETTER_Y, EIA608_CHAR_LATIN_SMALL_LETTER_Z, EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA, EIA608_CHAR_DIVISION_SIGN, EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE, EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE, EIA608_CHAR_FULL_BLOCK,
EIA608_CHAR_REGISTERED_SIGN, EIA608_CHAR_DEGREE_SIGN, EIA608_CHAR_VULGAR_FRACTION_ONE_HALF, EIA608_CHAR_INVERTED_QUESTION_MARK, EIA608_CHAR_TRADE_MARK_SIGN, EIA608_CHAR_CENT_SIGN, EIA608_CHAR_POUND_SIGN, EIA608_CHAR_EIGHTH_NOTE,
EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE, EIA608_CHAR_NO_BREAK_SPACE, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS, EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK, EIA608_CHAR_INVERTED_EXCLAMATION_MARK,
EIA608_CHAR_ASTERISK, EIA608_CHAR_APOSTROPHE, EIA608_CHAR_EM_DASH, EIA608_CHAR_COPYRIGHT_SIGN, EIA608_CHAR_SERVICE_MARK, EIA608_CHAR_BULLET, EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK, EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS, EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX, EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, EIA608_CHAR_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE, EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE,
EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE, EIA608_CHAR_LEFT_CURLY_BRACKET, EIA608_CHAR_RIGHT_CURLY_BRACKET, EIA608_CHAR_REVERSE_SOLIDUS, EIA608_CHAR_CIRCUMFLEX_ACCENT, EIA608_CHAR_LOW_LINE, EIA608_CHAR_VERTICAL_LINE, EIA608_CHAR_TILDE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S, EIA608_CHAR_YEN_SIGN, EIA608_CHAR_CURRENCY_SIGN, EIA608_CHAR_BROKEN_BAR,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE, EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT,
EIA608_CHAR_SPACE,
EIA608_CHAR_EXCLAMATION_MARK,
EIA608_CHAR_QUOTATION_MARK,
EIA608_CHAR_NUMBER_SIGN,
EIA608_CHAR_DOLLAR_SIGN,
EIA608_CHAR_PERCENT_SIGN,
EIA608_CHAR_AMPERSAND,
EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK,
EIA608_CHAR_LEFT_PARENTHESIS,
EIA608_CHAR_RIGHT_PARENTHESIS,
EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE,
EIA608_CHAR_PLUS_SIGN,
EIA608_CHAR_COMMA,
EIA608_CHAR_HYPHEN_MINUS,
EIA608_CHAR_FULL_STOP,
EIA608_CHAR_SOLIDUS,
EIA608_CHAR_DIGIT_ZERO,
EIA608_CHAR_DIGIT_ONE,
EIA608_CHAR_DIGIT_TWO,
EIA608_CHAR_DIGIT_THREE,
EIA608_CHAR_DIGIT_FOUR,
EIA608_CHAR_DIGIT_FIVE,
EIA608_CHAR_DIGIT_SIX,
EIA608_CHAR_DIGIT_SEVEN,
EIA608_CHAR_DIGIT_EIGHT,
EIA608_CHAR_DIGIT_NINE,
EIA608_CHAR_COLON,
EIA608_CHAR_SEMICOLON,
EIA608_CHAR_LESS_THAN_SIGN,
EIA608_CHAR_EQUALS_SIGN,
EIA608_CHAR_GREATER_THAN_SIGN,
EIA608_CHAR_QUESTION_MARK,
EIA608_CHAR_COMMERCIAL_AT,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A,
EIA608_CHAR_LATIN_CAPITAL_LETTER_B,
EIA608_CHAR_LATIN_CAPITAL_LETTER_C,
EIA608_CHAR_LATIN_CAPITAL_LETTER_D,
EIA608_CHAR_LATIN_CAPITAL_LETTER_E,
EIA608_CHAR_LATIN_CAPITAL_LETTER_F,
EIA608_CHAR_LATIN_CAPITAL_LETTER_G,
EIA608_CHAR_LATIN_CAPITAL_LETTER_H,
EIA608_CHAR_LATIN_CAPITAL_LETTER_I,
EIA608_CHAR_LATIN_CAPITAL_LETTER_J,
EIA608_CHAR_LATIN_CAPITAL_LETTER_K,
EIA608_CHAR_LATIN_CAPITAL_LETTER_L,
EIA608_CHAR_LATIN_CAPITAL_LETTER_M,
EIA608_CHAR_LATIN_CAPITAL_LETTER_N,
EIA608_CHAR_LATIN_CAPITAL_LETTER_O,
EIA608_CHAR_LATIN_CAPITAL_LETTER_P,
EIA608_CHAR_LATIN_CAPITAL_LETTER_Q,
EIA608_CHAR_LATIN_CAPITAL_LETTER_R,
EIA608_CHAR_LATIN_CAPITAL_LETTER_S,
EIA608_CHAR_LATIN_CAPITAL_LETTER_T,
EIA608_CHAR_LATIN_CAPITAL_LETTER_U,
EIA608_CHAR_LATIN_CAPITAL_LETTER_V,
EIA608_CHAR_LATIN_CAPITAL_LETTER_W,
EIA608_CHAR_LATIN_CAPITAL_LETTER_X,
EIA608_CHAR_LATIN_CAPITAL_LETTER_Y,
EIA608_CHAR_LATIN_CAPITAL_LETTER_Z,
EIA608_CHAR_LEFT_SQUARE_BRACKET,
EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE,
EIA608_CHAR_RIGHT_SQUARE_BRACKET,
EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE,
EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE,
EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE,
EIA608_CHAR_LATIN_SMALL_LETTER_A,
EIA608_CHAR_LATIN_SMALL_LETTER_B,
EIA608_CHAR_LATIN_SMALL_LETTER_C,
EIA608_CHAR_LATIN_SMALL_LETTER_D,
EIA608_CHAR_LATIN_SMALL_LETTER_E,
EIA608_CHAR_LATIN_SMALL_LETTER_F,
EIA608_CHAR_LATIN_SMALL_LETTER_G,
EIA608_CHAR_LATIN_SMALL_LETTER_H,
EIA608_CHAR_LATIN_SMALL_LETTER_I,
EIA608_CHAR_LATIN_SMALL_LETTER_J,
EIA608_CHAR_LATIN_SMALL_LETTER_K,
EIA608_CHAR_LATIN_SMALL_LETTER_L,
EIA608_CHAR_LATIN_SMALL_LETTER_M,
EIA608_CHAR_LATIN_SMALL_LETTER_N,
EIA608_CHAR_LATIN_SMALL_LETTER_O,
EIA608_CHAR_LATIN_SMALL_LETTER_P,
EIA608_CHAR_LATIN_SMALL_LETTER_Q,
EIA608_CHAR_LATIN_SMALL_LETTER_R,
EIA608_CHAR_LATIN_SMALL_LETTER_S,
EIA608_CHAR_LATIN_SMALL_LETTER_T,
EIA608_CHAR_LATIN_SMALL_LETTER_U,
EIA608_CHAR_LATIN_SMALL_LETTER_V,
EIA608_CHAR_LATIN_SMALL_LETTER_W,
EIA608_CHAR_LATIN_SMALL_LETTER_X,
EIA608_CHAR_LATIN_SMALL_LETTER_Y,
EIA608_CHAR_LATIN_SMALL_LETTER_Z,
EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA,
EIA608_CHAR_DIVISION_SIGN,
EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE,
EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE,
EIA608_CHAR_FULL_BLOCK,
EIA608_CHAR_REGISTERED_SIGN,
EIA608_CHAR_DEGREE_SIGN,
EIA608_CHAR_VULGAR_FRACTION_ONE_HALF,
EIA608_CHAR_INVERTED_QUESTION_MARK,
EIA608_CHAR_TRADE_MARK_SIGN,
EIA608_CHAR_CENT_SIGN,
EIA608_CHAR_POUND_SIGN,
EIA608_CHAR_EIGHTH_NOTE,
EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE,
EIA608_CHAR_NO_BREAK_SPACE,
EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE,
EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS,
EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS,
EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK,
EIA608_CHAR_INVERTED_EXCLAMATION_MARK,
EIA608_CHAR_ASTERISK,
EIA608_CHAR_APOSTROPHE,
EIA608_CHAR_EM_DASH,
EIA608_CHAR_COPYRIGHT_SIGN,
EIA608_CHAR_SERVICE_MARK,
EIA608_CHAR_BULLET,
EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK,
EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA,
EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS,
EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS,
EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS,
EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS,
EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX,
EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE,
EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX,
EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK,
EIA608_CHAR_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE,
EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE,
EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE,
EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE,
EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE,
EIA608_CHAR_LEFT_CURLY_BRACKET,
EIA608_CHAR_RIGHT_CURLY_BRACKET,
EIA608_CHAR_REVERSE_SOLIDUS,
EIA608_CHAR_CIRCUMFLEX_ACCENT,
EIA608_CHAR_LOW_LINE,
EIA608_CHAR_VERTICAL_LINE,
EIA608_CHAR_TILDE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS,
EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS,
EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS,
EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS,
EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S,
EIA608_CHAR_YEN_SIGN,
EIA608_CHAR_CURRENCY_SIGN,
EIA608_CHAR_BROKEN_BAR,
EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE,
EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE,
EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE,
EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE,
EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT,
EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT,
EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT,
EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT,
};
此差异已折叠。
/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
......@@ -21,270 +21,13 @@
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include "eia608.h"
#include <string.h>
#include <stdio.h>
#include "utf8.h"
#include <ctype.h>
#include <stdint.h>
////////////////////////////////////////////////////////////////////////////////
int eia608_row_map[] = {10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9};
int eia608_reverse_row_map[] = {2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0, 6, 7, 8, 9, 1};
const char* eia608_mode_map[] = {
"clear",
"loading",
"popOn",
"paintOn",
"rollUp",
};
const char* eia608_style_map[] = {
"white",
"green",
"blue",
"cyan",
"red",
"yellow",
"magenta",
"italics",
};
static inline uint16_t eia608_row_pramble (int row, int chan, int x, int underline)
{
row = eia608_reverse_row_map[row&0x0F];
return eia608_parity (0x1040 | (chan?0x0800:0x0000) | ( (row<<7) &0x0700) | ( (row<<5) &0x0020)) | ( (x<<1) &0x001E) | (underline?0x0001:0x0000);
}
uint16_t eia608_row_column_pramble (int row, int col, int chan, int underline) { return eia608_row_pramble (row,chan,0x10| (col/4),underline); }
uint16_t eia608_row_style_pramble (int row, eia608_style_t style, int chan, int underline) { return eia608_row_pramble (row,chan,style,underline); }
int eia608_parse_preamble (uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline)
{
(*row) = eia608_row_map[ ( (0x0700 & cc_data) >> 7) | ( (0x0020 & cc_data) >> 5)];
(*chan) = !! (0x0800 & cc_data);
(*underline) = 0x0001 & cc_data;
if (0x0010 & cc_data) {
(*style) = eia608_style_white;
(*col) = 4* ( (0x000E & cc_data) >> 1);
} else {
(*style) = (0x000E & cc_data) >> 1;
(*col) = 0;
}
return 1;
}
int eia608_parse_midrowchange (uint16_t cc_data, int* chan, eia608_style_t* style, int* underline)
{
(*chan) = !! (0x0800 & cc_data);
if (0x1120 == (0x7770 & cc_data)) {
(*style) = (0x000E & cc_data) >> 1;
(*underline) = 0x0001 & cc_data;
}
return 1;
}
////////////////////////////////////////////////////////////////////////////////
// control command
eia608_control_t eia608_parse_control (uint16_t cc_data, int* cc)
{
if (0x0200&cc_data) {
(*cc) = (cc_data&0x0800?0x01:0x00);
return (eia608_control_t) (0x177F & cc_data);
} else {
(*cc) = (cc_data&0x0800?0x01:0x00) | (cc_data&0x0100?0x02:0x00);
return (eia608_control_t) (0x167F & cc_data);
}
}
uint16_t eia608_control_command (eia608_control_t cmd, int cc)
{
uint16_t c = (cc&0x01) ?0x0800:0x0000;
uint16_t f = (cc&0x02) ?0x0100:0x0000;
if (eia608_tab_offset_0 == (eia608_control_t) (cmd&0xFFC0)) {
return (eia608_control_t) eia608_parity (cmd|c);
} else {
return (eia608_control_t) eia608_parity (cmd|c|f);
}
}
////////////////////////////////////////////////////////////////////////////////
// text
static const char* utf8_from_index (int idx) { return (0<=idx && EIA608_CHAR_COUNT>idx) ? eia608_char_map[idx] : ""; }
static int eia608_to_index (uint16_t cc_data, int* chan, int* c1, int* c2)
{
(*c1) = (*c2) = -1; (*chan) = 0;
cc_data &= 0x7F7F; // strip off parity bits
// Handle Basic NA BEFORE we strip the channel bit
if (eia608_is_basicna (cc_data)) {
// we got first char, yes. But what about second char?
(*c1) = (cc_data>>8) - 0x20;
cc_data &= 0x00FF;
if (0x0020<=cc_data && 0x0080>cc_data) {
(*c2) = cc_data - 0x20;
return 2;
}
return 1;
}
// Check then strip second channel toggle
(*chan) = cc_data & 0x0800;
cc_data = cc_data & 0xF7FF;
if (eia608_is_specialna (cc_data)) {
// Special North American character
(*c1) = cc_data - 0x1130 + 0x60;
return 1;
}
if (0x1220<=cc_data && 0x1240>cc_data) {
// Extended Western European character set, Spanish/Miscellaneous/French
(*c1) = cc_data - 0x1220 + 0x70;
return 1;
}
if (0x1320<=cc_data && 0x1340>cc_data) {
// Extended Western European character set, Portuguese/German/Danish
(*c1) = cc_data - 0x1320 + 0x90;
return 1;
}
return 0;
}
int eia608_to_utf8 (uint16_t c, int* chan, char* str1, char* str2)
{
int c1, c2;
int size = (int) eia608_to_index (c,chan,&c1,&c2);
strncpy (str1, utf8_from_index (c1),5);
strncpy (str2, utf8_from_index (c2),5);
return size;
}
uint16_t eia608_from_basicna (uint16_t bna1, uint16_t bna2)
{
if (! eia608_is_basicna (bna1) || ! eia608_is_basicna (bna2)) {
return 0;
}
return eia608_parity ( ( (0xFF00&bna1) >>0) | ( (0xFF00&bna2) >>8));
}
// prototype for re2c generated function
uint16_t _eia608_from_utf8 (const utf8_char_t* s);
uint16_t eia608_from_utf8_1 (const utf8_char_t* c, int chan)
{
uint16_t cc_data = _eia608_from_utf8 (c);
if (0 == cc_data) {
return cc_data;
}
if (chan && ! eia608_is_basicna (cc_data)) {
cc_data |= 0x0800;
}
return eia608_parity (cc_data);
}
uint16_t eia608_from_utf8_2 (const utf8_char_t* c1, const utf8_char_t* c2)
{
uint16_t cc1 = _eia608_from_utf8 (c1);
uint16_t cc2 = _eia608_from_utf8 (c2);
return eia608_from_basicna (cc1,cc2);
}
////////////////////////////////////////////////////////////////////////////////
void eia608_dump (uint16_t cc_data)
{
eia608_style_t style;
const char* text = 0;
char char1[5], char2[5];
char1[0] = char2[0] = 0;
int row, col, chan, underline;
if (!eia608_parity_varify (cc_data)) {
text = "parity failed";
} else if (0 == eia608_parity_strip (cc_data)) {
text = "pad";
} else if (eia608_is_basicna (cc_data)) {
text = "basicna";
eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]);
} else if (eia608_is_specialna (cc_data)) {
text = "specialna";
eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]);
} else if (eia608_is_westeu (cc_data)) {
text = "westeu";
eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]);
} else if (eia608_is_xds (cc_data)) {
text = "xds";
} else if (eia608_is_midrowchange (cc_data)) {
text = "midrowchange";
} else if (eia608_is_norpak (cc_data)) {
text = "norpak";
} else if (eia608_is_preamble (cc_data)) {
text = "preamble";
eia608_parse_preamble (cc_data, &row, &col, &style, &chan, &underline);
fprintf (stderr,"preamble %d %d %d %d %d\n", row, col, style, chan, underline);
} else if (eia608_is_control (cc_data)) {
switch (eia608_parse_control (cc_data,&chan)) {
default: text = "unknown_control"; break;
case eia608_tab_offset_0: text = "eia608_tab_offset_0"; break;
case eia608_tab_offset_1: text = "eia608_tab_offset_1"; break;
case eia608_tab_offset_2:text = "eia608_tab_offset_2"; break;
case eia608_tab_offset_3: text = "eia608_tab_offset_3"; break;
case eia608_control_resume_caption_loading: text = "eia608_control_resume_caption_loading"; break;
case eia608_control_backspace: text = "eia608_control_backspace"; break;
case eia608_control_alarm_off: text = "eia608_control_alarm_off"; break;
case eia608_control_alarm_on: text = "eia608_control_alarm_on"; break;
case eia608_control_delete_to_end_of_row: text = "eia608_control_delete_to_end_of_row"; break;
case eia608_control_roll_up_2: text = "eia608_control_roll_up_2"; break;
case eia608_control_roll_up_3: text = "eia608_control_roll_up_3"; break;
case eia608_control_roll_up_4: text = "eia608_control_roll_up_4"; break;
case eia608_control_resume_direct_captioning: text = "eia608_control_resume_direct_captioning"; break;
case eia608_control_text_restart: text = "eia608_control_text_restart"; break;
case eia608_control_text_resume_text_display: text = "eia608_control_text_resume_text_display"; break;
case eia608_control_erase_display_memory: text = "eia608_control_erase_display_memory"; break;
case eia608_control_carriage_return: text = "eia608_control_carriage_return"; break;
case eia608_control_erase_non_displayed_memory:text = "eia608_control_erase_non_displayed_memory"; break;
case eia608_control_end_of_caption: text = "eia608_control_end_of_caption"; break;
}
} else {
text = "unhandled";
}
fprintf (stderr,"cc %04X (%04X) '%s' '%s' (%s)\n", cc_data, eia608_parity_strip (cc_data), char1, char2, text);
}
////////////////////////////////////////////////////////////////////////////////
// below this line is re2c
uint16_t _eia608_from_utf8 (const utf8_char_t* s)
{
const unsigned char* YYMARKER; // needed by default rule
const unsigned char* YYMARKER = 0;
const unsigned char* YYCURSOR = (const unsigned char*) s;
if (0==s) { return 0x0000;}
......@@ -301,13 +44,13 @@ uint16_t _eia608_from_utf8 (const utf8_char_t* s)
"\x5C" { /*REVERSE_SOLIDUS*/ return 0x132B; }
"\x5E" { /*CIRCUMFLEX_ACCENT*/ return 0x132C; }
"\x5F" { /*LOW_LINE*/ return 0x132D; }
/*Lets Map this to a LEFT_SINGLE_QUOTATION_MARK, just so we have a cc_data for every printable ASCII value*/
"\x60" { /*GRAVE_ACCENT, No equivalent return 0x0000; return 1;*/ /*LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; }
/*Map GRAVE_ACCENT to a LEFT_SINGLE_QUOTATION_MARK so we have a cc_data for every printable ASCII value*/
"\x60" { /*GRAVE_ACCENT -> LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; }
"\x7B" { /*LEFT_CURLY_BRACKET*/ return 0x1329; }
"\x7C" { /*VERTICAL_LINE*/ return 0x132E; }
"\x7D" { /*RIGHT_CURLY_BRACKET*/ return 0x132A; }
"\x7E" { /*TILDE*/ return 0x132F; }
/*There is a controll equivilant. Havnt decided if we want to habcle that here, or not*/
/*There is a control equivalent. Haven't decided if we want to handle that here or not*/
"\x7F" { /*DEL/BACKSPACE. Need to set bits 9 and 12! return 0x1421;*/ return 0x0000; }
/* Rules are processed top to bottom. So All single byte chars MUST be above this line!*/
......@@ -416,6 +159,6 @@ uint16_t _eia608_from_utf8 (const utf8_char_t* s)
"\xE2\x94\x98" { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT*/ return 0x133F; }
/*Default rule*/
[^] { /*DEFAULT_RULE*/ return 0x0000; }
* { /*DEFAULT_RULE*/ return 0x0000; }
*/
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -22,7 +22,7 @@
#if BUILD_CAPTIONS
#include <caption/caption.h>
#include <caption/avc.h>
#include <caption/mpeg.h>
#endif
static inline bool active(const struct obs_output *output)
......@@ -989,7 +989,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
if (out->priority > 1)
return false;
sei_init(&sei);
sei_init(&sei, 0.0);
da_init(out_data);
da_push_back_array(out_data, &ref, sizeof(ref));
......@@ -2094,34 +2094,14 @@ void obs_output_output_caption_text1(obs_output_t *output, const char *text)
// split text into 32 character strings
int size = (int)strlen(text);
int r;
size_t char_count;
size_t line_length = 0;
size_t trimmed_length = 0;
blog(LOG_DEBUG, "Caption text: %s", text);
pthread_mutex_lock(&output->caption_mutex);
for (r = 0 ; 0 < size && CAPTION_LINE_CHARS > r; ++r) {
line_length = utf8_line_length(text);
trimmed_length = utf8_trimmed_length(text, line_length);
char_count = utf8_char_count(text, trimmed_length);
if (SCREEN_COLS < char_count) {
char_count = utf8_wrap_length(text, CAPTION_LINE_CHARS);
line_length = utf8_string_length(text, char_count + 1);
}
output->caption_tail = caption_text_new(
text,
line_length,
output->caption_tail,
&output->caption_head);
text += line_length;
size -= (int)line_length;
}
output->caption_tail = caption_text_new(
text, size,
output->caption_tail,
&output->caption_head);
pthread_mutex_unlock(&output->caption_mutex);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册