mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-02-27 18:16:20 +00:00
ccdetect: extract CDP parser to a separate module
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/474>
This commit is contained in:
parent
19611f0ebe
commit
3c982a3de0
3 changed files with 157 additions and 134 deletions
|
@ -22,11 +22,9 @@ use gst::subclass::prelude::*;
|
||||||
use gst::{gst_trace, gst_warning};
|
use gst::{gst_trace, gst_warning};
|
||||||
use gst_base::subclass::prelude::*;
|
use gst_base::subclass::prelude::*;
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use crate::ccutils::{extract_cdp, ParseError, ParseErrorCode};
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::u64;
|
use std::u64;
|
||||||
|
|
||||||
|
@ -83,29 +81,6 @@ struct CCPacketContents {
|
||||||
cc708: bool,
|
cc708: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
enum ParseErrorCode {
|
|
||||||
WrongLength,
|
|
||||||
WrongMagicSequence,
|
|
||||||
WrongLayout,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct ParseError {
|
|
||||||
code: ParseErrorCode,
|
|
||||||
byte: usize,
|
|
||||||
msg: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{:?} at byte {}: {}", self.code, self.byte, self.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ParseError {}
|
|
||||||
|
|
||||||
impl CCDetect {
|
impl CCDetect {
|
||||||
fn detect_cc_data(data: &[u8]) -> Result<CCPacketContents, ParseError> {
|
fn detect_cc_data(data: &[u8]) -> Result<CCPacketContents, ParseError> {
|
||||||
if data.len() % 3 != 0 {
|
if data.len() % 3 != 0 {
|
||||||
|
@ -172,115 +147,10 @@ impl CCDetect {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect_cdp(mut data: &[u8]) -> Result<CCPacketContents, ParseError> {
|
fn detect_cdp(data: &[u8]) -> Result<CCPacketContents, ParseError> {
|
||||||
/* logic from ccconverter */
|
let data = extract_cdp(data)?;
|
||||||
let data_len = data.len();
|
|
||||||
|
|
||||||
if data.len() < 11 {
|
Self::detect_cc_data(data)
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongLength,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: format!(
|
|
||||||
"cdp packet too short {}. expected at least {}",
|
|
||||||
data.len(),
|
|
||||||
11
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if 0x9669 != BigEndian::read_u16(&data[..2]) {
|
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongMagicSequence,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: String::from("cdp packet does not have initial magic bytes of 0x9669"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
data = &data[2..];
|
|
||||||
|
|
||||||
if (data[0] as usize) != data_len {
|
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongLength,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: format!(
|
|
||||||
"advertised cdp packet length {} does not match length of data {}",
|
|
||||||
data[0], data_len
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
data = &data[1..];
|
|
||||||
|
|
||||||
/* skip framerate value */
|
|
||||||
data = &data[1..];
|
|
||||||
|
|
||||||
let flags = data[0];
|
|
||||||
data = &data[1..];
|
|
||||||
|
|
||||||
if flags & 0x40 == 0 {
|
|
||||||
/* no cc_data */
|
|
||||||
return Ok(CCPacketContents {
|
|
||||||
cc608: false,
|
|
||||||
cc708: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* skip sequence counter */
|
|
||||||
data = &data[2..];
|
|
||||||
|
|
||||||
/* timecode present? */
|
|
||||||
if flags & 0x80 == 0x80 {
|
|
||||||
if data.len() < 5 {
|
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongLength,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: String::from("cdp packet signals a timecode but is not large enough to contain a timecode")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
data = &data[5..];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cc_data */
|
|
||||||
if data.len() < 2 {
|
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongLength,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: String::from(
|
|
||||||
"cdp packet signals cc_data but is not large enough to contain cc_data",
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if data[0] != 0x72 {
|
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongMagicSequence,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: String::from("ccp is missing start code 0x72"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
data = &data[1..];
|
|
||||||
|
|
||||||
let cc_count = data[0];
|
|
||||||
data = &data[1..];
|
|
||||||
if cc_count & 0xe0 != 0xe0 {
|
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongMagicSequence,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: format!("reserved bits are not 0xe0, found {:02x}", cc_count & 0xe0),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let cc_count = cc_count & 0x1f;
|
|
||||||
let len = 3 * cc_count as usize;
|
|
||||||
|
|
||||||
if len > data.len() {
|
|
||||||
return Err(ParseError {
|
|
||||||
code: ParseErrorCode::WrongLength,
|
|
||||||
byte: data_len - data.len(),
|
|
||||||
msg: String::from("cc_data length extends past the end of the cdp packet"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: validate checksum */
|
|
||||||
|
|
||||||
Self::detect_cc_data(&data[..len])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect(format: CCFormat, data: &[u8]) -> Result<CCPacketContents, ParseError> {
|
fn detect(format: CCFormat, data: &[u8]) -> Result<CCPacketContents, ParseError> {
|
||||||
|
|
152
video/closedcaption/src/ccutils.rs
Normal file
152
video/closedcaption/src/ccutils.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the
|
||||||
|
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||||
|
// Boston, MA 02110-1335, USA.
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ParseErrorCode {
|
||||||
|
WrongLength,
|
||||||
|
WrongMagicSequence,
|
||||||
|
WrongLayout,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ParseError {
|
||||||
|
pub code: ParseErrorCode,
|
||||||
|
pub byte: usize,
|
||||||
|
pub msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_cdp(mut data: &[u8]) -> Result<&[u8], ParseError> {
|
||||||
|
/* logic from ccconverter */
|
||||||
|
let data_len = data.len();
|
||||||
|
|
||||||
|
if data.len() < 11 {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongLength,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: format!(
|
||||||
|
"cdp packet too short {}. expected at least {}",
|
||||||
|
data.len(),
|
||||||
|
11
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if 0x9669 != BigEndian::read_u16(&data[..2]) {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongMagicSequence,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: String::from("cdp packet does not have initial magic bytes of 0x9669"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data = &data[2..];
|
||||||
|
|
||||||
|
if (data[0] as usize) != data_len {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongLength,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: format!(
|
||||||
|
"advertised cdp packet length {} does not match length of data {}",
|
||||||
|
data[0], data_len
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data = &data[1..];
|
||||||
|
|
||||||
|
/* skip framerate value */
|
||||||
|
data = &data[1..];
|
||||||
|
|
||||||
|
let flags = data[0];
|
||||||
|
data = &data[1..];
|
||||||
|
|
||||||
|
if flags & 0x40 == 0 {
|
||||||
|
/* no cc_data */
|
||||||
|
return Ok(&[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip sequence counter */
|
||||||
|
data = &data[2..];
|
||||||
|
|
||||||
|
/* timecode present? */
|
||||||
|
if flags & 0x80 == 0x80 {
|
||||||
|
if data.len() < 5 {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongLength,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: String::from(
|
||||||
|
"cdp packet signals a timecode but is not large enough to contain a timecode",
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data = &data[5..];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cc_data */
|
||||||
|
if data.len() < 2 {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongLength,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: String::from(
|
||||||
|
"cdp packet signals cc_data but is not large enough to contain cc_data",
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if data[0] != 0x72 {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongMagicSequence,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: String::from("ccp is missing start code 0x72"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data = &data[1..];
|
||||||
|
|
||||||
|
let cc_count = data[0];
|
||||||
|
data = &data[1..];
|
||||||
|
if cc_count & 0xe0 != 0xe0 {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongMagicSequence,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: format!("reserved bits are not 0xe0, found {:02x}", cc_count & 0xe0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let cc_count = cc_count & 0x1f;
|
||||||
|
let len = 3 * cc_count as usize;
|
||||||
|
|
||||||
|
if len > data.len() {
|
||||||
|
return Err(ParseError {
|
||||||
|
code: ParseErrorCode::WrongLength,
|
||||||
|
byte: data_len - data.len(),
|
||||||
|
msg: String::from("cc_data length extends past the end of the cdp packet"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: validate checksum */
|
||||||
|
|
||||||
|
Ok(&data[..len])
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?} at byte {}: {}", self.code, self.byte, self.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ParseError {}
|
|
@ -24,6 +24,7 @@ mod ffi;
|
||||||
|
|
||||||
mod caption_frame;
|
mod caption_frame;
|
||||||
mod ccdetect;
|
mod ccdetect;
|
||||||
|
mod ccutils;
|
||||||
mod cea608overlay;
|
mod cea608overlay;
|
||||||
mod cea608tott;
|
mod cea608tott;
|
||||||
mod line_reader;
|
mod line_reader;
|
||||||
|
|
Loading…
Reference in a new issue