From e5f8a7428a077a9de9f18048486c86d95b810b72 Mon Sep 17 00:00:00 2001 From: Matthew Szatmary Date: Tue, 19 Jun 2018 17:47:45 -0700 Subject: [PATCH] Feature/262 (#31) * Support for h262 parsing * Support for parsing out of order frames --- .gitignore | 2 + CMakeLists.txt | 4 +- README.md | 8 +- caption/caption.h | 2 +- caption/cea708.h | 11 +- caption/{avc.h => mpeg.h} | 107 ++++++------ caption/srt.h | 2 +- caption/utf8.h | 2 +- caption/vtt.h | 8 +- examples/captioner.c | 117 ------------- examples/flv+srt.c | 6 +- examples/flv.c | 6 +- examples/flv.h | 3 +- examples/flv2srt.c | 45 ++--- examples/party.c | 3 +- examples/rollup.c | 6 +- examples/scc2srt.c | 2 +- examples/srtdump.c | 3 +- examples/ts.c | 8 +- examples/ts.h | 7 +- examples/ts2srt.c | 76 ++++----- examples/vttdump.c | 1 - examples/vttsegmenter.c | 57 +++---- format.sh | 1 - src/caption.c | 6 +- src/cea708.c | 95 ++++++----- src/eia608_charmap.c | 198 +++++++++++++++++++--- src/eia608_from_utf8.c.cached | 2 +- src/{avc.c => mpeg.c} | 302 ++++++++++++++++++++-------------- src/srt.c | 4 +- src/utf8.c | 26 +-- src/vtt.c | 96 ++++++----- 32 files changed, 652 insertions(+), 564 deletions(-) rename caption/{avc.h => mpeg.h} (75%) delete mode 100644 examples/captioner.c rename src/{avc.c => mpeg.c} (70%) diff --git a/.gitignore b/.gitignore index 12aaa0c..3c0e126 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,8 @@ party rtmpspit rollup sccdump +vttdump +vttsegmenter # Autogenerated files src/eia608_from_utf8.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 953d0da..48d79e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,12 +47,12 @@ else() endif() set(CAPTION_SOURCES - src/avc.c src/caption.c src/cea708.c src/eia608.c src/eia608_charmap.c src/eia608_from_utf8.c + src/mpeg.c src/scc.c src/srt.c src/utf8.c @@ -61,11 +61,11 @@ set(CAPTION_SOURCES ) set(CAPTION_HEADERS - caption/avc.h caption/caption.h caption/cea708.h caption/eia608.h caption/eia608_charmap.h + caption/mpeg.h caption/scc.h caption/srt.h caption/utf8.h diff --git a/README.md b/README.md index ef136c4..352ea27 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # version -v0.7 +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 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. +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. @@ -37,8 +37,8 @@ H.264 utility functions are limited to wrapping the 708 payload into a SEI NALU. * WEG = Extended Western European character set : German/Danish ## Limitations -Current B-frame support is minimal. libcaption ensures no re-ordering of captions is required -on playback. But caption parsing with out of order frames behaviour is currently undefined +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 diff --git a/caption/caption.h b/caption/caption.h index c2cb2d2..ee4d8a7 100644 --- a/caption/caption.h +++ b/caption/caption.h @@ -41,8 +41,8 @@ typedef signed int ssize_t; #endif typedef enum { - LIBCAPTION_OK = 1, LIBCAPTION_ERROR = 0, + LIBCAPTION_OK = 1, LIBCAPTION_READY = 2 } libcaption_stauts_t; diff --git a/caption/cea708.h b/caption/cea708.h index 6fe5559..c4b35e5 100644 --- a/caption/cea708.h +++ b/caption/cea708.h @@ -84,6 +84,7 @@ typedef struct { 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'); @@ -92,15 +93,19 @@ const static uint32_t DTG1 = (('D' << 24) | ('T' << 16) | ('G' << 8) | '1'); /*! \brief \param */ -int cea708_init(cea708_t* cea708); // will confgure using HLS compatiable defaults +int cea708_init(cea708_t* cea708, double timestamp); // will confgure using HLS compatiable defaults /*! \brief \param */ -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); /*! \brief \param */ -libcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708, double pts); +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); /*! \brief \param */ diff --git a/caption/avc.h b/caption/mpeg.h similarity index 75% rename from caption/avc.h rename to caption/mpeg.h index f9d91e0..699a29c 100644 --- a/caption/avc.h +++ b/caption/mpeg.h @@ -21,8 +21,8 @@ /* 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 +#ifndef LIBCAPTION_MPEG_H +#define LIBCAPTION_MPEG_H #ifdef __cplusplus extern "C" { #endif @@ -31,21 +31,46 @@ extern "C" { #include "cea708.h" #include "scc.h" #include +#include //////////////////////////////////////////////////////////////////////////////// -#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, @@ -71,10 +96,14 @@ typedef enum { sei_type_stereo_video_info = 21, } sei_msgtype_t; //////////////////////////////////////////////////////////////////////////////// -// time in seconds +typedef struct _sei_message_t { + size_t size; + sei_msgtype_t type; + struct _sei_message_t* next; +} sei_message_t; + typedef struct { - double dts; - double cts; + double timestamp; sei_message_t* head; sei_message_t* tail; } sei_t; @@ -82,7 +111,7 @@ typedef struct { /*! \brief \param */ -void sei_init(sei_t* sei); +void sei_init(sei_t* sei, double timestamp); /*! \brief \param */ @@ -98,22 +127,7 @@ void sei_message_append(sei_t* sei, sei_message_t* msg); /*! \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; } -/*! \brief - \param -*/ -int sei_parse_nalu(sei_t* sei, const uint8_t* data, size_t size, double dts, double cts); -/*! \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); } -/*! \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 */ @@ -161,18 +175,6 @@ 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; - } -} -//////////////////////////////////////////////////////////////////////////////// -/*! \brief - \param -*/ size_t sei_render_size(sei_t* sei); /*! \brief \param @@ -185,7 +187,7 @@ void sei_dump(sei_t* sei); /*! \brief \param */ -void sei_dump_messages(sei_message_t* head); +void sei_dump_messages(sei_message_t* head, double timestamp); //////////////////////////////////////////////////////////////////////////////// /*! \brief \param @@ -203,21 +205,6 @@ libcaption_stauts_t sei_from_caption_clear(sei_t* sei); \param */ libcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame); -/*! \brief - \param -*/ -static inline libcaption_stauts_t avcnalu_to_caption_frame(caption_frame_t* frame, const uint8_t* data, size_t size, double dts, double cts) -{ - sei_t sei; - libcaption_stauts_t err = LIBCAPTION_ERROR; - - sei_init(&sei); - sei_parse_nalu(&sei, data, size, dts, cts); - err = sei_to_caption_frame(&sei, frame); - sei_free(&sei); - - return err; -} //////////////////////////////////////////////////////////////////////////////// #ifdef __cplusplus } diff --git a/caption/srt.h b/caption/srt.h index 5b5c138..6c4c437 100644 --- a/caption/srt.h +++ b/caption/srt.h @@ -66,7 +66,7 @@ static inline utf8_char_t* srt_cue_data(srt_cue_t* cue) { return vtt_block_data( /*! \brief \param */ -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); }; +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); }; /*! \brief \param diff --git a/caption/utf8.h b/caption/utf8.h index 662b14c..b93a246 100644 --- a/caption/utf8.h +++ b/caption/utf8.h @@ -119,7 +119,7 @@ utf8_char_t* utf8_load_text_file(const char* path, size_t* size); Compares 2 strings up to max len */ #ifndef strnstr -char *strnstr(const char *string1, const char *string2, size_t len); +char* strnstr(const char* string1, const char* string2, size_t len); #endif #ifdef __cplusplus diff --git a/caption/vtt.h b/caption/vtt.h index f690490..4130054 100644 --- a/caption/vtt.h +++ b/caption/vtt.h @@ -44,11 +44,11 @@ typedef struct _vtt_block_t { // CUE-Only double timestamp; double duration; // -1.0 for no duration - char *cue_settings; - char *cue_id; + char* cue_settings; + char* cue_id; // Standard block data size_t text_size; - char *block_text; + char* block_text; } vtt_block_t; // VTT files are a collection of REGION, STYLE and CUE blocks. @@ -133,7 +133,7 @@ int vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame); /*! \brief \param */ -vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t *vtt); +vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt); /*! \brief \param */ diff --git a/examples/captioner.c b/examples/captioner.c deleted file mode 100644 index dc76854..0000000 --- a/examples/captioner.c +++ /dev/null @@ -1,117 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* 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 */ -/* 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 "caption.h" -#include "flv.h" -#include -#include -#include -#include -#include -#include -#include - -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; -} diff --git a/examples/flv+srt.c b/examples/flv+srt.c index ecb20e9..0a8157f 100644 --- a/examples/flv+srt.c +++ b/examples/flv+srt.c @@ -21,8 +21,8 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "flv.h" +#include "mpeg.h" #include "srt.h" #include @@ -101,8 +101,8 @@ srt_t* srt_from_fd(int fd) int main(int argc, char** argv) { flvtag_t tag; - srt_t *old_srt = NULL; - srt_cue_t *next_cue = NULL; + srt_t* old_srt = NULL; + srt_cue_t* next_cue = NULL; double timestamp, offset = 0, clear_timestamp = 0; int has_audio, has_video; FILE* flv = flv_open_read(argv[1]); diff --git a/examples/flv.c b/examples/flv.c index 24eb80e..8b01105 100644 --- a/examples/flv.c +++ b/examples/flv.c @@ -345,7 +345,7 @@ int flvtag_addsei(flvtag_t* tag, sei_t* sei) } sei_t new_sei; - sei_init(&new_sei); + sei_init(&new_sei, flvtag_pts(tag)); sei_cat(&new_sei, sei, 1); flvtag_t new_tag; @@ -386,7 +386,7 @@ int flvtag_addsei(flvtag_t* tag, sei_t* sei) int flvtag_addcaption_text(flvtag_t* tag, const utf8_char_t* text) { sei_t sei; - sei_init(&sei); + sei_init(&sei, flvtag_pts(tag)); if (text) { caption_frame_t frame; @@ -405,7 +405,7 @@ int flvtag_addcaption_text(flvtag_t* tag, const utf8_char_t* text) int flvtag_addcaption_scc(flvtag_t* tag, const scc_t* scc) { sei_t sei; - sei_init(&sei); + sei_init(&sei, flvtag_pts(tag)); sei_from_scc(&sei, scc); int ret = flvtag_addsei(tag, &sei); sei_free(&sei); diff --git a/examples/flv.h b/examples/flv.h index fa2f0f6..3ff3cb7 100644 --- a/examples/flv.h +++ b/examples/flv.h @@ -24,6 +24,7 @@ #ifndef LIBCAPTION_FLV_H #define LIBCAPTION_FLV_H +#include "mpeg.h" #include #include #include @@ -32,8 +33,6 @@ #define FLV_TAG_HEADER_SIZE 11 #define FLV_TAG_FOOTER_SIZE 4 //////////////////////////////////////////////////////////////////////////////// -#include "avc.h" -//////////////////////////////////////////////////////////////////////////////// typedef struct { uint8_t* data; size_t aloc; diff --git a/examples/flv2srt.c b/examples/flv2srt.c index 3ed1ca9..1eddb75 100644 --- a/examples/flv2srt.c +++ b/examples/flv2srt.c @@ -21,23 +21,24 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "flv.h" +#include "mpeg.h" #include "srt.h" +#include #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; + srt_t* srt = 0; int has_audio, has_video; caption_frame_t frame; + mpeg_bitstream_t mpegbs; + const char* path = argv[1]; flvtag_init(&tag); caption_frame_init(&frame); + mpeg_bitstream_init(&mpegbs); FILE* flv = flv_open_read(path); srt = srt_new(); @@ -54,24 +55,26 @@ int main(int argc, char** argv) 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_seconds(&tag), flvtag_cts_seconds(&tag)); - // sei_dump(&sei); + size_t nalu_size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + mpeg_bitstream_parse(&mpegbs, &frame, (const uint8_t*)"\0\0\1", 3, STREAM_TYPE_H264, flvtag_dts_seconds(&tag), flvtag_cts_seconds(&tag)); + mpeg_bitstream_parse(&mpegbs, &frame, &data[LENGTH_SIZE], nalu_size, STREAM_TYPE_H264, flvtag_dts_seconds(&tag), flvtag_cts_seconds(&tag)); + data += nalu_size + LENGTH_SIZE, size -= nalu_size + LENGTH_SIZE; + switch (mpeg_bitstream_status(&mpegbs)) { + default: + case LIBCAPTION_ERROR: + fprintf(stderr, "LIBCAPTION_ERROR == mpeg_bitstream_parse()\n"); + mpeg_bitstream_init(&mpegbs); + return EXIT_FAILURE; + break; - if (LIBCAPTION_READY == sei_to_caption_frame(&sei, &frame)) { - srt_cue_from_caption_frame(&frame, srt); - } + case LIBCAPTION_OK: + break; - // caption_frame_dump(&frame); - sei_free(&sei); - } + case LIBCAPTION_READY: { + caption_frame_dump(&frame); + srt_cue_from_caption_frame(&frame, srt); + } break; + } //switch } } } diff --git a/examples/party.c b/examples/party.c index deb4d2c..805811a 100644 --- a/examples/party.c +++ b/examples/party.c @@ -21,8 +21,8 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "flv.h" +#include "mpeg.h" #include #include #include @@ -50,7 +50,6 @@ void write_amfcaptions_708(FILE* out, uint32_t timestamp, const char* text) 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); diff --git a/examples/rollup.c b/examples/rollup.c index e9088cd..b94ecb3 100644 --- a/examples/rollup.c +++ b/examples/rollup.c @@ -21,8 +21,8 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "flv.h" +#include "mpeg.h" #include "srt.h" #include "wonderland.h" #include @@ -49,7 +49,7 @@ void append_caption(const utf8_char_t* data, srt_t* srt) // fprintf (stderr,"%.*s\n", line_length, data); double timestamp = srt->cue_tail ? srt->cue_tail->timestamp + SECONDS_PER_LINE : 0; - srt_cue_t *cue = srt_cue_new(srt, data, line_length); + srt_cue_t* cue = srt_cue_new(srt, data, line_length); cue->timestamp = timestamp; cue->duration = SECONDS_PER_LINE; @@ -62,7 +62,7 @@ int main(int argc, char** argv) { int i = 0; flvtag_t tag; - srt_t *srt = 0; + srt_t* srt = 0; int has_audio, has_video; FILE* flv = flv_open_read(argv[1]); FILE* out = flv_open_write(argv[2]); diff --git a/examples/scc2srt.c b/examples/scc2srt.c index 61de1fd..f8bfbc2 100644 --- a/examples/scc2srt.c +++ b/examples/scc2srt.c @@ -33,7 +33,7 @@ int main(int argc, char** argv) scc_t* scc = NULL; size_t scc_size = 0; caption_frame_t frame; - srt_t *srt = 0; + srt_t* srt = 0; utf8_char_t* scc_data_ptr = utf8_load_text_file(argv[1], &scc_size); utf8_char_t* scc_data = scc_data_ptr; diff --git a/examples/srtdump.c b/examples/srtdump.c index 85dceb1..93fd234 100644 --- a/examples/srtdump.c +++ b/examples/srtdump.c @@ -21,7 +21,6 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "srt.h" #include #include @@ -63,7 +62,7 @@ int main(int argc, char** argv) size_t size; utf8_char_t* data = utf8_load_text_file(argv[1], &size); srt = srt_parse(data, size); - + for (cue = srt->cue_head; cue; cue = cue->next) { caption_frame_init(&frame); srt_cue_to_caption_frame(cue, &frame); diff --git a/examples/ts.c b/examples/ts.c index 138ed47..e246546 100644 --- a/examples/ts.c +++ b/examples/ts.c @@ -22,6 +22,7 @@ /* THE SOFTWARE. */ /**********************************************************************************************/ #include "ts.h" +#include #include void ts_init(ts_t* ts) @@ -85,15 +86,16 @@ int ts_parse_packet(ts_t* ts, const uint8_t* data) 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; + if (STREAM_TYPE_H262 == stream_type || STREAM_TYPE_H264 == stream_type || STREAM_TYPE_H265 == stream_type) { + ts->ccpid = elementary_pid; + ts->stream_type = stream_type; } i += 5 + esinfo_length; descriptor_loop_length -= 5 + esinfo_length; } } - } else if (payload_present && pid == ts->avcpid) { + } else if (payload_present && pid == ts->ccpid) { if (pusi) { // int data_alignment = !! (data[i + 6] & 0x04); int has_pts = !!(data[i + 7] & 0x80); diff --git a/examples/ts.h b/examples/ts.h index e6df801..80ca873 100644 --- a/examples/ts.h +++ b/examples/ts.h @@ -24,9 +24,12 @@ #ifndef LIBCAPTION_TS_H #define LIBCAPTION_TS_H #include "caption.h" +#include "mpeg.h" + typedef struct { int16_t pmtpid; - int16_t avcpid; + int16_t ccpid; + int16_t stream_type; int64_t pts; int64_t dts; size_t size; @@ -44,6 +47,6 @@ 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; } +static inline double ts_cts_seconds(ts_t* ts) { return (ts->pts - ts->dts) / 90000.0; } #endif diff --git a/examples/ts2srt.c b/examples/ts2srt.c index e9fac16..ca278c5 100644 --- a/examples/ts2srt.c +++ b/examples/ts2srt.c @@ -21,81 +21,65 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "srt.h" #include "ts.h" #include +#include int main(int argc, char** argv) { const char* path = argv[1]; ts_t ts; - sei_t sei; - avcnalu_t nalu; - srt_t *srt; + srt_t* srt = 0; + // srt_cue_t, *cue; + mpeg_bitstream_t mpegbs; caption_frame_t frame; uint8_t pkt[TS_PACKET_SIZE]; ts_init(&ts); - avcnalu_init(&nalu); caption_frame_init(&frame); + mpeg_bitstream_init(&mpegbs); srt = srt_new(); - FILE* file = fopen(path, "rb+"); - + setvbuf(file, 0, _IOFBF, 8192 * TS_PACKET_SIZE); // This fread 188 bytes at a time is VERY slow. Need to rewrite that 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"); + if (LIBCAPTION_READY == ts_parse_packet(&ts, &pkt[0])) { + double dts = ts_dts_seconds(&ts); + double cts = ts_cts_seconds(&ts); 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: + size_t bytes_read = mpeg_bitstream_parse(&mpegbs, &frame, ts.data, ts.size, ts.stream_type, dts, cts); + ts.data += bytes_read, ts.size -= bytes_read; + switch (mpeg_bitstream_status(&mpegbs)) { + default: + case LIBCAPTION_ERROR: + fprintf(stderr, "LIBCAPTION_ERROR == mpeg_bitstream_parse()\n"); + mpeg_bitstream_init(&mpegbs); + return EXIT_FAILURE; break; - case LIBCAPTION_ERROR: - // fprintf (stderr,"LIBCAPTION_ERROR == avcnalu_parse_annexb()\n"); - avcnalu_init(&nalu); + case LIBCAPTION_OK: 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_cue_from_caption_frame(&frame, srt); - } - - sei_free(&sei); - } - - avcnalu_init(&nalu); + // caption_frame_dump(&frame); + srt_cue_from_caption_frame(&frame, srt); } break; - } - } - } break; - - case LIBCAPTION_ERROR: - // fprintf (stderr,"read ts packet ERROR\n"); - break; + } //switch + } // while + } // if + } // while + + // Flush anything left + while (mpeg_bitstream_flush(&mpegbs, &frame)) { + if (mpeg_bitstream_status(&mpegbs)) { + srt_cue_from_caption_frame(&frame, srt); } } srt_dump(srt); srt_free(srt); - return 1; + return EXIT_SUCCESS; } diff --git a/examples/vttdump.c b/examples/vttdump.c index 7b4c42e..cf1ba7a 100644 --- a/examples/vttdump.c +++ b/examples/vttdump.c @@ -21,7 +21,6 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "vtt.h" #include #include diff --git a/examples/vttsegmenter.c b/examples/vttsegmenter.c index e6660e1..af3df7c 100644 --- a/examples/vttsegmenter.c +++ b/examples/vttsegmenter.c @@ -21,7 +21,6 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" #include "vtt.h" #include #include @@ -38,37 +37,34 @@ void vtt_write_header(FILE* vttFile) void vtt_write_block(vtt_block_t* block, FILE* vttFile) { - switch (block->type) - { - case VTT_REGION: - fprintf(vttFile, "REGION\r\n%s\r\n\r\n", vtt_block_data(block)); - break; - case VTT_STYLE: - fprintf(vttFile, "STYLE\r\n%s\r\n\r\n", vtt_block_data(block)); - break; - case VTT_NOTE: - fprintf(vttFile, "NOTE\r\n%s\r\n\r\n", vtt_block_data(block)); - break; - case VTT_CUE: - { - if (block->cue_id != NULL) { - fprintf(vttFile, "%s\r\n", block->cue_id); - } + switch (block->type) { + case VTT_REGION: + fprintf(vttFile, "REGION\r\n%s\r\n\r\n", vtt_block_data(block)); + break; + case VTT_STYLE: + fprintf(vttFile, "STYLE\r\n%s\r\n\r\n", vtt_block_data(block)); + break; + case VTT_NOTE: + fprintf(vttFile, "NOTE\r\n%s\r\n\r\n", vtt_block_data(block)); + break; + case VTT_CUE: { + if (block->cue_id != NULL) { + fprintf(vttFile, "%s\r\n", block->cue_id); + } - int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2; - vtt_crack_time(block->timestamp, &hh1, &mm1, &ss1, &ms1); - vtt_crack_time(block->timestamp + block->duration, &hh2, &mm2, &ss2, &ms2); - - fprintf(vttFile, "%d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d", - hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2); - - if (block->cue_settings != NULL) { - printf(" %s", block->cue_settings); - } + int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2; + vtt_crack_time(block->timestamp, &hh1, &mm1, &ss1, &ms1); + vtt_crack_time(block->timestamp + block->duration, &hh2, &mm2, &ss2, &ms2); - fprintf(vttFile, "\r\n%s\r\n\r\n", vtt_block_data(block)); + fprintf(vttFile, "%d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d", + hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2); + + if (block->cue_settings != NULL) { + printf(" %s", block->cue_settings); } - break; + + fprintf(vttFile, "\r\n%s\r\n\r\n", vtt_block_data(block)); + } break; } } @@ -133,11 +129,10 @@ int main(int argc, char** argv) double segment_start = i * segment_size; double segment_end = (i + 1) * segment_size; - vtt_block_t* cue = vtt->cue_head; while (cue != NULL) { if ((cue->timestamp < segment_end) && ((cue->timestamp + cue->duration) > segment_start)) { - vtt_write_block(cue, outputFile); + vtt_write_block(cue, outputFile); } cue = cue->next; } diff --git a/format.sh b/format.sh index c6c3e02..6c565af 100755 --- a/format.sh +++ b/format.sh @@ -1,4 +1,3 @@ #!/bin/bash cd "$(dirname "$0")" -# find . \( -name '*.cpp' -o -name '*.c' -o -name '*.h' -o -name '*.hpp' -o -name '*.re2c' \) -exec astyle --style=stroustrup --attach-extern-c --break-blocks --pad-header --pad-paren-out --unpad-paren --add-brackets --keep-one-line-blocks --keep-one-line-statements --convert-tabs --align-pointer=type --align-reference=type --suffix=none --lineend=linux --max-code-length=180 {} \; find . \( -name '*.c' -o -name '*.h' \) -exec clang-format -i -style=WebKit {} \; diff --git a/src/caption.c b/src/caption.c index 6559a9c..aa53af9 100644 --- a/src/caption.c +++ b/src/caption.c @@ -214,7 +214,7 @@ libcaption_stauts_t caption_frame_decode_control(caption_frame_t* frame, uint16_ case eia608_control_erase_display_memory: caption_frame_buffer_clear(&frame->front); - return LIBCAPTION_OK; + return LIBCAPTION_READY; // ROLL-UP case eia608_control_roll_up_2: @@ -414,8 +414,8 @@ size_t caption_frame_dump_buffer(caption_frame_t* frame, utf8_char_t* buf) { int r, c; size_t bytes, total = 0; - bytes = sprintf(buf, " row: %d\tcol: %d\troll-up: %d\n", - frame->state.row, frame->state.col, caption_frame_rollup(frame)); + bytes = sprintf(buf, " timestamp: %f\n row: %02d col: %02d roll-up: %d\n", + frame->timestamp, frame->state.row, frame->state.col, caption_frame_rollup(frame)); total += bytes, buf += bytes; bytes = sprintf(buf, " 00000000001111111111222222222233\t 00000000001111111111222222222233\n" " 01234567890123456789012345678901\t 01234567890123456789012345678901\n" diff --git a/src/cea708.c b/src/cea708.c index 83d1bfc..2405c9b 100644 --- a/src/cea708.c +++ b/src/cea708.c @@ -22,6 +22,7 @@ /* THE SOFTWARE. */ /**********************************************************************************************/ #include "cea708.h" +#include #include #include @@ -37,7 +38,7 @@ uint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_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)); cea708->country = country_united_states; @@ -50,24 +51,41 @@ int cea708_init(cea708_t* cea708) cea708->user_data.additional_data_flag = 0; cea708->user_data.em_data = 0xFF; cea708->user_data.cc_count = 0; + cea708->timestamp = timestamp; return 1; } -// 00 00 00 06 C1 FF FC 34 B9 FF : onCaptionInfo. -int cea708_parse(uint8_t* data, size_t size, cea708_t* cea708) +void cea708_parse_user_data_type_strcture(const uint8_t* data, size_t size, user_data_t* user_data) { - int i; + 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. +libcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708) +{ 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; cea708->user_data_type_code = 0; - data += 3; - size -= 3; + data += 3, size -= 3; if (t35_provider_atsc == cea708->provider) { if (4 > size) { @@ -75,27 +93,25 @@ int cea708_parse(uint8_t* data, size_t size, cea708_t* cea708) } cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]); - data += 4; - size -= 4; + data += 4, size -= 4; } // where country and provider are zero // 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; + data += 1, size -= 1; } else if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) { if (1 > size) { goto error; } cea708->user_data_type_code = data[0]; - data += 1; - size -= 1; + data += 1, size -= 1; } if (t35_provider_direct_tv == cea708->provider) { @@ -104,37 +120,11 @@ int cea708_parse(uint8_t* data, size_t size, cea708_t* cea708) } cea708->directv_user_data_length = data[0]; - data += 1; - size -= 1; + data += 1, size -= 1; } - memset(&cea708->user_data, 0, sizeof(user_data_t)); - - if (3 == cea708->user_data_type_code) { - if (2 > size) { - goto error; - } - - 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; - - for (i = 0; i < (int)cea708->user_data.cc_count; ++i) { - if (3 > size) { - goto error; - } - - 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; - } + 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) { @@ -148,6 +138,21 @@ int cea708_parse(uint8_t* data, size_t size, cea708_t* cea708) 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; + } + + 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 LIBCAPTION_OK; +} + 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) { @@ -245,8 +250,8 @@ void cea708_dump(cea708_t* cea708) fprintf(stderr, "user_data.em_data %d\n", cea708->user_data.em_data); for (i = 0; i < (int)cea708->user_data.cc_count; ++i) { - cea708_cc_type_t type; 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) { @@ -257,19 +262,19 @@ void cea708_dump(cea708_t* cea708) } } -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); libcaption_stauts_t status = LIBCAPTION_OK; if (GA94 == cea708->user_identifier) { for (i = 0; i < count; ++i) { - cea708_cc_type_t type; 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)); + status = libcaption_status_update(status, caption_frame_decode(frame, cc_data, cea708->timestamp)); } } } diff --git a/src/eia608_charmap.c b/src/eia608_charmap.c index 936f06d..4a8258e 100644 --- a/src/eia608_charmap.c +++ b/src/eia608_charmap.c @@ -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, }; diff --git a/src/eia608_from_utf8.c.cached b/src/eia608_from_utf8.c.cached index e511bd1..893ca4a 100644 --- a/src/eia608_from_utf8.c.cached +++ b/src/eia608_from_utf8.c.cached @@ -1,4 +1,4 @@ -/* Generated by re2c 1.0.2 on Tue Oct 10 11:21:38 2017 */ +/* Generated by re2c 1.0.3 on Tue Jun 19 17:18:11 2018 */ /**********************************************************************************************/ /* The MIT License */ /* */ diff --git a/src/avc.c b/src/mpeg.c similarity index 70% rename from src/avc.c rename to src/mpeg.c index 8913d2f..b34dfb0 100644 --- a/src/avc.c +++ b/src/mpeg.c @@ -22,7 +22,7 @@ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "avc.h" +#include "mpeg.h" #include #include #include @@ -136,12 +136,6 @@ size_t _copy_from_rbsp(uint8_t* data, uint8_t* payloadData, size_t payloadSize) return total; } //////////////////////////////////////////////////////////////////////////////// -struct _sei_message_t { - size_t size; - sei_msgtype_t type; - struct _sei_message_t* next; -}; - sei_message_t* sei_message_next(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->next; } sei_msgtype_t sei_message_type(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->type; } size_t sei_message_size(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->size; } @@ -169,12 +163,11 @@ sei_message_t* sei_message_new(sei_msgtype_t type, uint8_t* data, size_t size) return (sei_message_t*)msg; } //////////////////////////////////////////////////////////////////////////////// -void sei_init(sei_t* sei) +void sei_init(sei_t* sei, double timestamp) { - sei->dts = -1; - sei->cts = -1; sei->head = 0; sei->tail = 0; + sei->timestamp = timestamp; } void sei_message_append(sei_t* sei, sei_message_t* msg) @@ -212,20 +205,20 @@ void sei_free(sei_t* sei) sei->head = tail; } - sei_init(sei); + sei_init(sei, 0); } void sei_dump(sei_t* sei) { fprintf(stderr, "SEI %p\n", sei); - sei_dump_messages(sei->head); + sei_dump_messages(sei->head, sei->timestamp); } -void sei_dump_messages(sei_message_t* head) +void sei_dump_messages(sei_message_t* head, double timestamp) { cea708_t cea708; sei_message_t* msg; - cea708_init(&cea708); + cea708_init(&cea708, timestamp); for (msg = head; msg; msg = sei_message_next(msg)) { uint8_t* data = sei_message_data(msg); @@ -241,7 +234,7 @@ void sei_dump_messages(sei_message_t* head) fprintf(stderr, "\n"); if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) { - if (LIBCAPTION_OK != cea708_parse(sei_message_data(msg), sei_message_size(msg), &cea708)) { + if (LIBCAPTION_OK != cea708_parse_h262(sei_message_data(msg), sei_message_size(msg), &cea708)) { fprintf(stderr, "cea708_parse error\n"); } else { cea708_dump(&cea708); @@ -329,58 +322,39 @@ uint8_t* sei_render_alloc(sei_t* sei, size_t* size) } //////////////////////////////////////////////////////////////////////////////// -int sei_parse_nalu(sei_t* sei, const uint8_t* data, size_t size, double dts, double cts) +libcaption_stauts_t sei_parse(sei_t* sei, const uint8_t* data, size_t size, double timestamp) { - assert(0 <= cts); // cant present before decode - sei_init(sei); - sei->dts = dts; - sei->cts = cts; + sei_init(sei, timestamp); int ret = 0; - if (0 == data || 0 == size) { - return 0; - } - - uint8_t nal_unit_type = (*data) & 0x1F; - ++data; - --size; - - if (6 != nal_unit_type) { - return 0; - } - // SEI may contain more than one payload while (1 < size) { - int payloadType = 0; - int payloadSize = 0; + size_t payloadType = 0; + size_t payloadSize = 0; while (0 < size && 255 == (*data)) { payloadType += 255; - ++data; - --size; + ++data, --size; } if (0 == size) { - goto error; + return LIBCAPTION_ERROR; } payloadType += (*data); - ++data; - --size; + ++data, --size; while (0 < size && 255 == (*data)) { payloadSize += 255; - ++data; - --size; + ++data, --size; } if (0 == size) { - goto error; + return LIBCAPTION_ERROR; } payloadSize += (*data); - ++data; - --size; + ++data, --size; if (payloadSize) { sei_message_t* msg = sei_message_new((sei_msgtype_t)payloadType, 0, payloadSize); @@ -388,8 +362,8 @@ int sei_parse_nalu(sei_t* sei, const uint8_t* data, size_t size, double dts, dou size_t bytes = _copy_to_rbsp(payloadData, payloadSize, data, size); sei_message_append(sei, msg); - if ((int)bytes < payloadSize) { - goto error; + if (bytes < payloadSize) { + return LIBCAPTION_ERROR; } data += bytes; @@ -399,10 +373,7 @@ int sei_parse_nalu(sei_t* sei, const uint8_t* data, size_t size, double dts, dou } // There should be one trailing byte, 0x80. But really, we can just ignore that fact. - return ret; -error: - sei_init(sei); - return 0; + return LIBCAPTION_OK; } //////////////////////////////////////////////////////////////////////////////// libcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame) @@ -411,22 +382,21 @@ libcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame) sei_message_t* msg; libcaption_stauts_t status = LIBCAPTION_OK; - cea708_init(&cea708); + cea708_init(&cea708, frame->timestamp); 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); - status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708, sei_pts(sei))); + cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), &cea708); + status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708)); } } if (LIBCAPTION_READY == status) { - frame->timestamp = sei_pts(sei); + frame->timestamp = sei->timestamp; } return status; } - //////////////////////////////////////////////////////////////////////////////// #define DEFAULT_CHANNEL 0 @@ -435,8 +405,7 @@ void sei_append_708(sei_t* sei, cea708_t* cea708) sei_message_t* msg = sei_message_new(sei_type_user_data_registered_itu_t_t35, 0, CEA608_MAX_SIZE); msg->size = cea708_render(cea708, sei_message_data(msg), sei_message_size(msg)); sei_message_append(sei, msg); - // cea708_dump (cea708); - cea708_init(cea708); // will confgure using HLS compatiable defaults + cea708_init(cea708, sei->timestamp); // will confgure using HLS compatiable defaults } // This should be moved to 708.c @@ -473,7 +442,8 @@ libcaption_stauts_t sei_from_caption_frame(sei_t* sei, caption_frame_t* frame) uint16_t prev_cc_data; eia608_style_t styl, prev_styl; - cea708_init(&cea708); // set up a new popon frame + sei_init(sei, frame->timestamp); + cea708_init(&cea708, frame->timestamp); // set up a new popon frame cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL)); cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL)); @@ -552,7 +522,7 @@ libcaption_stauts_t sei_from_caption_frame(sei_t* sei, caption_frame_t* frame) } sei_encode_eia608(sei, &cea708, 0); // flush - sei->dts = frame->timestamp; // assumes in order frames + sei->timestamp = frame->timestamp; // assumes in order frames // sei_dump (sei); return LIBCAPTION_OK; } @@ -561,7 +531,7 @@ libcaption_stauts_t sei_from_scc(sei_t* sei, const scc_t* scc) { unsigned int i; cea708_t cea708; - cea708_init(&cea708); // set up a new popon frame + cea708_init(&cea708, sei->timestamp); // set up a new popon frame for (i = 0; i < scc->cc_size; ++i) { if (31 == cea708.user_data.cc_count) { @@ -581,7 +551,7 @@ libcaption_stauts_t sei_from_scc(sei_t* sei, const scc_t* scc) libcaption_stauts_t sei_from_caption_clear(sei_t* sei) { cea708_t cea708; - cea708_init(&cea708); // set up a new popon frame + cea708_init(&cea708, sei->timestamp); // set up a new popon frame cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL)); cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL)); cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL)); @@ -592,93 +562,183 @@ libcaption_stauts_t sei_from_caption_clear(sei_t* sei) return LIBCAPTION_OK; } //////////////////////////////////////////////////////////////////////////////// -static int avc_is_start_code(const uint8_t* data, int size, int* len) +// bitstream +void mpeg_bitstream_init(mpeg_bitstream_t* packet) { - if (3 > size) { - return -1; - } + packet->dts = 0; + packet->cts = 0; + packet->size = 0; + packet->front = 0; + packet->latent = 0; + packet->status = LIBCAPTION_OK; +} - if (1 < data[2]) { - return 3; +uint8_t mpeg_bitstream_packet_type(mpeg_bitstream_t* packet, unsigned stream_type) +{ + if (4 > packet->size) { + return 0; } - - if (0 != data[1]) { - return 2; + switch (stream_type) { + case STREAM_TYPE_H262: + return packet->data[3]; + case STREAM_TYPE_H264: + return packet->data[3] & 0x1F; + case STREAM_TYPE_H265: + return (packet->data[3] >> 1) & 0x3F; + default: + return 0; } +} - if (0 == data[0]) { - if (1 == data[2]) { - *len = 3; - return 0; - } - - if (4 <= size && 1 == data[3]) { - *len = 4; - return 0; +// TODO optomize +// static size_t find_start_code_increnental(const uint8_t* data, size_t size, size_t prev_size) +// { +// // prev_size MUST be at least 4 +// assert(3 < prev_size); +// uint32_t start_code = 0xffffffff; +// for (size_t i = prev_size - 3; i < size; ++i) { +// start_code = (start_code << 8) | data[i]; +// if (0x00000100 == (start_code & 0xffffff00)) { +// return i - 3; +// } +// } +// return 0; +// } + +static size_t find_start_code(const uint8_t* data, size_t size) +{ + uint32_t start_code = 0xffffffff; + for (size_t i = 1; i < size; ++i) { + start_code = (start_code << 8) | data[i]; + if (0x00000100 == (start_code & 0xffffff00)) { + return i - 3; } } - - return 1; + return 0; } -static int avc_find_start_code(const uint8_t* data, int size, int* len) +// WILL wrap around if larger than MAX_REFRENCE_FRAMES for memory saftey +cea708_t* _mpeg_bitstream_cea708_at(mpeg_bitstream_t* packet, size_t pos) { return &packet->cea708[(packet->front + pos) % MAX_REFRENCE_FRAMES]; } +cea708_t* _mpeg_bitstream_cea708_front(mpeg_bitstream_t* packet) { return _mpeg_bitstream_cea708_at(packet, 0); } +cea708_t* _mpeg_bitstream_cea708_back(mpeg_bitstream_t* packet) { return _mpeg_bitstream_cea708_at(packet, packet->latent - 1); } +cea708_t* _mpeg_bitstream_cea708_emplace_back(mpeg_bitstream_t* packet, double timestamp) { - int pos = 0; + ++packet->latent; + cea708_t* cea708 = _mpeg_bitstream_cea708_back(packet); + cea708_init(cea708, timestamp); + return cea708; +} - for (;;) { - // is pos pointing to a start code? - int isc = avc_is_start_code(data + pos, size - pos, len); - - if (0 < isc) { - pos += isc; - } else if (0 > isc) { - // No start code found - return isc; - } else { - // Start code found at pos - return pos; +void _mpeg_bitstream_cea708_sort(mpeg_bitstream_t* packet) +{ + // TODO better sort? (for small nearly sorted lists bubble is difficult to beat) + // This must be stable, decending sort +again: + for (size_t i = 1; i < packet->latent; ++i) { + cea708_t c; + cea708_t* a = _mpeg_bitstream_cea708_at(packet, i - 1); + cea708_t* b = _mpeg_bitstream_cea708_at(packet, i); + if (a->timestamp > b->timestamp) { + memcpy(&c, a, sizeof(cea708_t)); + memcpy(a, b, sizeof(cea708_t)); + memcpy(b, &c, sizeof(cea708_t)); + goto again; } } } -static int avc_find_start_code_increnental(const uint8_t* data, int size, int prev_size, int* len) +// Removes items from front +size_t mpeg_bitstream_flush(mpeg_bitstream_t* packet, caption_frame_t* frame) { - int offset = (3 <= prev_size) ? (prev_size - 3) : 0; - int pos = avc_find_start_code(data + offset, size - offset, len); - - if (0 <= pos) { - return pos + offset; + if (packet->latent) { + cea708_t* cea708 = _mpeg_bitstream_cea708_front(packet); + packet->status = libcaption_status_update(LIBCAPTION_OK, cea708_to_caption_frame(frame, cea708)); + packet->front = (packet->front + 1) % MAX_REFRENCE_FRAMES; + --packet->latent; } - return pos; + return packet->latent; } -void avcnalu_init(avcnalu_t* nalu) +void _mpeg_bitstream_cea708_sort_flush(mpeg_bitstream_t* packet, caption_frame_t* frame, double dts) { - memset(nalu, 0, sizeof(avcnalu_t)); + _mpeg_bitstream_cea708_sort(packet); + // Loop will terminate on LIBCAPTION_READY + while (packet->latent && packet->status == LIBCAPTION_OK && _mpeg_bitstream_cea708_front(packet)->timestamp < dts) { + mpeg_bitstream_flush(packet, frame); + } } -int avcnalu_parse_annexb(avcnalu_t* nalu, const uint8_t** data, size_t* size) +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) { - int scpos, sclen; - int new_size = (int)(nalu->size + (*size)); + if (MAX_NALU_SIZE <= packet->size) { + packet->status = LIBCAPTION_ERROR; + // fprintf(stderr, "LIBCAPTION_ERROR\n"); + return 0; + } - if (new_size > MAX_NALU_SIZE) { - (*size) = nalu->size = 0; - return LIBCAPTION_ERROR; + // consume upto MAX_NALU_SIZE bytes + if (MAX_NALU_SIZE <= packet->size + size) { + size = MAX_NALU_SIZE - packet->size; } - memcpy(&nalu->data[nalu->size], (*data), (*size)); - scpos = avc_find_start_code_increnental(&nalu->data[0], new_size, (int)nalu->size, &sclen); + sei_t sei; + size_t header_size, scpos; + packet->status = LIBCAPTION_OK; + memcpy(&packet->data[packet->size], data, size); + packet->size += size; - if (0 <= scpos) { - (*data) += (scpos - nalu->size) + sclen; - (*size) -= (scpos - nalu->size) + sclen; - nalu->size = scpos; - return 0 < nalu->size ? LIBCAPTION_READY : LIBCAPTION_OK; - } else { - (*size) = 0; - nalu->size = new_size; - return LIBCAPTION_OK; + while (packet->status == LIBCAPTION_OK && 0 < (scpos = find_start_code(&packet->data[0], packet->size))) { + switch (mpeg_bitstream_packet_type(packet, stream_type)) { + default: + break; + case H262_SEI_PACKET: + header_size = 4; + if (STREAM_TYPE_H262 == stream_type && scpos > header_size) { + cea708_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts); + packet->status = libcaption_status_update(packet->status, cea708_parse_h262(&packet->data[header_size], scpos - header_size, cea708)); + _mpeg_bitstream_cea708_sort_flush(packet, frame, dts); + } + break; + case H264_SEI_PACKET: + case H265_SEI_PACKET: + header_size = STREAM_TYPE_H264 == stream_type ? 4 : STREAM_TYPE_H265 == stream_type ? 5 : 0; + if (header_size && scpos > header_size) { + packet->status = libcaption_status_update(packet->status, sei_parse(&sei, &packet->data[header_size], scpos - header_size, dts + cts)); + for (sei_message_t* 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_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts); + packet->status = libcaption_status_update(packet->status, cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), cea708)); + _mpeg_bitstream_cea708_sort_flush(packet, frame, dts); + } + } + } + break; + } + + packet->size -= scpos; + memmove(&packet->data[0], &packet->data[scpos], packet->size); } + + return size; } +//////////////////////////////////////////////////////////////////////////////// +// // h262 +// libcaption_stauts_t h262_user_data_to_caption_frame(caption_frame_t* frame, mpeg_bitstream_t* packet, double dts, double cts) +// { +// cea708_t cea708; +// libcaption_stauts_t status = LIBCAPTION_OK; + +// cea708_init(&cea708); +// size_t size = mpeg_bitstream_size(packet, STREAM_TYPE_H262); +// const uint8_t* data = mpeg_bitstream_data(packet, STREAM_TYPE_H262); +// status = cea708_parse_h262(data, size, &cea708); +// // cea708_dump(&cea708); +// status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708, dts + cts)); + +// if (LIBCAPTION_READY == status) { +// frame->timestamp = dts + cts; +// } + +// return status; +// } diff --git a/src/srt.c b/src/srt.c index 40d7c2d..48891ad 100644 --- a/src/srt.c +++ b/src/srt.c @@ -22,8 +22,8 @@ /* THE SOFTWARE. */ /**********************************************************************************************/ #include "srt.h" -#include "vtt.h" #include "utf8.h" +#include "vtt.h" #include #include #include @@ -53,7 +53,7 @@ void srt_dump(srt_t* srt) vtt_crack_time(block->timestamp, &hh1, &mm1, &ss1, &ms1); vtt_crack_time(block->timestamp + block->duration, &hh2, &mm2, &ss2, &ms2); - printf("%02d\r\n%d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\r\n%s\r\n\r\n", i, + printf("%02d\r\n%d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\r\n%s\r\n", i, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, vtt_block_data(block)); } } diff --git a/src/utf8.c b/src/utf8.c index 45ee9b3..05e51e7 100644 --- a/src/utf8.c +++ b/src/utf8.c @@ -45,7 +45,7 @@ size_t utf8_char_length(const utf8_char_t* c) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; - return _utf8_char_length[(c[0] >> 3)&0x1F]; + return _utf8_char_length[(c[0] >> 3) & 0x1F]; } int utf8_char_whitespace(const utf8_char_t* c) @@ -212,26 +212,26 @@ utf8_char_t* utf8_load_text_file(const char* path, size_t* size) } } - data[*size] = '\0'; + data[*size] = 0; return data; } #ifndef strnstr -char *strnstr(const char *string1, const char *string2, size_t len) +char* strnstr(const char* string1, const char* string2, size_t len) { - size_t length2; + size_t length2; length2 = strlen(string2); - if (!length2) { - return (char *)string1; + if (!length2) { + return (char*)string1; } - while (len >= length2) { - len--; - if (!memcmp(string1, string2, length2)) - return (char *)string1; - string1++; - } - return NULL; + while (len >= length2) { + len--; + if (!memcmp(string1, string2, length2)) + return (char*)string1; + string1++; + } + return NULL; } #endif \ No newline at end of file diff --git a/src/vtt.c b/src/vtt.c index a8d9106..b082306 100644 --- a/src/vtt.c +++ b/src/vtt.c @@ -29,8 +29,9 @@ vtt_block_t* vtt_block_free_head(vtt_block_t* head); -vtt_t *vtt_new() { - vtt_t *vtt = malloc(sizeof(vtt_t)); +vtt_t* vtt_new() +{ + vtt_t* vtt = malloc(sizeof(vtt_t)); vtt->region_head = NULL; vtt->style_head = NULL; vtt->cue_head = NULL; @@ -63,35 +64,35 @@ vtt_block_t* vtt_block_new(vtt_t* vtt, const utf8_char_t* data, size_t size, enu block->text_size = size; switch (type) { - case VTT_REGION: - if (vtt->region_head == NULL) { - vtt->region_head = block; - } else { - vtt->region_tail->next = block; - } - vtt->region_tail = block; - break; - case VTT_STYLE: - if (vtt->style_head == NULL) { - vtt->style_head = block; - } else { - vtt->style_tail->next = block; - } - vtt->style_tail = block; - break; - case VTT_CUE: - if (vtt->cue_head == NULL) { - vtt->cue_head = block; - } else { - vtt->cue_tail->next = block; - } - vtt->cue_tail = block; - break; - case VTT_NOTE: - break; + case VTT_REGION: + if (vtt->region_head == NULL) { + vtt->region_head = block; + } else { + vtt->region_tail->next = block; + } + vtt->region_tail = block; + break; + case VTT_STYLE: + if (vtt->style_head == NULL) { + vtt->style_head = block; + } else { + vtt->style_tail->next = block; + } + vtt->style_tail = block; + break; + case VTT_CUE: + if (vtt->cue_head == NULL) { + vtt->cue_head = block; + } else { + vtt->cue_tail->next = block; + } + vtt->cue_tail = block; + break; + case VTT_NOTE: + break; } - utf8_char_t* dest = (utf8_char_t*)vtt_block_data(block); + utf8_char_t* dest = (utf8_char_t*)vtt_block_data(block); if (data) { memcpy(dest, data, size); } else { @@ -115,21 +116,24 @@ vtt_block_t* vtt_block_free_head(vtt_block_t* head) return next; } -void vtt_cue_free_head(vtt_t* vtt) { +void vtt_cue_free_head(vtt_t* vtt) +{ vtt->cue_head = vtt_block_free_head(vtt->cue_head); if (vtt->cue_head == NULL) { vtt->cue_tail = NULL; } } -void vtt_style_free_head(vtt_t* vtt) { +void vtt_style_free_head(vtt_t* vtt) +{ vtt->style_head = vtt_block_free_head(vtt->style_head); if (vtt->style_head == NULL) { vtt->style_tail = NULL; } } -void vtt_region_free_head(vtt_t* vtt) { +void vtt_region_free_head(vtt_t* vtt) +{ vtt->region_head = vtt_block_free_head(vtt->region_head); if (vtt->region_head == NULL) { vtt->region_tail = NULL; @@ -137,7 +141,8 @@ void vtt_region_free_head(vtt_t* vtt) { } #define VTTTIME2SECONDS(HH, MM, SS, MS) ((HH * 3600.0) + (MM * 60.0) + SS + (MS / 1000.0)) -double parse_timestamp(const utf8_char_t *line) { +double parse_timestamp(const utf8_char_t* line) +{ int hh, mm, ss, ms; if (sscanf(line, "%d:%2d:%2d%*1[,.]%3d", &hh, &mm, &ss, &ms) == 4) { return VTTTIME2SECONDS(hh, mm, ss, ms); @@ -148,7 +153,8 @@ double parse_timestamp(const utf8_char_t *line) { return -1.0; } -void parse_timestamps(const utf8_char_t* line, double *start_pts, double *end_pts, char **cue_settings) { +void parse_timestamps(const utf8_char_t* line, double* start_pts, double* end_pts, char** cue_settings) +{ char start_str[32]; char end_str[32]; char cue_str[1024]; @@ -174,17 +180,17 @@ void parse_timestamps(const utf8_char_t* line, double *start_pts, double *end_pt } } -vtt_t *vtt_parse(const utf8_char_t* data, size_t size) +vtt_t* vtt_parse(const utf8_char_t* data, size_t size) { return _vtt_parse(data, size, 0); } vtt_t* _vtt_parse(const utf8_char_t* data, size_t size, int srt_mode) { - vtt_t *vtt = NULL; + vtt_t* vtt = NULL; double str_pts = 0, end_pts = 0; size_t line_length = 0, trimmed_length = 0; - char *cue_settings; + char* cue_settings; enum VTT_BLOCK_TYPE block_type; size_t cue_id_length = 0; const utf8_char_t* cue_id = NULL; @@ -211,7 +217,7 @@ vtt_t* _vtt_parse(const utf8_char_t* data, size_t size, int srt_mode) do { data += line_length; size -= line_length; - line_length = utf8_line_length(data); // Line length + line_length = utf8_line_length(data); // Line length trimmed_length = utf8_trimmed_length(data, line_length); // Skip empty lines } while (0 < line_length && 0 == trimmed_length); @@ -295,16 +301,20 @@ int vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame) return caption_frame_from_text(frame, data); } -vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t *vtt) +vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt) { + if (vtt->cue_tail && 0 >= vtt->cue_tail->duration) { + vtt->cue_tail->duration = frame->timestamp - vtt->cue_tail->timestamp; + } + // CRLF per row, plus an extra at the end vtt_block_t* cue = vtt_block_new(vtt, NULL, 2 + CAPTION_FRAME_TEXT_BYTES, VTT_CUE); utf8_char_t* data = vtt_block_data(cue); caption_frame_to_text(frame, data); + cue->timestamp = frame->timestamp; // vtt requires an extra new line strcat((char*)data, "\r\n"); - // TODO: Set timestamps return cue; } @@ -335,13 +345,13 @@ static void _dump(vtt_t* vtt) printf("%s\n", block->cue_id); } - printf("%d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d", + printf("%02d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d", hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2); - + if (block->cue_settings != NULL) { printf(" %s", block->cue_settings); } - + printf("\r\n%s\r\n", vtt_block_data(block)); } }