[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)
This commit is contained in:
Artyom Fedoskin 2019-03-16 01:30:48 +01:00 committed by Carlos Fernandez Sanz
parent ab4f3d0d26
commit 4d24568a0b
2 changed files with 126 additions and 5 deletions

View File

@ -2,7 +2,9 @@
#include "utility.h"
#include "matroska.h"
#include "ccx_encoders_helpers.h"
#include "ccx_common_timing.h"
#include <limits.h>
#include <assert.h>
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;
}

View File

@ -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);