From 359d56851a908c8041b29a6b921b6c393983b1e4 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Wed, 18 Mar 2020 19:09:29 +0100 Subject: [PATCH] caption_frame_decode: rework API Fixes #59 * pop_on mode requires incrementing the frame timestamp until end_of_caption is encountered. * caption_frame_decode now always updates the timestamp of the frame when the timestamp parameter != -1. This requires that callers only pass a valid timestamp when a new one is encountered, for example with SCC the timestamp at the start of the cue, then -1 until the next new timestamp. * A new enum member is added for the return value, LIBCAPTION_CLEAR. It allows the caller to determine that closed captions should not be displayed anymore, in order to finish the previous cue earlier than the start of the next cue. --- caption/caption.h | 3 ++- caption/srt.h | 2 ++ caption/vtt.h | 4 ++++ examples/scc2srt.c | 14 +++++++++++++- examples/scc2vtt.c | 14 +++++++++++++- examples/sccdump.c | 12 ++++++++++-- src/caption.c | 18 +++++++++++------- src/cea708.c | 5 ++++- src/vtt.c | 7 +++++++ 9 files changed, 66 insertions(+), 13 deletions(-) diff --git a/caption/caption.h b/caption/caption.h index ee4d8a7..508935c 100644 --- a/caption/caption.h +++ b/caption/caption.h @@ -43,7 +43,8 @@ typedef signed int ssize_t; typedef enum { LIBCAPTION_ERROR = 0, LIBCAPTION_OK = 1, - LIBCAPTION_READY = 2 + LIBCAPTION_READY = 2, + LIBCAPTION_CLEAR = 3, } libcaption_stauts_t; static inline libcaption_stauts_t libcaption_status_update(libcaption_stauts_t old_stat, libcaption_stauts_t new_stat) diff --git a/caption/srt.h b/caption/srt.h index 6c4c437..37e1526 100644 --- a/caption/srt.h +++ b/caption/srt.h @@ -68,6 +68,8 @@ static inline utf8_char_t* srt_cue_data(srt_cue_t* cue) { return vtt_block_data( */ 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 void srt_cue_finish(caption_frame_t* frame, srt_t* srt) { return vtt_cue_finish(frame, srt); }; + /*! \brief \param */ diff --git a/caption/vtt.h b/caption/vtt.h index 4130054..93467bf 100644 --- a/caption/vtt.h +++ b/caption/vtt.h @@ -134,6 +134,10 @@ int vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame); \param */ vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt); + +// finishes the last cue +void vtt_cue_finish(caption_frame_t* frame, vtt_t* vtt); + /*! \brief \param */ diff --git a/examples/scc2srt.c b/examples/scc2srt.c index 3c96a88..8d965b5 100644 --- a/examples/scc2srt.c +++ b/examples/scc2srt.c @@ -42,11 +42,23 @@ int main(int argc, char** argv) scc_data += scc_to_608(&scc, scc_data); while (scc->cc_size) { + double timestamp = scc->timestamp; + for (i = 0; i < scc->cc_size; ++i) { // eia608_dump (scc->cc_data[i]); - if (LIBCAPTION_READY == caption_frame_decode(&frame, scc->cc_data[i], scc->timestamp)) { + + switch (caption_frame_decode(&frame, scc->cc_data[i], timestamp)) { + case LIBCAPTION_READY: srt_cue_from_caption_frame(&frame, srt); + break; + case LIBCAPTION_CLEAR: + srt_cue_finish(&frame, srt); + break; + default: + break; } + + timestamp = -1; } scc_data += scc_to_608(&scc, scc_data); diff --git a/examples/scc2vtt.c b/examples/scc2vtt.c index a5db1c2..2720e87 100644 --- a/examples/scc2vtt.c +++ b/examples/scc2vtt.c @@ -47,10 +47,22 @@ int main(int argc, char** argv) scc_data += scc_to_608(&scc, scc_data); while (scc->cc_size) { + double timestamp = scc->timestamp; + for (i = 0; i < scc->cc_size; ++i) { - if (LIBCAPTION_READY == caption_frame_decode(&frame, scc->cc_data[i], scc->timestamp)) { + + switch (caption_frame_decode(&frame, scc->cc_data[i], timestamp)) { + case LIBCAPTION_READY: vtt_cue_from_caption_frame(&frame, vtt); + break; + case LIBCAPTION_CLEAR: + vtt_cue_finish(&frame, vtt); + break; + default: + break; } + + timestamp = -1; } scc_data += scc_to_608(&scc, scc_data); diff --git a/examples/sccdump.c b/examples/sccdump.c index 090c535..4ec2bd4 100644 --- a/examples/sccdump.c +++ b/examples/sccdump.c @@ -39,13 +39,21 @@ int main(int argc, char** argv) scc_data += scc_to_608(&scc, scc_data); while (scc->cc_size) { - fprintf(stderr, "Timestamp: %f\n", scc->timestamp); + double timestamp = scc->timestamp; + + fprintf(stderr, "Timestamp: %f\n", timestamp); for (i = 0; i < scc->cc_size; ++i) { eia608_dump(scc->cc_data[i]); - if (LIBCAPTION_READY == caption_frame_decode(&frame, scc->cc_data[i], scc->timestamp)) { + switch (caption_frame_decode(&frame, scc->cc_data[i], timestamp)) { + case LIBCAPTION_READY: caption_frame_dump(&frame); + break; + default: + break; } + + timestamp = -1; } scc_data += scc_to_608(&scc, scc_data); diff --git a/src/caption.c b/src/caption.c index c7c2377..ff1f8fa 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_READY; + return LIBCAPTION_CLEAR; // ROLL-UP case eia608_control_roll_up_2: @@ -306,17 +306,21 @@ libcaption_stauts_t caption_frame_decode(caption_frame_t* frame, uint16_t cc_dat return frame->status; } - if (0 > frame->timestamp || frame->timestamp == timestamp || LIBCAPTION_READY == frame->status) { - frame->timestamp = timestamp; - frame->status = LIBCAPTION_OK; - } - // skip duplicate controll commands. We also skip duplicate specialna to match the behaviour of iOS/vlc if ((eia608_is_specialna(cc_data) || eia608_is_control(cc_data)) && cc_data == frame->state.cc_data) { + if (caption_frame_popon (frame)) + frame->timestamp += (1 / 29.97); + return LIBCAPTION_OK; + } + + if (timestamp >= 0) { + frame->timestamp = timestamp; frame->status = LIBCAPTION_OK; - return frame->status; } + if (caption_frame_popon (frame)) + frame->timestamp += (1 / 29.97); + frame->state.cc_data = cc_data; if (frame->xds.state) { diff --git a/src/cea708.c b/src/cea708.c index 2405c9b..e47b9e5 100644 --- a/src/cea708.c +++ b/src/cea708.c @@ -268,13 +268,16 @@ libcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* ce libcaption_stauts_t status = LIBCAPTION_OK; if (GA94 == cea708->user_identifier) { + float timestamp = cea708->timestamp; + 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, cea708->timestamp)); + status = libcaption_status_update(status, caption_frame_decode(frame, cc_data, timestamp)); + timestamp = -1; } } } diff --git a/src/vtt.c b/src/vtt.c index 23aa10d..a47760d 100644 --- a/src/vtt.c +++ b/src/vtt.c @@ -316,6 +316,13 @@ vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt) return cue; } +void vtt_cue_finish(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; + } +} + static void _dump(vtt_t* vtt) { vtt_block_t* block;