gst-plugins-rs/video/cdg/src/cdgparse/imp.rs

229 lines
7 KiB
Rust
Raw Normal View History

2019-05-26 14:12:16 +00:00
// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
2022-03-14 08:22:53 +00:00
// SPDX-License-Identifier: MIT OR Apache-2.0
2019-05-26 14:12:16 +00:00
2020-11-22 17:21:45 +00:00
use gst::format::Bytes;
2021-06-03 18:20:54 +00:00
use gst::glib;
2019-05-26 14:12:16 +00:00
use gst::subclass::prelude::*;
use gst_base::prelude::*;
use gst_base::subclass::prelude::*;
2020-11-22 17:21:45 +00:00
use once_cell::sync::Lazy;
2019-05-26 14:12:16 +00:00
2019-06-05 05:19:27 +00:00
use crate::constants::{
CDG_COMMAND, CDG_HEIGHT, CDG_MASK, CDG_PACKET_PERIOD, CDG_PACKET_SIZE, CDG_WIDTH,
};
2019-05-26 14:12:16 +00:00
const CDG_CMD_MEMORY_PRESET: u8 = 1;
const CDG_CMD_MEMORY_LOAD_COLOR_TABLE_1: u8 = 30;
const CDG_CMD_MEMORY_LOAD_COLOR_TABLE_2: u8 = 31;
2019-05-26 14:12:16 +00:00
#[derive(Default)]
pub struct CdgParse;
2020-11-22 17:21:45 +00:00
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"cdgparse",
gst::DebugColorFlags::empty(),
Some("CDG parser"),
2020-11-22 17:21:45 +00:00
)
});
2019-05-26 14:12:16 +00:00
#[glib::object_subclass]
2019-05-26 14:12:16 +00:00
impl ObjectSubclass for CdgParse {
const NAME: &'static str = "GstCdgParse";
type Type = super::CdgParse;
2019-05-26 14:12:16 +00:00
type ParentType = gst_base::BaseParse;
}
impl ObjectImpl for CdgParse {}
2019-05-26 14:12:16 +00:00
2021-10-23 08:57:31 +00:00
impl GstObjectImpl for CdgParse {}
impl ElementImpl for CdgParse {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"CDG parser",
"Codec/Parser/Video",
"CDG parser",
"Guillaume Desmottes <guillaume.desmottes@collabora.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let sink_caps = gst::Caps::builder("video/x-cdg").build();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&sink_caps,
)
.unwrap();
let src_caps = gst::Caps::builder("video/x-cdg")
.field("width", CDG_WIDTH as i32)
.field("height", CDG_HEIGHT as i32)
.field("framerate", gst::Fraction::new(0, 1))
.field("parsed", true)
.build();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&src_caps,
)
.unwrap();
vec![src_pad_template, sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
}
2019-05-26 14:12:16 +00:00
fn bytes_to_time(bytes: Bytes) -> gst::ClockTime {
let nb = bytes / CDG_PACKET_SIZE as u64;
nb.mul_div_round(*gst::ClockTime::SECOND, CDG_PACKET_PERIOD)
.unwrap()
.nseconds()
2019-05-26 14:12:16 +00:00
}
fn time_to_bytes(time: gst::ClockTime) -> Bytes {
time.nseconds()
.mul_div_round(
CDG_PACKET_PERIOD * CDG_PACKET_SIZE as u64,
*gst::ClockTime::SECOND,
)
.unwrap()
.bytes()
2019-05-26 14:12:16 +00:00
}
impl BaseParseImpl for CdgParse {
fn start(&self) -> Result<(), gst::ErrorMessage> {
self.instance().set_min_frame_size(CDG_PACKET_SIZE as u32);
2019-05-26 14:12:16 +00:00
/* Set duration */
let mut query = gst::query::Duration::new(gst::Format::Bytes);
if self.instance().src_pad().query(&mut query) {
2021-04-12 12:49:54 +00:00
let size = query.result();
2021-06-04 17:06:24 +00:00
let bytes: Option<Bytes> = size.try_into().unwrap();
self.instance().set_duration(bytes.map(bytes_to_time), 0);
2019-05-26 14:12:16 +00:00
}
Ok(())
}
fn handle_frame(
&self,
mut frame: gst_base::BaseParseFrame,
2019-05-26 14:12:16 +00:00
) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
if self.instance().src_pad().current_caps().is_none() {
2019-05-26 14:12:16 +00:00
// Set src pad caps
let src_caps = gst::Caps::builder("video/x-cdg")
.field("width", CDG_WIDTH as i32)
.field("height", CDG_HEIGHT as i32)
.field("framerate", gst::Fraction::new(0, 1))
.field("parsed", true)
.build();
2019-05-26 14:12:16 +00:00
self.instance()
.src_pad()
.push_event(gst::event::Caps::new(&src_caps));
2019-05-26 14:12:16 +00:00
}
// Scan for CDG instruction
2021-04-12 12:49:54 +00:00
let input = frame.buffer().unwrap();
2019-05-26 14:12:16 +00:00
let skip = {
2019-12-18 05:50:10 +00:00
let map = input.map_readable().map_err(|_| {
gst::element_imp_error!(
self,
2019-05-26 14:12:16 +00:00
gst::CoreError::Failed,
["Failed to map input buffer readable"]
);
gst::FlowError::Error
})?;
let data = map.as_slice();
data.iter()
.enumerate()
.find(|(_, byte)| (*byte & CDG_MASK == CDG_COMMAND))
.map(|(i, _)| i)
2021-04-12 12:49:54 +00:00
.unwrap_or_else(|| input.size()) // skip the whole buffer
2019-05-26 14:12:16 +00:00
as u32
};
if skip != 0 {
// Skip to the start of the CDG packet
return Ok((gst::FlowSuccess::Ok, skip));
}
let (keyframe, header) = {
2019-12-18 05:50:10 +00:00
let map = input.map_readable().map_err(|_| {
gst::element_imp_error!(
self,
2019-05-26 14:12:16 +00:00
gst::CoreError::Failed,
["Failed to map input buffer readable"]
);
gst::FlowError::Error
})?;
let data = map.as_slice();
match data[1] & CDG_MASK {
// consider memory preset as keyframe as it clears the screen
CDG_CMD_MEMORY_PRESET => (true, false),
// mark palette commands as headers
CDG_CMD_MEMORY_LOAD_COLOR_TABLE_1 | CDG_CMD_MEMORY_LOAD_COLOR_TABLE_2 => {
(false, true)
}
_ => (false, false),
}
2019-05-26 14:12:16 +00:00
};
let pts = bytes_to_time(frame.offset().bytes());
2021-04-12 12:49:54 +00:00
let buffer = frame.buffer_mut().unwrap();
2019-05-26 14:12:16 +00:00
buffer.set_pts(pts);
if !keyframe {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
if header {
buffer.set_flags(gst::BufferFlags::HEADER);
}
2019-05-26 14:12:16 +00:00
gst::debug!(CAT, imp: self, "Found frame pts={}", pts);
2019-05-26 14:12:16 +00:00
self.instance()
.finish_frame(frame, CDG_PACKET_SIZE as u32)?;
2019-05-26 14:12:16 +00:00
Ok((gst::FlowSuccess::Ok, skip))
}
fn convert(
2019-05-26 14:12:16 +00:00
&self,
src_val: impl FormattedValue,
2019-05-26 14:12:16 +00:00
dest_format: gst::Format,
) -> Option<gst::GenericFormattedValue> {
let src_val = src_val.into();
match (src_val, dest_format) {
(gst::GenericFormattedValue::Bytes(bytes), gst::Format::Time) => {
2021-06-04 17:06:24 +00:00
Some(bytes.map(bytes_to_time).into())
}
2019-05-26 14:12:16 +00:00
(gst::GenericFormattedValue::Time(time), gst::Format::Bytes) => {
2021-06-04 17:06:24 +00:00
Some(time.map(time_to_bytes).into())
2019-05-26 14:12:16 +00:00
}
_ => None,
}
}
}