Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] [FIX] #1499 Persistent Dtvcc struct for CEA-708 decoding in Rust #1501

Closed
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ windows/.vs/**
*.opendb
*.db
*.vscode
.cache

####
# Ignore the header file that is updated upon build
Expand Down Expand Up @@ -153,4 +154,4 @@ src/rust/target/
windows/ccx_rust.lib
windows/*/debug/*
windows/*/CACHEDIR.TAG
windows/.rustc_info.json
windows/.rustc_info.json
8 changes: 8 additions & 0 deletions src/lib_ccx/ccx_decoders_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ made to reuse, not duplicate, as many functions as possible */
#ifndef DISABLE_RUST
extern int ccxr_process_cc_data(struct lib_cc_decode *dec_ctx, unsigned char *cc_data, int cc_count);
extern void ccxr_flush_decoder(struct dtvcc_ctx *dtvcc, struct dtvcc_service_decoder *decoder);
extern void *ccxr_dtvcc_init(struct ccx_decoder_dtvcc_settings *settings_dtvcc);
extern void ccxr_dtvcc_free(void *dtvcc_rust);
#endif

uint64_t utc_refvalue = UINT64_MAX; /* _UI64_MAX/UINT64_MAX means don't use UNIX, 0 = use current system time as reference, +1 use a specific reference */
Expand Down Expand Up @@ -232,6 +234,9 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
void dinit_cc_decode(struct lib_cc_decode **ctx)
{
struct lib_cc_decode *lctx = *ctx;
#ifndef DISABLE_RUST
ccxr_dtvcc_free(lctx->dtvcc_rust);
#endif
dtvcc_free(&lctx->dtvcc);
dinit_avc(&lctx->avc_ctx);
ccx_decoder_608_dinit_library(&lctx->context_cc608_field_1);
Expand Down Expand Up @@ -261,6 +266,9 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
ctx->no_rollup = setting->no_rollup;
ctx->noscte20 = setting->noscte20;

#ifndef DISABLE_RUST
ctx->dtvcc_rust = ccxr_dtvcc_init(setting->settings_dtvcc);
#endif
ctx->dtvcc = dtvcc_init(setting->settings_dtvcc);
ctx->dtvcc->is_active = setting->settings_dtvcc->enabled;

Expand Down
1 change: 1 addition & 0 deletions src/lib_ccx/ccx_decoders_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ struct lib_cc_decode
int stat_divicom;
int false_pict_header;

void *dtvcc_rust;
dtvcc_ctx *dtvcc;
int current_field;
// Analyse/use the picture information
Expand Down
16 changes: 16 additions & 0 deletions src/lib_ccx/general_loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include "dvd_subtitle_decoder.h"
#include "ccx_demuxer_mxf.h"

#ifndef DISABLE_RUST
extern void ccxr_dtvcc_set_encoder(void *dtvcc_rust, void *encoder);
#endif

int end_of_file = 0; // End of file?

// Program stream specific data grabber
Expand Down Expand Up @@ -896,7 +900,11 @@ int process_non_multiprogram_general_loop(struct lib_ccx_ctx *ctx,
cinfo = get_cinfo(ctx->demux_ctx, pid);
*enc_ctx = update_encoder_list_cinfo(ctx, cinfo);
*dec_ctx = update_decoder_list_cinfo(ctx, cinfo);
#ifdef DISABLE_RUST
(*dec_ctx)->dtvcc->encoder = (void *)(*enc_ctx);
#else
ccxr_dtvcc_set_encoder((*dec_ctx)->dtvcc_rust, *enc_ctx);
#endif

if ((*dec_ctx)->timing->min_pts == 0x01FFFFFFFFLL) // if we didn't set the min_pts of the program
{
Expand Down Expand Up @@ -1093,7 +1101,11 @@ int general_loop(struct lib_ccx_ctx *ctx)

enc_ctx = update_encoder_list_cinfo(ctx, cinfo);
dec_ctx = update_decoder_list_cinfo(ctx, cinfo);
#ifdef DISABLE_RUST
dec_ctx->dtvcc->encoder = (void *)enc_ctx; // WARN: otherwise cea-708 will not work
#else
ccxr_dtvcc_set_encoder(dec_ctx->dtvcc_rust, (void *)enc_ctx);
#endif

if (dec_ctx->timing->min_pts == 0x01FFFFFFFFLL) // if we didn't set the min_pts of the program
{
Expand Down Expand Up @@ -1268,7 +1280,11 @@ int rcwt_loop(struct lib_ccx_ctx *ctx)
}

dec_ctx = update_decoder_list(ctx);
#ifdef DISABLE_RUST
dec_ctx->dtvcc->encoder = (void *)enc_ctx; // WARN: otherwise cea-708 will not work
#else
ccxr_dtvcc_set_encoder(dec_ctx->dtvcc_rust, (void *)enc_ctx);
#endif
if (parsebuf[6] == 0 && parsebuf[7] == 2)
{
dec_ctx->codec = CCX_CODEC_TELETEXT;
Expand Down
10 changes: 10 additions & 0 deletions src/lib_ccx/mp4.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

#define GF_ISOM_SUBTYPE_C708 GF_4CC('c', '7', '0', '8')

#ifndef DISABLE_RUST
extern void ccxr_process_data(void *dtvcc_rust, unsigned char cc_valid, unsigned char cc_char, unsigned char data1, unsigned char data2);
extern void ccxr_dtvcc_set_encoder(void *dtvcc_rust, void *encoder);
#endif

static short bswap16(short v)
{
return ((v >> 8) & 0x00FF) | ((v << 8) & 0xFF00);
Expand Down Expand Up @@ -394,8 +399,13 @@ static int process_clcp(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx,
continue;
}
// WARN: otherwise cea-708 will not work
#ifdef DISABLE_RUST
dec_ctx->dtvcc->encoder = (void *)enc_ctx;
dtvcc_process_data(dec_ctx->dtvcc, (unsigned char *)temp);
#else
ccxr_dtvcc_set_encoder(dec_ctx->dtvcc_rust, (void *)enc_ctx);
ccxr_process_data(dec_ctx->dtvcc_rust, cc_valid, cc_type, cc_data[1], cc_data[2]);
#endif
cb_708++;
}
if (ctx->write_format == CCX_OF_MCC)
Expand Down
1 change: 1 addition & 0 deletions src/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ fn main() {
}

let bindings = builder
.derive_default(true)
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
Expand Down
153 changes: 118 additions & 35 deletions src/rust/src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ mod timing;
mod tv_screen;
mod window;

use crate::{bindings::*, utils::is_true};
use crate::{
bindings::*,
utils::{is_false, is_true},
};

use log::{debug, warn};

Expand All @@ -19,47 +22,107 @@ const CCX_DTVCC_SCREENGRID_ROWS: u8 = 75;
const CCX_DTVCC_SCREENGRID_COLUMNS: u8 = 210;
const CCX_DTVCC_MAX_ROWS: u8 = 15;
const CCX_DTVCC_MAX_COLUMNS: u8 = 32 * 2;
const CCX_DTVCC_MAX_SERVICES: usize = 63;
// const CCX_DTVCC_MAX_WINDOWS: usize = 8;

/// Context required for processing 708 data
pub struct Dtvcc<'a> {
pub struct Dtvcc {
pub is_active: bool,
pub active_services_count: u8,
pub services_active: Vec<i32>,
pub services_active: [i32; CCX_DTVCC_MAX_SERVICES],
pub report_enabled: bool,
pub report: &'a mut ccx_decoder_dtvcc_report,
pub decoders: Vec<&'a mut dtvcc_service_decoder>,
pub packet: Vec<u8>,
pub report: *mut ccx_decoder_dtvcc_report,
pub decoders: [Option<Box<dtvcc_service_decoder>>; CCX_DTVCC_MAX_SERVICES],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no longer a need to use bindings for any 708 related structs/enums, we can define them now in rust itself. We still need to use bindings for encoder, timing and report. But all others should be defined in rust.
This is going to be somewhat bigger and comprehensive change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that be a separate PR since it seems to be independent of this one?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be in this PR as I think it will allow you to remove the additional Boxing required here, and also will make the code more idiomatic rust as we can stop relying on the C bindings

pub packet: [u8; CCX_DTVCC_MAX_SERVICES],
pub packet_length: u8,
pub is_header_parsed: bool,
pub last_sequence: i32,
pub encoder: &'a mut encoder_ctx,
pub encoder: *mut encoder_ctx,
pub no_rollup: bool,
pub timing: &'a mut ccx_common_timing_ctx,
pub timing: *mut ccx_common_timing_ctx,
}

impl<'a> Dtvcc<'a> {
impl Dtvcc {
/// Create a new dtvcc context
pub fn new(ctx: &'a mut dtvcc_ctx) -> Self {
let report = unsafe { &mut *ctx.report };
let encoder = unsafe { &mut *(ctx.encoder as *mut encoder_ctx) };
let timing = unsafe { &mut *ctx.timing };
pub fn new(opts: &ccx_decoder_dtvcc_settings) -> Self {
// closely follows `dtvcc_init` at `src/lib_ccx/ccx_dtvcc.c:76`

let report = opts.report;
unsafe {
(*report).reset_count = 0;
}

let is_active = false;
let no_rollup = is_true(opts.no_rollup);
let active_services_count = opts.active_services_count as u8;
let services_active = opts.services_enabled;

// `dtvcc_clear_packet` does the following
let packet_length = 0;
let is_header_parsed = false;
let packet = [0; CCX_DTVCC_MAX_SERVICES]; // unlike C, packet is allocated on the stack

let last_sequence = CCX_DTVCC_NO_LAST_SEQUENCE;

let report_enabled = is_true(opts.print_file_reports);
let timing = opts.timing;

// unlike C, here the decoders are allocated on the stack as an array.
let decoders = {
const INIT: Option<Box<dtvcc_service_decoder>> = None;
let mut decoders = [INIT; CCX_DTVCC_MAX_SERVICES];

decoders
.iter_mut()
.zip(opts.services_enabled)
.enumerate()
.for_each(|(i, (d, se))| {
if is_false(se) {
return;
}

let mut decoder = Box::new(dtvcc_service_decoder {
// we cannot allocate this on the stack as `dtvcc_service_decoder` is a C
// struct cannot be changed trivially
tv: Box::into_raw(Box::new(dtvcc_tv_screen {
cc_count: 0,
service_number: i as i32 + 1,
..dtvcc_tv_screen::default()
})),
..dtvcc_service_decoder::default()
});

decoder.windows.iter_mut().for_each(|w| {
w.memory_reserved = 0;
});

unsafe { dtvcc_windows_reset(decoder.as_mut()) };

*d = Some(decoder);
});

decoders
};

let encoder = std::ptr::null_mut(); // Unlike C, does not mention `encoder` and is initialised to `null` by default

Self {
is_active: is_true(ctx.is_active),
active_services_count: ctx.active_services_count as u8,
services_active: ctx.services_active.to_vec(),
report_enabled: is_true(ctx.report_enabled),
report,
decoders: ctx.decoders.iter_mut().collect(),
packet: ctx.current_packet.to_vec(),
packet_length: ctx.current_packet_length as u8,
is_header_parsed: is_true(ctx.is_current_packet_header_parsed),
last_sequence: ctx.last_sequence,
encoder,
no_rollup: is_true(ctx.no_rollup),
is_active,
no_rollup,
active_services_count,
services_active,
packet_length,
is_header_parsed,
packet,
last_sequence,
report_enabled,
timing,
decoders,
encoder,
}
}

/// Process cc data and add it to the dtvcc packet
pub fn process_cc_data(&mut self, cc_valid: u8, cc_type: u8, data1: u8, data2: u8) {
if !self.is_active && !self.report_enabled {
Expand Down Expand Up @@ -166,15 +229,15 @@ impl<'a> Dtvcc<'a> {
}

if block_length != 0 {
self.report.services[service_number as usize] = 1;
unsafe { (*self.report).services[service_number as usize] = 1 };
}

if service_number > 0 && is_true(self.services_active[(service_number - 1) as usize]) {
let decoder = &mut self.decoders[(service_number - 1) as usize];
decoder.process_service_block(
decoder.as_mut().unwrap().process_service_block(
&self.packet[pos as usize..(pos + block_length) as usize],
self.encoder,
self.timing,
unsafe { self.encoder.as_mut().unwrap() },
unsafe { self.timing.as_mut().unwrap() },
self.no_rollup,
);
}
Expand All @@ -197,6 +260,33 @@ impl<'a> Dtvcc<'a> {
}
}

impl Drop for Dtvcc {
fn drop(&mut self) {
// closely follows `dtvcc_free` at `src/lib_ccx/ccx_dtvcc.c:126`
for i in 0..CCX_DTVCC_MAX_SERVICES {
if let Some(decoder) = self.decoders[i].as_mut() {
if !is_true(self.services_active[i]) {
continue;
}

decoder.windows.iter_mut().for_each(|window| {
if is_false(window.memory_reserved) {
return;
}

window.rows.iter().for_each(|symbol_ptr| unsafe {
symbol_ptr.drop_in_place();
});

window.memory_reserved = 0;
});

unsafe { decoder.tv.drop_in_place() };
}
}
}
}

/// A single character symbol
///
/// sym stores the symbol
Expand All @@ -216,10 +306,3 @@ impl dtvcc_symbol {
is_true(self.init)
}
}

impl Default for dtvcc_symbol {
/// Create a blank uninitialized symbol
fn default() -> Self {
Self { sym: 0, init: 0 }
}
}
8 changes: 4 additions & 4 deletions src/rust/src/decoder/tv_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ impl dtvcc_tv_screen {
use_colors: bool,
) -> Result<(), String> {
let mut buf = Vec::new();
let mut pen_color = dtvcc_pen_color::default();
let mut pen_attribs = dtvcc_pen_attribs::default();
let mut pen_color = dtvcc_pen_color::new();
let mut pen_attribs = dtvcc_pen_attribs::new();
let (first, last) = self.get_write_interval(row_index);
debug!("First: {}, Last: {}", first, last);

Expand Down Expand Up @@ -418,7 +418,7 @@ impl dtvcc_tv_screen {
return;
}
let new_pen_attribs = if col_index >= CCX_DTVCC_SCREENGRID_COLUMNS as usize {
dtvcc_pen_attribs::default()
dtvcc_pen_attribs::new()
} else {
self.pen_attribs[row_index][col_index]
};
Expand Down Expand Up @@ -455,7 +455,7 @@ impl dtvcc_tv_screen {
return;
}
let new_pen_color = if col_index >= CCX_DTVCC_SCREENGRID_COLUMNS as usize {
dtvcc_pen_color::default()
dtvcc_pen_color::new()
} else {
self.pen_colors[row_index][col_index]
};
Expand Down
Loading