From 4d24568a0be6e20e90dba862348d789689803470 Mon Sep 17 00:00:00 2001 From: Artyom Fedoskin Date: Sat, 16 Mar 2019 01:30:48 +0100 Subject: [PATCH] [FEATURE] Added support for EIA-608 inside MKV (#1080) * Initial work on adding EIA-608 support to Matroska * [REQUEST] Finished adding support for EIA-608 inside MKV (#1068) --- src/lib_ccx/matroska.c | 119 +++++++++++++++++++++++++++++++++++++++-- src/lib_ccx/matroska.h | 12 +++++ 2 files changed, 126 insertions(+), 5 deletions(-) diff --git a/src/lib_ccx/matroska.c b/src/lib_ccx/matroska.c index 3a328201..46c6bcd3 100644 --- a/src/lib_ccx/matroska.c +++ b/src/lib_ccx/matroska.c @@ -2,7 +2,9 @@ #include "utility.h" #include "matroska.h" #include "ccx_encoders_helpers.h" +#include "ccx_common_timing.h" #include +#include void skip_bytes(FILE* file, ULLONG n) { FSEEK(file, n, SEEK_CUR); @@ -480,8 +482,8 @@ void parse_segment_cluster(struct matroska_ctx* mkv_ctx) { read_vint_block_skip(file); MATROSKA_SWITCH_BREAK(code, code_len); case MATROSKA_SEGMENT_CLUSTER_SIMPLE_BLOCK: - // Same as Block inside the Block Group, but we can't save subs in this structure - read_vint_block_skip(file); + // Same as Block inside the Block Group + parse_simple_block(mkv_ctx, timecode); MATROSKA_SWITCH_BREAK(code, code_len); case MATROSKA_SEGMENT_CLUSTER_BLOCK_GROUP: parse_segment_cluster_block_group(mkv_ctx, timecode); @@ -515,6 +517,74 @@ void parse_segment_cluster(struct matroska_ctx* mkv_ctx) { (int) (mkv_ctx->current_second % 60)); } +void parse_simple_block(struct matroska_ctx* mkv_ctx, ULLONG frame_timestamp) { + FILE* file = mkv_ctx->file; + + struct matroska_avc_frame frame; + ULLONG len = read_vint_length(file); + ULLONG pos = get_current_byte(file); + + ULLONG track = read_vint_length(file); + + if(track != mkv_ctx->avc_track_number) { + // Skip everything except AVC track + skip_bytes(file, len - 1); // 1 byte for track + return; + } + + ULLONG timecode = mkv_read_byte(file); + timecode <<= 8; timecode += mkv_read_byte(file); + mkv_read_byte(file); // skip flags byte + + // Construct the frame + frame.len = pos + len - get_current_byte(file); + frame.data = read_byte_block(file, frame.len); + frame.FTS = frame_timestamp + timecode; + + process_avc_frame_mkv(mkv_ctx, frame); + + free(frame.data); +} + +static long bswap32(long v) +{ + // For 0x12345678 returns 78563412 + long swapped=((v&0xFF)<<24) | ((v&0xFF00)<<8) | ((v&0xFF0000) >>8) | ((v&0xFF000000) >>24); + return swapped; +} + +int process_avc_frame_mkv(struct matroska_ctx* mkv_ctx, struct matroska_avc_frame frame) +{ + int status = 0; + uint32_t i; + struct lib_cc_decode *dec_ctx = update_decoder_list(mkv_ctx->ctx); + + // Delete + // Inspired by set_fts(struct ccx_common_timing_ctx *ctx) + set_current_pts(dec_ctx->timing, frame.FTS*(MPEG_CLOCK_FREQ/1000)); + set_fts(dec_ctx->timing); + + // NAL unit length is assumed to be 4 + uint8_t nal_unit_size = 4; + + for(i = 0; i < frame.len; ) + { + uint32_t nal_length; + + nal_length = bswap32(*(long* ) &frame.data[i]); + i += nal_unit_size; + + if (nal_length>0) + do_NAL (dec_ctx, (unsigned char *) &( frame.data[i]), nal_length, &mkv_ctx->dec_sub); + i += nal_length; + } // outer for + assert(i == frame.len); + + mkv_ctx->current_second = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000); + + return status; +} + char* get_track_entry_type_description(enum matroska_track_entry_type type) { switch (type) { case MATROSKA_TRACK_TYPE_VIDEO: @@ -614,11 +684,15 @@ void parse_segment_track_entry(struct matroska_ctx* mkv_ctx) { codec_id_string = read_vint_block_string(file); codec_id = get_track_subtitle_codec_id(codec_id_string); mprint(" Codec ID: %s\n", codec_id_string); - free(codec_id_string); + //We only support AVC by now + if( *codec_id_string == *avc_codec_id) mkv_ctx->avc_track_number = track_number; + else free(codec_id_string); MATROSKA_SWITCH_BREAK(code, code_len); case MATROSKA_SEGMENT_TRACK_CODEC_PRIVATE: if (track_type == MATROSKA_TRACK_TYPE_SUBTITLE) header = read_vint_block_string(file); + else if( *codec_id_string == *avc_codec_id && mkv_ctx->avc_track_number == track_number) + parse_private_codec_data(mkv_ctx); else read_vint_block_skip(file); MATROSKA_SWITCH_BREAK(code, code_len); @@ -723,6 +797,24 @@ void parse_segment_track_entry(struct matroska_ctx* mkv_ctx) { free(lang); } +// Read sequence parameter set for AVC +void parse_private_codec_data(struct matroska_ctx* mkv_ctx) +{ + FILE* file = mkv_ctx->file; + ULLONG len = read_vint_length(file); + // Skip reserved data + ULLONG reserved_len = 8; + skip_bytes(file, reserved_len); + + struct lib_cc_decode *dec_ctx = update_decoder_list(mkv_ctx->ctx); + ULLONG size = len - reserved_len; + + unsigned char* data = read_byte_block(file, size); + do_NAL(dec_ctx, data, size, &mkv_ctx->dec_sub); + + free(data); +} + void parse_segment_tracks(struct matroska_ctx* mkv_ctx) { FILE* file = mkv_ctx->file; @@ -740,7 +832,7 @@ void parse_segment_tracks(struct matroska_ctx* mkv_ctx) case MATROSKA_SEGMENT_TRACK_ENTRY: - parse_segment_track_entry(mkv_ctx); + parse_segment_track_entry(mkv_ctx); MATROSKA_SWITCH_BREAK(code, code_len); /* Misc ids */ @@ -1002,6 +1094,13 @@ void matroska_save_all(struct matroska_ctx* mkv_ctx,char* lang) else save_sub_track(mkv_ctx, mkv_ctx->sub_tracks[i]); } + + //EIA-608 + struct lib_cc_decode *dec_ctx = update_decoder_list(mkv_ctx->ctx); + if(mkv_ctx->dec_sub.got_output) { + struct encoder_ctx *enc_ctx = update_encoder_list(mkv_ctx->ctx); + encode_sub(enc_ctx, &mkv_ctx->dec_sub); + } } void matroska_free_all(struct matroska_ctx* mkv_ctx) @@ -1083,6 +1182,9 @@ int matroska_loop(struct lib_ccx_ctx *ctx) mkv_ctx->filename = ctx->inputfile[ctx->current_file]; mkv_ctx->file = create_file(ctx); mkv_ctx->sub_tracks = malloc(sizeof(struct matroska_sub_track**)); + //EIA-608 + memset(&mkv_ctx->dec_sub,0,sizeof(mkv_ctx->dec_sub)); + mkv_ctx->avc_track_number = -1; matroska_parse(mkv_ctx); @@ -1094,7 +1196,14 @@ int matroska_loop(struct lib_ccx_ctx *ctx) int sentence_count = mkv_ctx->sentence_count; matroska_free_all(mkv_ctx); - mprint("\n"); + mprint("\n\n"); + // Support only one AVC track by now + if(mkv_ctx->avc_track_number > -1) + mprint("Found AVC track. "); + else + mprint("Found no AVC track. "); + + if(mkv_ctx->dec_sub.got_output) return 1; return sentence_count; } diff --git a/src/lib_ccx/matroska.h b/src/lib_ccx/matroska.h index 5550187a..941aae4d 100644 --- a/src/lib_ccx/matroska.h +++ b/src/lib_ccx/matroska.h @@ -177,6 +177,8 @@ char* matroska_track_text_subtitle_id_extensions[] = { NULL, NULL // Unknown }; +char* avc_codec_id = "V_MPEG4/ISO/AVC"; + /* Messages */ #define MATROSKA_INFO "\nMatroska parser info: " #define MATROSKA_WARNING "\nMatroska parser warning: " @@ -204,6 +206,11 @@ struct matroska_sub_sentence { struct block_addition* blockaddition; }; +struct matroska_avc_frame { + UBYTE *data; + ULLONG len; + ULLONG FTS; +}; struct matroska_sub_track { char* header; // Style header for ASS/SSA (and other) subtitles @@ -219,6 +226,8 @@ struct matroska_sub_track { struct matroska_ctx { struct matroska_sub_track** sub_tracks; struct lib_ccx_ctx* ctx; + struct cc_subtitle dec_sub; + int avc_track_number; // ID of AVC track. -1 if there is none int sub_tracks_count; int block_index; int sentence_count; @@ -247,7 +256,10 @@ void parse_segment_info(FILE* file); struct matroska_sub_sentence* parse_segment_cluster_block_group_block(struct matroska_ctx* mkv_ctx, ULLONG cluster_timecode); void parse_segment_cluster_block_group(struct matroska_ctx* mkv_ctx, ULLONG cluster_timecode); void parse_segment_cluster(struct matroska_ctx* mkv_ctx); +void parse_simple_block(struct matroska_ctx* mkv_ctx, ULLONG frame_timestamp); +int process_avc_frame_mkv(struct matroska_ctx* mkv_ctx, struct matroska_avc_frame frame); void parse_segment_track_entry(struct matroska_ctx* mkv_ctx); +void parse_private_codec_data(struct matroska_ctx* mkv_ctx); void parse_segment_tracks(struct matroska_ctx* mkv_ctx); void parse_segment(struct matroska_ctx* mkv_ctx);