mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-06-02 21:39:23 +00:00
video: Update for subclassing API changes
This commit is contained in:
parent
b021a8bf10
commit
717477fd36
|
@ -17,18 +17,19 @@ use std::sync::Mutex;
|
||||||
|
|
||||||
use crate::constants::{CDG_HEIGHT, CDG_WIDTH};
|
use crate::constants::{CDG_HEIGHT, CDG_WIDTH};
|
||||||
|
|
||||||
struct CdgDec {
|
|
||||||
cdg_inter: Mutex<Box<cdg_renderer::CdgInterpreter>>,
|
|
||||||
output_info: Mutex<Option<gst_video::VideoInfo>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CAT: gst::DebugCategory =
|
static ref CAT: gst::DebugCategory =
|
||||||
gst::DebugCategory::new("cdgdec", gst::DebugColorFlags::empty(), Some("CDG decoder"),);
|
gst::DebugCategory::new("cdgdec", gst::DebugColorFlags::empty(), Some("CDG decoder"),);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CdgDec {
|
||||||
|
cdg_inter: Mutex<Box<cdg_renderer::CdgInterpreter>>,
|
||||||
|
output_info: Mutex<Option<gst_video::VideoInfo>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ObjectSubclass for CdgDec {
|
impl ObjectSubclass for CdgDec {
|
||||||
const NAME: &'static str = "CdgDec";
|
const NAME: &'static str = "CdgDec";
|
||||||
|
type Type = super::CdgDec;
|
||||||
type ParentType = gst_video::VideoDecoder;
|
type ParentType = gst_video::VideoDecoder;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
@ -42,7 +43,7 @@ impl ObjectSubclass for CdgDec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"CDG decoder",
|
"CDG decoder",
|
||||||
"Decoder/Video",
|
"Decoder/Video",
|
||||||
|
@ -85,14 +86,14 @@ impl ObjectImpl for CdgDec {}
|
||||||
impl ElementImpl for CdgDec {}
|
impl ElementImpl for CdgDec {}
|
||||||
|
|
||||||
impl VideoDecoderImpl for CdgDec {
|
impl VideoDecoderImpl for CdgDec {
|
||||||
fn start(&self, element: &gst_video::VideoDecoder) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
let mut out_info = self.output_info.lock().unwrap();
|
let mut out_info = self.output_info.lock().unwrap();
|
||||||
*out_info = None;
|
*out_info = None;
|
||||||
|
|
||||||
self.parent_start(element)
|
self.parent_start(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self, element: &gst_video::VideoDecoder) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
{
|
{
|
||||||
let mut cdg_inter = self.cdg_inter.lock().unwrap();
|
let mut cdg_inter = self.cdg_inter.lock().unwrap();
|
||||||
cdg_inter.reset(true);
|
cdg_inter.reset(true);
|
||||||
|
@ -102,7 +103,7 @@ impl VideoDecoderImpl for CdgDec {
|
||||||
|
|
||||||
fn handle_frame(
|
fn handle_frame(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &Self::Type,
|
||||||
mut frame: gst_video::VideoCodecFrame,
|
mut frame: gst_video::VideoCodecFrame,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
{
|
{
|
||||||
|
@ -192,7 +193,7 @@ impl VideoDecoderImpl for CdgDec {
|
||||||
|
|
||||||
fn decide_allocation(
|
fn decide_allocation(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &Self::Type,
|
||||||
query: &mut gst::QueryRef,
|
query: &mut gst::QueryRef,
|
||||||
) -> Result<(), gst::ErrorMessage> {
|
) -> Result<(), gst::ErrorMessage> {
|
||||||
if let gst::query::QueryView::Allocation(allocation) = query.view() {
|
if let gst::query::QueryView::Allocation(allocation) = query.view() {
|
||||||
|
@ -216,7 +217,7 @@ impl VideoDecoderImpl for CdgDec {
|
||||||
self.parent_decide_allocation(element, query)
|
self.parent_decide_allocation(element, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&self, element: &gst_video::VideoDecoder) -> bool {
|
fn flush(&self, element: &Self::Type) -> bool {
|
||||||
gst_debug!(CAT, obj: element, "flushing, reset CDG interpreter");
|
gst_debug!(CAT, obj: element, "flushing, reset CDG interpreter");
|
||||||
|
|
||||||
let mut cdg_inter = self.cdg_inter.lock().unwrap();
|
let mut cdg_inter = self.cdg_inter.lock().unwrap();
|
||||||
|
@ -224,12 +225,3 @@ impl VideoDecoderImpl for CdgDec {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"cdgdec",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
CdgDec::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
29
video/cdg/src/cdgdec/mod.rs
Normal file
29
video/cdg/src/cdgdec/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct CdgDec(ObjectSubclass<imp::CdgDec>) @extends gst_video::VideoDecoder, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for CdgDec {}
|
||||||
|
unsafe impl Sync for CdgDec {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"cdgdec",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
CdgDec::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ const CDG_CMD_MEMORY_PRESET: u8 = 1;
|
||||||
const CDG_CMD_MEMORY_LOAD_COLOR_TABLE_1: u8 = 30;
|
const CDG_CMD_MEMORY_LOAD_COLOR_TABLE_1: u8 = 30;
|
||||||
const CDG_CMD_MEMORY_LOAD_COLOR_TABLE_2: u8 = 31;
|
const CDG_CMD_MEMORY_LOAD_COLOR_TABLE_2: u8 = 31;
|
||||||
|
|
||||||
struct CdgParse;
|
pub struct CdgParse;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CAT: gst::DebugCategory = gst::DebugCategory::new(
|
static ref CAT: gst::DebugCategory = gst::DebugCategory::new(
|
||||||
|
@ -35,6 +35,7 @@ lazy_static! {
|
||||||
|
|
||||||
impl ObjectSubclass for CdgParse {
|
impl ObjectSubclass for CdgParse {
|
||||||
const NAME: &'static str = "CdgParse";
|
const NAME: &'static str = "CdgParse";
|
||||||
|
type Type = super::CdgParse;
|
||||||
type ParentType = gst_base::BaseParse;
|
type ParentType = gst_base::BaseParse;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
@ -45,7 +46,7 @@ impl ObjectSubclass for CdgParse {
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"CDG parser",
|
"CDG parser",
|
||||||
"Codec/Parser/Video",
|
"Codec/Parser/Video",
|
||||||
|
@ -108,7 +109,7 @@ fn time_to_bytes(time: gst::ClockTime) -> Bytes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseParseImpl for CdgParse {
|
impl BaseParseImpl for CdgParse {
|
||||||
fn start(&self, element: &gst_base::BaseParse) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
element.set_min_frame_size(CDG_PACKET_SIZE as u32);
|
element.set_min_frame_size(CDG_PACKET_SIZE as u32);
|
||||||
|
|
||||||
/* Set duration */
|
/* Set duration */
|
||||||
|
@ -125,7 +126,7 @@ impl BaseParseImpl for CdgParse {
|
||||||
|
|
||||||
fn handle_frame(
|
fn handle_frame(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_base::BaseParse,
|
element: &Self::Type,
|
||||||
mut frame: gst_base::BaseParseFrame,
|
mut frame: gst_base::BaseParseFrame,
|
||||||
) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
|
) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
|
||||||
let pad = element.get_src_pad();
|
let pad = element.get_src_pad();
|
||||||
|
@ -212,7 +213,7 @@ impl BaseParseImpl for CdgParse {
|
||||||
|
|
||||||
fn convert<V: Into<gst::GenericFormattedValue>>(
|
fn convert<V: Into<gst::GenericFormattedValue>>(
|
||||||
&self,
|
&self,
|
||||||
_element: &gst_base::BaseParse,
|
_element: &Self::Type,
|
||||||
src_val: V,
|
src_val: V,
|
||||||
dest_format: gst::Format,
|
dest_format: gst::Format,
|
||||||
) -> Option<gst::GenericFormattedValue> {
|
) -> Option<gst::GenericFormattedValue> {
|
||||||
|
@ -229,12 +230,3 @@ impl BaseParseImpl for CdgParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"cdgparse",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
CdgParse::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
29
video/cdg/src/cdgparse/mod.rs
Normal file
29
video/cdg/src/cdgparse/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct CdgParse(ObjectSubclass<imp::CdgParse>) @extends gst_base::BaseParse, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for CdgParse {}
|
||||||
|
unsafe impl Sync for CdgParse {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"cdgparse",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
CdgParse::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -18,91 +18,12 @@ extern crate lazy_static;
|
||||||
mod cdgdec;
|
mod cdgdec;
|
||||||
mod cdgparse;
|
mod cdgparse;
|
||||||
mod constants;
|
mod constants;
|
||||||
|
mod typefind;
|
||||||
use constants::{CDG_COMMAND, CDG_MASK, CDG_PACKET_PERIOD, CDG_PACKET_SIZE};
|
|
||||||
use gst::{Caps, TypeFind, TypeFindProbability};
|
|
||||||
use std::cmp;
|
|
||||||
|
|
||||||
const NB_WINDOWS: u64 = 8;
|
|
||||||
const TYPEFIND_SEARCH_WINDOW_SEC: i64 = 4;
|
|
||||||
const TYPEFIND_SEARCH_WINDOW: i64 =
|
|
||||||
TYPEFIND_SEARCH_WINDOW_SEC * (CDG_PACKET_SIZE as i64 * CDG_PACKET_PERIOD as i64); /* in bytes */
|
|
||||||
|
|
||||||
/* Return the percentage of CDG packets in the first @len bytes of @typefind */
|
|
||||||
fn cdg_packets_ratio(typefind: &mut TypeFind, start: i64, len: i64) -> i64 {
|
|
||||||
let mut count = 0;
|
|
||||||
let total = len / CDG_PACKET_SIZE as i64;
|
|
||||||
|
|
||||||
for offset in (0..len).step_by(CDG_PACKET_SIZE as usize) {
|
|
||||||
match typefind.peek(start + offset, CDG_PACKET_SIZE as u32) {
|
|
||||||
Some(data) => {
|
|
||||||
if data[0] & CDG_MASK == CDG_COMMAND {
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(count * 100) / total
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some CDG files starts drawing right away and then pause for a while
|
|
||||||
* (typically because of the song intro) while other wait for a few
|
|
||||||
* seconds before starting to draw.
|
|
||||||
* In order to support all variants, scan through all the file per block
|
|
||||||
* of size TYPEFIND_SEARCH_WINDOW and keep the highest ratio of CDG packets
|
|
||||||
* detected. */
|
|
||||||
fn compute_probability(typefind: &mut TypeFind) -> TypeFindProbability {
|
|
||||||
let mut best = TypeFindProbability::None;
|
|
||||||
// Try looking at the start of the file if its length isn't available
|
|
||||||
let len = typefind
|
|
||||||
.get_length()
|
|
||||||
.unwrap_or(TYPEFIND_SEARCH_WINDOW as u64 * NB_WINDOWS);
|
|
||||||
let step = len / NB_WINDOWS;
|
|
||||||
|
|
||||||
// Too short file
|
|
||||||
if step == 0 {
|
|
||||||
return TypeFindProbability::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
for offset in (0..len).step_by(step as usize) {
|
|
||||||
let proba = match cdg_packets_ratio(typefind, offset as i64, TYPEFIND_SEARCH_WINDOW) {
|
|
||||||
0..=5 => TypeFindProbability::None,
|
|
||||||
6..=10 => TypeFindProbability::Possible,
|
|
||||||
_ => TypeFindProbability::Likely,
|
|
||||||
};
|
|
||||||
|
|
||||||
if proba == TypeFindProbability::Likely {
|
|
||||||
return proba;
|
|
||||||
}
|
|
||||||
|
|
||||||
best = cmp::max(best, proba);
|
|
||||||
}
|
|
||||||
|
|
||||||
best
|
|
||||||
}
|
|
||||||
|
|
||||||
fn typefind_register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
TypeFind::register(
|
|
||||||
Some(plugin),
|
|
||||||
"cdg_typefind",
|
|
||||||
gst::Rank::None,
|
|
||||||
Some("cdg"),
|
|
||||||
Some(&Caps::new_simple("video/x-cdg", &[])),
|
|
||||||
|mut typefind| {
|
|
||||||
let proba = compute_probability(&mut typefind);
|
|
||||||
|
|
||||||
if proba != gst::TypeFindProbability::None {
|
|
||||||
typefind.suggest(proba, &Caps::new_simple("video/x-cdg", &[]));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
cdgdec::register(plugin)?;
|
cdgdec::register(plugin)?;
|
||||||
cdgparse::register(plugin)?;
|
cdgparse::register(plugin)?;
|
||||||
typefind_register(plugin)?;
|
typefind::register(plugin)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
87
video/cdg/src/typefind.rs
Normal file
87
video/cdg/src/typefind.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use crate::constants::{CDG_COMMAND, CDG_MASK, CDG_PACKET_PERIOD, CDG_PACKET_SIZE};
|
||||||
|
use gst::{Caps, TypeFind, TypeFindProbability};
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
const NB_WINDOWS: u64 = 8;
|
||||||
|
const TYPEFIND_SEARCH_WINDOW_SEC: i64 = 4;
|
||||||
|
const TYPEFIND_SEARCH_WINDOW: i64 =
|
||||||
|
TYPEFIND_SEARCH_WINDOW_SEC * (CDG_PACKET_SIZE as i64 * CDG_PACKET_PERIOD as i64); /* in bytes */
|
||||||
|
|
||||||
|
/* Return the percentage of CDG packets in the first @len bytes of @typefind */
|
||||||
|
fn cdg_packets_ratio(typefind: &mut TypeFind, start: i64, len: i64) -> i64 {
|
||||||
|
let mut count = 0;
|
||||||
|
let total = len / CDG_PACKET_SIZE as i64;
|
||||||
|
|
||||||
|
for offset in (0..len).step_by(CDG_PACKET_SIZE as usize) {
|
||||||
|
match typefind.peek(start + offset, CDG_PACKET_SIZE as u32) {
|
||||||
|
Some(data) => {
|
||||||
|
if data[0] & CDG_MASK == CDG_COMMAND {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(count * 100) / total
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Some CDG files starts drawing right away and then pause for a while
|
||||||
|
* (typically because of the song intro) while other wait for a few
|
||||||
|
* seconds before starting to draw.
|
||||||
|
* In order to support all variants, scan through all the file per block
|
||||||
|
* of size TYPEFIND_SEARCH_WINDOW and keep the highest ratio of CDG packets
|
||||||
|
* detected. */
|
||||||
|
fn compute_probability(typefind: &mut TypeFind) -> TypeFindProbability {
|
||||||
|
let mut best = TypeFindProbability::None;
|
||||||
|
// Try looking at the start of the file if its length isn't available
|
||||||
|
let len = typefind
|
||||||
|
.get_length()
|
||||||
|
.unwrap_or(TYPEFIND_SEARCH_WINDOW as u64 * NB_WINDOWS);
|
||||||
|
let step = len / NB_WINDOWS;
|
||||||
|
|
||||||
|
// Too short file
|
||||||
|
if step == 0 {
|
||||||
|
return TypeFindProbability::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
for offset in (0..len).step_by(step as usize) {
|
||||||
|
let proba = match cdg_packets_ratio(typefind, offset as i64, TYPEFIND_SEARCH_WINDOW) {
|
||||||
|
0..=5 => TypeFindProbability::None,
|
||||||
|
6..=10 => TypeFindProbability::Possible,
|
||||||
|
_ => TypeFindProbability::Likely,
|
||||||
|
};
|
||||||
|
|
||||||
|
if proba == TypeFindProbability::Likely {
|
||||||
|
return proba;
|
||||||
|
}
|
||||||
|
|
||||||
|
best = cmp::max(best, proba);
|
||||||
|
}
|
||||||
|
|
||||||
|
best
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
TypeFind::register(
|
||||||
|
Some(plugin),
|
||||||
|
"cdg_typefind",
|
||||||
|
gst::Rank::None,
|
||||||
|
Some("cdg"),
|
||||||
|
Some(&Caps::new_simple("video/x-cdg", &[])),
|
||||||
|
|mut typefind| {
|
||||||
|
let proba = compute_probability(&mut typefind);
|
||||||
|
|
||||||
|
if proba != gst::TypeFindProbability::None {
|
||||||
|
typefind.suggest(proba, &Caps::new_simple("video/x-cdg", &[]));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use super::cea608tott_ffi as ffi;
|
use super::ffi;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
|
|
@ -69,7 +69,7 @@ struct State {
|
||||||
last_cc708_change: gst::ClockTime,
|
last_cc708_change: gst::ClockTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CCDetect {
|
pub struct CCDetect {
|
||||||
settings: Mutex<Settings>,
|
settings: Mutex<Settings>,
|
||||||
state: Mutex<Option<State>>,
|
state: Mutex<Option<State>>,
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ impl CCDetect {
|
||||||
|
|
||||||
fn maybe_update_properties(
|
fn maybe_update_properties(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_base::BaseTransform,
|
element: &super::CCDetect,
|
||||||
ts: gst::ClockTime,
|
ts: gst::ClockTime,
|
||||||
cc_packet: CCPacketContents,
|
cc_packet: CCPacketContents,
|
||||||
) -> Result<(), gst::FlowError> {
|
) -> Result<(), gst::FlowError> {
|
||||||
|
@ -382,6 +382,7 @@ impl CCDetect {
|
||||||
|
|
||||||
impl ObjectSubclass for CCDetect {
|
impl ObjectSubclass for CCDetect {
|
||||||
const NAME: &'static str = "CCDetect";
|
const NAME: &'static str = "CCDetect";
|
||||||
|
type Type = super::CCDetect;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
@ -395,7 +396,7 @@ impl ObjectSubclass for CCDetect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"Closed Caption Detect",
|
"Closed Caption Detect",
|
||||||
"Filter/Video/ClosedCaption/Detect",
|
"Filter/Video/ClosedCaption/Detect",
|
||||||
|
@ -441,7 +442,7 @@ impl ObjectSubclass for CCDetect {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for CCDetect {
|
impl ObjectImpl for CCDetect {
|
||||||
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
|
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -453,7 +454,7 @@ impl ObjectImpl for CCDetect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -479,7 +480,7 @@ impl ElementImpl for CCDetect {}
|
||||||
impl BaseTransformImpl for CCDetect {
|
impl BaseTransformImpl for CCDetect {
|
||||||
fn transform_ip_passthrough(
|
fn transform_ip_passthrough(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_base::BaseTransform,
|
element: &Self::Type,
|
||||||
buf: &gst::Buffer,
|
buf: &gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let map = buf.map_readable().map_err(|_| gst::FlowError::Error)?;
|
let map = buf.map_readable().map_err(|_| gst::FlowError::Error)?;
|
||||||
|
@ -517,7 +518,7 @@ impl BaseTransformImpl for CCDetect {
|
||||||
Ok(gst::FlowSuccess::Ok)
|
Ok(gst::FlowSuccess::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, element: &gst_base::BaseTransform, event: gst::Event) -> bool {
|
fn sink_event(&self, element: &Self::Type, event: gst::Event) -> bool {
|
||||||
match event.view() {
|
match event.view() {
|
||||||
gst::event::EventView::Gap(gap) => {
|
gst::event::EventView::Gap(gap) => {
|
||||||
let _ = self.maybe_update_properties(
|
let _ = self.maybe_update_properties(
|
||||||
|
@ -536,7 +537,7 @@ impl BaseTransformImpl for CCDetect {
|
||||||
|
|
||||||
fn set_caps(
|
fn set_caps(
|
||||||
&self,
|
&self,
|
||||||
_element: &gst_base::BaseTransform,
|
_element: &Self::Type,
|
||||||
incaps: &gst::Caps,
|
incaps: &gst::Caps,
|
||||||
outcaps: &gst::Caps,
|
outcaps: &gst::Caps,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
|
@ -569,19 +570,10 @@ impl BaseTransformImpl for CCDetect {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self, _element: &gst_base::BaseTransform) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
// Drop state
|
// Drop state
|
||||||
let _ = self.state.lock().unwrap().take();
|
let _ = self.state.lock().unwrap().take();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"ccdetect",
|
|
||||||
gst::Rank::None,
|
|
||||||
CCDetect::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
38
video/closedcaption/src/ccdetect/mod.rs
Normal file
38
video/closedcaption/src/ccdetect/mod.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// 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 glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct CCDetect(ObjectSubclass<imp::CCDetect>) @extends gst_base::BaseTransform, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for CCDetect {}
|
||||||
|
unsafe impl Sync for CCDetect {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"ccdetect",
|
||||||
|
gst::Rank::None,
|
||||||
|
CCDetect::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -15,13 +15,6 @@
|
||||||
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||||
// Boston, MA 02110-1335, USA.
|
// Boston, MA 02110-1335, USA.
|
||||||
|
|
||||||
// Example command-line:
|
|
||||||
//
|
|
||||||
// gst-launch-1.0 cccombiner name=ccc ! cea608overlay ! autovideosink \
|
|
||||||
// videotestsrc ! video/x-raw, width=1280, height=720 ! queue ! ccc.sink \
|
|
||||||
// filesrc location=input.srt ! subparse ! tttocea608 ! queue ! ccc.caption
|
|
||||||
|
|
||||||
use glib::prelude::*;
|
|
||||||
use glib::subclass;
|
use glib::subclass;
|
||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
@ -68,7 +61,7 @@ impl Default for State {
|
||||||
|
|
||||||
unsafe impl Send for State {}
|
unsafe impl Send for State {}
|
||||||
|
|
||||||
struct Cea608Overlay {
|
pub struct Cea608Overlay {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
state: Mutex<State>,
|
state: Mutex<State>,
|
||||||
|
@ -86,7 +79,7 @@ impl Cea608Overlay {
|
||||||
// TODO: switch to the API presented in this post once it's been exposed
|
// TODO: switch to the API presented in this post once it's been exposed
|
||||||
fn recalculate_layout(
|
fn recalculate_layout(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::Cea608Overlay,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let video_info = state.video_info.as_ref().unwrap();
|
let video_info = state.video_info.as_ref().unwrap();
|
||||||
|
@ -242,7 +235,7 @@ impl Cea608Overlay {
|
||||||
|
|
||||||
fn negotiate(
|
fn negotiate(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::Cea608Overlay,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let video_info = match state.video_info.as_ref() {
|
let video_info = match state.video_info.as_ref() {
|
||||||
|
@ -294,7 +287,7 @@ impl Cea608Overlay {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::Cea608Overlay,
|
||||||
mut buffer: gst::Buffer,
|
mut buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
@ -358,7 +351,12 @@ impl Cea608Overlay {
|
||||||
self.srcpad.push(buffer)
|
self.srcpad.push(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(
|
||||||
|
&self,
|
||||||
|
pad: &gst::Pad,
|
||||||
|
element: &super::Cea608Overlay,
|
||||||
|
event: gst::Event,
|
||||||
|
) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -388,13 +386,14 @@ impl Cea608Overlay {
|
||||||
|
|
||||||
impl ObjectSubclass for Cea608Overlay {
|
impl ObjectSubclass for Cea608Overlay {
|
||||||
const NAME: &'static str = "RsCea608Overlay";
|
const NAME: &'static str = "RsCea608Overlay";
|
||||||
|
type Type = super::Cea608Overlay;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.chain_function(|pad, parent, buffer| {
|
.chain_function(|pad, parent, buffer| {
|
||||||
|
@ -426,7 +425,7 @@ impl ObjectSubclass for Cea608Overlay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"Cea 608 overlay",
|
"Cea 608 overlay",
|
||||||
"Video/Overlay/Subtitle",
|
"Video/Overlay/Subtitle",
|
||||||
|
@ -460,19 +459,18 @@ impl ObjectSubclass for Cea608Overlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Cea608Overlay {
|
impl ObjectImpl for Cea608Overlay {
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
element.add_pad(&self.srcpad).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Cea608Overlay {
|
impl ElementImpl for Cea608Overlay {
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &Self::Type,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||||
|
@ -489,12 +487,3 @@ impl ElementImpl for Cea608Overlay {
|
||||||
self.parent_change_state(element, transition)
|
self.parent_change_state(element, transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"cea608overlay",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
Cea608Overlay::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
44
video/closedcaption/src/cea608overlay/mod.rs
Normal file
44
video/closedcaption/src/cea608overlay/mod.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (C) 2020 Mathieu Duponchelle <mathieu@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.
|
||||||
|
|
||||||
|
// Example command-line:
|
||||||
|
//
|
||||||
|
// gst-launch-1.0 cccombiner name=ccc ! cea608overlay ! autovideosink \
|
||||||
|
// videotestsrc ! video/x-raw, width=1280, height=720 ! queue ! ccc.sink \
|
||||||
|
// filesrc location=input.srt ! subparse ! tttocea608 ! queue ! ccc.caption
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct Cea608Overlay(ObjectSubclass<imp::Cea608Overlay>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for Cea608Overlay {}
|
||||||
|
unsafe impl Sync for Cea608Overlay {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"cea608overlay",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
Cea608Overlay::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,7 +6,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use glib::prelude::*;
|
|
||||||
use glib::subclass;
|
use glib::subclass;
|
||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
@ -42,7 +41,7 @@ impl Default for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Cea608ToTt {
|
pub struct Cea608ToTt {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ impl Cea608ToTt {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
_element: &gst::Element,
|
_element: &super::Cea608ToTt,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
@ -271,7 +270,7 @@ impl Cea608ToTt {
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::Cea608ToTt, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -371,13 +370,14 @@ impl Cea608ToTt {
|
||||||
|
|
||||||
impl ObjectSubclass for Cea608ToTt {
|
impl ObjectSubclass for Cea608ToTt {
|
||||||
const NAME: &'static str = "Cea608ToTt";
|
const NAME: &'static str = "Cea608ToTt";
|
||||||
|
type Type = super::Cea608ToTt;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.chain_function(|pad, parent, buffer| {
|
.chain_function(|pad, parent, buffer| {
|
||||||
|
@ -409,7 +409,7 @@ impl ObjectSubclass for Cea608ToTt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"CEA-608 to TT",
|
"CEA-608 to TT",
|
||||||
"Generic",
|
"Generic",
|
||||||
|
@ -461,19 +461,18 @@ impl ObjectSubclass for Cea608ToTt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Cea608ToTt {
|
impl ObjectImpl for Cea608ToTt {
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
element.add_pad(&self.srcpad).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Cea608ToTt {
|
impl ElementImpl for Cea608ToTt {
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &Self::Type,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||||
|
@ -499,12 +498,3 @@ impl ElementImpl for Cea608ToTt {
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"cea608tott",
|
|
||||||
gst::Rank::None,
|
|
||||||
Cea608ToTt::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
29
video/closedcaption/src/cea608tott/mod.rs
Normal file
29
video/closedcaption/src/cea608tott/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct Cea608ToTt(ObjectSubclass<imp::Cea608ToTt>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for Cea608ToTt {}
|
||||||
|
unsafe impl Sync for Cea608ToTt {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"cea608tott",
|
||||||
|
gst::Rank::None,
|
||||||
|
Cea608ToTt::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -17,11 +17,6 @@
|
||||||
|
|
||||||
#![recursion_limit = "128"]
|
#![recursion_limit = "128"]
|
||||||
|
|
||||||
// These macros are in weird paths currently,
|
|
||||||
// and extern crate is used to avoid the explicit imports
|
|
||||||
// should not be needed ideally in the upcoming releases.
|
|
||||||
// https://github.com/gtk-rs/glib/issues/420
|
|
||||||
// https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/170
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate glib;
|
extern crate glib;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -33,21 +28,20 @@ extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate pretty_assertions;
|
extern crate pretty_assertions;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types, non_upper_case_globals, unused)]
|
||||||
|
#[allow(clippy::redundant_static_lifetimes, clippy::unreadable_literal)]
|
||||||
|
#[allow(clippy::useless_transmute, clippy::trivially_copy_pass_by_ref)]
|
||||||
|
mod ffi;
|
||||||
|
|
||||||
mod caption_frame;
|
mod caption_frame;
|
||||||
mod ccdetect;
|
mod ccdetect;
|
||||||
mod cea608overlay;
|
mod cea608overlay;
|
||||||
mod cea608tott;
|
mod cea608tott;
|
||||||
#[allow(non_camel_case_types, non_upper_case_globals)]
|
|
||||||
#[allow(clippy::redundant_static_lifetimes, clippy::unreadable_literal)]
|
|
||||||
#[allow(clippy::useless_transmute, clippy::trivially_copy_pass_by_ref)]
|
|
||||||
pub mod cea608tott_ffi;
|
|
||||||
mod line_reader;
|
mod line_reader;
|
||||||
mod mcc_enc;
|
mod mcc_enc;
|
||||||
mod mcc_parse;
|
mod mcc_parse;
|
||||||
mod mcc_parser;
|
|
||||||
mod scc_enc;
|
mod scc_enc;
|
||||||
mod scc_parse;
|
mod scc_parse;
|
||||||
mod scc_parser;
|
|
||||||
mod tttocea608;
|
mod tttocea608;
|
||||||
|
|
||||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
|
|
@ -28,9 +28,7 @@ use uuid::Uuid;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
#[path = "mcc_enc_headers.rs"]
|
use super::headers::*;
|
||||||
mod mcc_enc_headers;
|
|
||||||
use self::mcc_enc_headers::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum Format {
|
enum Format {
|
||||||
|
@ -89,7 +87,7 @@ static PROPERTIES: [subclass::Property; 2] = [
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
struct MccEnc {
|
pub struct MccEnc {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
state: Mutex<State>,
|
state: Mutex<State>,
|
||||||
|
@ -291,7 +289,7 @@ impl MccEnc {
|
||||||
|
|
||||||
fn generate_caption(
|
fn generate_caption(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::MccEnc,
|
||||||
state: &State,
|
state: &State,
|
||||||
buffer: &gst::Buffer,
|
buffer: &gst::Buffer,
|
||||||
outbuf: &mut Vec<u8>,
|
outbuf: &mut Vec<u8>,
|
||||||
|
@ -359,7 +357,7 @@ impl MccEnc {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::MccEnc,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
@ -383,7 +381,7 @@ impl MccEnc {
|
||||||
self.srcpad.push(buf)
|
self.srcpad.push(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::MccEnc, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -426,7 +424,7 @@ impl MccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn src_event(&self, pad: &gst::Pad, element: &super::MccEnc, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -439,7 +437,12 @@ impl MccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_query(&self, pad: &gst::Pad, element: &gst::Element, query: &mut gst::QueryRef) -> bool {
|
fn src_query(
|
||||||
|
&self,
|
||||||
|
pad: &gst::Pad,
|
||||||
|
element: &super::MccEnc,
|
||||||
|
query: &mut gst::QueryRef,
|
||||||
|
) -> bool {
|
||||||
use gst::QueryView;
|
use gst::QueryView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||||
|
@ -462,13 +465,14 @@ impl MccEnc {
|
||||||
|
|
||||||
impl ObjectSubclass for MccEnc {
|
impl ObjectSubclass for MccEnc {
|
||||||
const NAME: &'static str = "RsMccEnc";
|
const NAME: &'static str = "RsMccEnc";
|
||||||
|
type Type = super::MccEnc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.chain_function(|pad, parent, buffer| {
|
.chain_function(|pad, parent, buffer| {
|
||||||
|
@ -513,7 +517,7 @@ impl ObjectSubclass for MccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"Mcc Encoder",
|
"Mcc Encoder",
|
||||||
"Encoder/ClosedCaption",
|
"Encoder/ClosedCaption",
|
||||||
|
@ -571,7 +575,7 @@ impl ObjectSubclass for MccEnc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for MccEnc {
|
impl ObjectImpl for MccEnc {
|
||||||
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
|
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -587,7 +591,7 @@ impl ObjectImpl for MccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -603,19 +607,18 @@ impl ObjectImpl for MccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
element.add_pad(&self.srcpad).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for MccEnc {
|
impl ElementImpl for MccEnc {
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &Self::Type,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||||
|
@ -632,12 +635,3 @@ impl ElementImpl for MccEnc {
|
||||||
self.parent_change_state(element, transition)
|
self.parent_change_state(element, transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"mccenc",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
MccEnc::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
39
video/closedcaption/src/mcc_enc/mod.rs
Normal file
39
video/closedcaption/src/mcc_enc/mod.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright (C) 2018 Sebastian Dröge <sebastian@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 glib::prelude::*;
|
||||||
|
|
||||||
|
mod headers;
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct MccEnc(ObjectSubclass<imp::MccEnc>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for MccEnc {}
|
||||||
|
unsafe impl Sync for MccEnc {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"mccenc",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
MccEnc::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -26,8 +26,8 @@ use std::cmp;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
|
use super::parser::{MccLine, MccParser, TimeCode};
|
||||||
use crate::line_reader::LineReader;
|
use crate::line_reader::LineReader;
|
||||||
use crate::mcc_parser::{MccLine, MccParser, TimeCode};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CAT: gst::DebugCategory = {
|
static ref CAT: gst::DebugCategory = {
|
||||||
|
@ -54,7 +54,7 @@ struct PullState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PullState {
|
impl PullState {
|
||||||
fn new(element: &gst::Element, pad: &gst::Pad) -> Self {
|
fn new(element: &super::MccParse, pad: &gst::Pad) -> Self {
|
||||||
Self {
|
Self {
|
||||||
need_stream_start: true,
|
need_stream_start: true,
|
||||||
stream_id: pad
|
stream_id: pad
|
||||||
|
@ -180,7 +180,7 @@ impl State {
|
||||||
|
|
||||||
fn handle_timecode(
|
fn handle_timecode(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
framerate: gst::Fraction,
|
framerate: gst::Fraction,
|
||||||
drop_frame: bool,
|
drop_frame: bool,
|
||||||
tc: TimeCode,
|
tc: TimeCode,
|
||||||
|
@ -219,7 +219,7 @@ impl State {
|
||||||
/// not produce timestamps jumping backwards
|
/// not produce timestamps jumping backwards
|
||||||
fn update_timestamp(
|
fn update_timestamp(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
timecode: &gst_video::ValidVideoTimeCode,
|
timecode: &gst_video::ValidVideoTimeCode,
|
||||||
) {
|
) {
|
||||||
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
|
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
|
||||||
|
@ -255,7 +255,7 @@ impl State {
|
||||||
|
|
||||||
fn add_buffer_metadata(
|
fn add_buffer_metadata(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
buffer: &mut gst::buffer::Buffer,
|
buffer: &mut gst::buffer::Buffer,
|
||||||
timecode: &gst_video::ValidVideoTimeCode,
|
timecode: &gst_video::ValidVideoTimeCode,
|
||||||
framerate: gst::Fraction,
|
framerate: gst::Fraction,
|
||||||
|
@ -281,7 +281,7 @@ impl State {
|
||||||
|
|
||||||
fn create_events(
|
fn create_events(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
format: Option<Format>,
|
format: Option<Format>,
|
||||||
framerate: gst::Fraction,
|
framerate: gst::Fraction,
|
||||||
) -> Vec<gst::Event> {
|
) -> Vec<gst::Event> {
|
||||||
|
@ -341,7 +341,7 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MccParse {
|
pub struct MccParse {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
state: Mutex<State>,
|
state: Mutex<State>,
|
||||||
|
@ -369,7 +369,7 @@ impl AsMut<[u8]> for OffsetVec {
|
||||||
impl MccParse {
|
impl MccParse {
|
||||||
fn handle_buffer(
|
fn handle_buffer(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
buffer: Option<gst::Buffer>,
|
buffer: Option<gst::Buffer>,
|
||||||
scan_tc_rate: bool,
|
scan_tc_rate: bool,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
|
@ -513,7 +513,7 @@ impl MccParse {
|
||||||
|
|
||||||
fn handle_skipped_line(
|
fn handle_skipped_line(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
tc: TimeCode,
|
tc: TimeCode,
|
||||||
mut state: MutexGuard<State>,
|
mut state: MutexGuard<State>,
|
||||||
) -> Result<MutexGuard<State>, gst::FlowError> {
|
) -> Result<MutexGuard<State>, gst::FlowError> {
|
||||||
|
@ -537,7 +537,7 @@ impl MccParse {
|
||||||
|
|
||||||
fn handle_line(
|
fn handle_line(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
tc: TimeCode,
|
tc: TimeCode,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
format: Format,
|
format: Format,
|
||||||
|
@ -587,7 +587,7 @@ impl MccParse {
|
||||||
fn sink_activate(
|
fn sink_activate(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
let mode = {
|
let mode = {
|
||||||
let mut query = gst::query::Scheduling::new();
|
let mut query = gst::query::Scheduling::new();
|
||||||
|
@ -616,7 +616,7 @@ impl MccParse {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_task(&self, element: &gst::Element) -> Result<(), gst::LoggableError> {
|
fn start_task(&self, element: &super::MccParse) -> Result<(), gst::LoggableError> {
|
||||||
let element_weak = element.downgrade();
|
let element_weak = element.downgrade();
|
||||||
let pad_weak = self.sinkpad.downgrade();
|
let pad_weak = self.sinkpad.downgrade();
|
||||||
let res = self.sinkpad.start_task(move || {
|
let res = self.sinkpad.start_task(move || {
|
||||||
|
@ -642,7 +642,7 @@ impl MccParse {
|
||||||
fn sink_activatemode(
|
fn sink_activatemode(
|
||||||
&self,
|
&self,
|
||||||
_pad: &gst::Pad,
|
_pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
mode: gst::PadMode,
|
mode: gst::PadMode,
|
||||||
active: bool,
|
active: bool,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
|
@ -659,7 +659,7 @@ impl MccParse {
|
||||||
|
|
||||||
fn scan_duration(
|
fn scan_duration(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
) -> Result<Option<ValidVideoTimeCode>, gst::LoggableError> {
|
) -> Result<Option<ValidVideoTimeCode>, gst::LoggableError> {
|
||||||
gst_debug!(CAT, obj: element, "Scanning duration");
|
gst_debug!(CAT, obj: element, "Scanning duration");
|
||||||
|
|
||||||
|
@ -748,7 +748,7 @@ impl MccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_eos(&self, element: &gst::Element) {
|
fn push_eos(&self, element: &super::MccParse) {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
if state.seeking {
|
if state.seeking {
|
||||||
|
@ -784,7 +784,7 @@ impl MccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loop_fn(&self, element: &gst::Element) {
|
fn loop_fn(&self, element: &super::MccParse) {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
let State {
|
let State {
|
||||||
timecode_rate: ref tc_rate,
|
timecode_rate: ref tc_rate,
|
||||||
|
@ -882,7 +882,7 @@ impl MccParse {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::MccParse,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
@ -910,7 +910,7 @@ impl MccParse {
|
||||||
self.state.lock().unwrap()
|
self.state.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::MccParse, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -956,7 +956,7 @@ impl MccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_seek(&self, event: &gst::event::Seek, element: &gst::Element) -> bool {
|
fn perform_seek(&self, event: &gst::event::Seek, element: &super::MccParse) -> bool {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
if state.pull.is_none() {
|
if state.pull.is_none() {
|
||||||
|
@ -1049,7 +1049,7 @@ impl MccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn src_event(&self, pad: &gst::Pad, element: &super::MccParse, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -1059,7 +1059,12 @@ impl MccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_query(&self, pad: &gst::Pad, element: &gst::Element, query: &mut gst::QueryRef) -> bool {
|
fn src_query(
|
||||||
|
&self,
|
||||||
|
pad: &gst::Pad,
|
||||||
|
element: &super::MccParse,
|
||||||
|
query: &mut gst::QueryRef,
|
||||||
|
) -> bool {
|
||||||
use gst::QueryView;
|
use gst::QueryView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||||
|
@ -1120,13 +1125,14 @@ impl MccParse {
|
||||||
|
|
||||||
impl ObjectSubclass for MccParse {
|
impl ObjectSubclass for MccParse {
|
||||||
const NAME: &'static str = "RsMccParse";
|
const NAME: &'static str = "RsMccParse";
|
||||||
|
type Type = super::MccParse;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.activate_function(|pad, parent| {
|
.activate_function(|pad, parent| {
|
||||||
|
@ -1189,7 +1195,7 @@ impl ObjectSubclass for MccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"Mcc Parse",
|
"Mcc Parse",
|
||||||
"Parser/ClosedCaption",
|
"Parser/ClosedCaption",
|
||||||
|
@ -1241,19 +1247,18 @@ impl ObjectSubclass for MccParse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for MccParse {
|
impl ObjectImpl for MccParse {
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
element.add_pad(&self.srcpad).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for MccParse {
|
impl ElementImpl for MccParse {
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &Self::Type,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||||
|
@ -1270,12 +1275,3 @@ impl ElementImpl for MccParse {
|
||||||
self.parent_change_state(element, transition)
|
self.parent_change_state(element, transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"mccparse",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
MccParse::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
39
video/closedcaption/src/mcc_parse/mod.rs
Normal file
39
video/closedcaption/src/mcc_parse/mod.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright (C) 2018 Sebastian Dröge <sebastian@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 glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
mod parser;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct MccParse(ObjectSubclass<imp::MccParse>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for MccParse {}
|
||||||
|
unsafe impl Sync for MccParse {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"mccparse",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
MccParse::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -752,7 +752,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parser() {
|
fn test_parser() {
|
||||||
let mcc_file = include_bytes!("../tests/captions-test_708.mcc");
|
let mcc_file = include_bytes!("../../tests/captions-test_708.mcc");
|
||||||
let mut reader = crate::line_reader::LineReader::new();
|
let mut reader = crate::line_reader::LineReader::new();
|
||||||
let mut parser = MccParser::new();
|
let mut parser = MccParser::new();
|
||||||
let mut line_cnt = 0;
|
let mut line_cnt = 0;
|
|
@ -16,7 +16,6 @@
|
||||||
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||||
// Boston, MA 02110-1335, USA.
|
// Boston, MA 02110-1335, USA.
|
||||||
|
|
||||||
use glib::prelude::*;
|
|
||||||
use glib::subclass;
|
use glib::subclass;
|
||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
@ -70,7 +69,7 @@ impl State {
|
||||||
|
|
||||||
fn generate_caption(
|
fn generate_caption(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::SccEnc,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<Option<gst::Buffer>, gst::FlowError> {
|
) -> Result<Option<gst::Buffer>, gst::FlowError> {
|
||||||
// Arbitrary number that was chosen to keep in order
|
// Arbitrary number that was chosen to keep in order
|
||||||
|
@ -141,7 +140,7 @@ impl State {
|
||||||
// Flush the internal buffers into a line
|
// Flush the internal buffers into a line
|
||||||
fn write_line(
|
fn write_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::SccEnc,
|
||||||
) -> Result<Option<gst::Buffer>, gst::FlowError> {
|
) -> Result<Option<gst::Buffer>, gst::FlowError> {
|
||||||
let mut outbuf = Vec::new();
|
let mut outbuf = Vec::new();
|
||||||
let mut line_start = true;
|
let mut line_start = true;
|
||||||
|
@ -218,7 +217,7 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SccEnc {
|
pub struct SccEnc {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
state: Mutex<State>,
|
state: Mutex<State>,
|
||||||
|
@ -228,7 +227,7 @@ impl SccEnc {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::SccEnc,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
@ -246,7 +245,7 @@ impl SccEnc {
|
||||||
Ok(gst::FlowSuccess::Ok)
|
Ok(gst::FlowSuccess::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::SccEnc, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -294,7 +293,7 @@ impl SccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn src_event(&self, pad: &gst::Pad, element: &super::SccEnc, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -307,7 +306,12 @@ impl SccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_query(&self, pad: &gst::Pad, element: &gst::Element, query: &mut gst::QueryRef) -> bool {
|
fn src_query(
|
||||||
|
&self,
|
||||||
|
pad: &gst::Pad,
|
||||||
|
element: &super::SccEnc,
|
||||||
|
query: &mut gst::QueryRef,
|
||||||
|
) -> bool {
|
||||||
use gst::QueryView;
|
use gst::QueryView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||||
|
@ -330,13 +334,14 @@ impl SccEnc {
|
||||||
|
|
||||||
impl ObjectSubclass for SccEnc {
|
impl ObjectSubclass for SccEnc {
|
||||||
const NAME: &'static str = "RsSccEnc";
|
const NAME: &'static str = "RsSccEnc";
|
||||||
|
type Type = super::SccEnc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.chain_function(|pad, parent, buffer| {
|
.chain_function(|pad, parent, buffer| {
|
||||||
|
@ -380,7 +385,7 @@ impl ObjectSubclass for SccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"Scc Encoder",
|
"Scc Encoder",
|
||||||
"Encoder/ClosedCaption",
|
"Encoder/ClosedCaption",
|
||||||
|
@ -416,19 +421,18 @@ impl ObjectSubclass for SccEnc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for SccEnc {
|
impl ObjectImpl for SccEnc {
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
element.add_pad(&self.srcpad).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for SccEnc {
|
impl ElementImpl for SccEnc {
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &Self::Type,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||||
|
@ -445,12 +449,3 @@ impl ElementImpl for SccEnc {
|
||||||
self.parent_change_state(element, transition)
|
self.parent_change_state(element, transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"sccenc",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
SccEnc::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
39
video/closedcaption/src/scc_enc/mod.rs
Normal file
39
video/closedcaption/src/scc_enc/mod.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
// Copyright (C) 2019 Jordan Petridis <jordan@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 glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct SccEnc(ObjectSubclass<imp::SccEnc>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for SccEnc {}
|
||||||
|
unsafe impl Sync for SccEnc {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"sccenc",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
SccEnc::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -16,7 +16,6 @@
|
||||||
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||||
// Boston, MA 02110-1335, USA.
|
// Boston, MA 02110-1335, USA.
|
||||||
|
|
||||||
use glib::prelude::*;
|
|
||||||
use glib::subclass;
|
use glib::subclass;
|
||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
@ -24,8 +23,8 @@ use gst::subclass::prelude::*;
|
||||||
|
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
|
use super::parser::{SccLine, SccParser, TimeCode};
|
||||||
use crate::line_reader::LineReader;
|
use crate::line_reader::LineReader;
|
||||||
use crate::scc_parser::{SccLine, SccParser, TimeCode};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CAT: gst::DebugCategory = {
|
static ref CAT: gst::DebugCategory = {
|
||||||
|
@ -83,7 +82,7 @@ impl State {
|
||||||
&mut self,
|
&mut self,
|
||||||
tc: TimeCode,
|
tc: TimeCode,
|
||||||
framerate: gst::Fraction,
|
framerate: gst::Fraction,
|
||||||
element: &gst::Element,
|
element: &super::SccParse,
|
||||||
) -> Result<gst_video::ValidVideoTimeCode, gst::FlowError> {
|
) -> Result<gst_video::ValidVideoTimeCode, gst::FlowError> {
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
@ -137,7 +136,7 @@ impl State {
|
||||||
fn update_timestamp(
|
fn update_timestamp(
|
||||||
&mut self,
|
&mut self,
|
||||||
timecode: &gst_video::ValidVideoTimeCode,
|
timecode: &gst_video::ValidVideoTimeCode,
|
||||||
element: &gst::Element,
|
element: &super::SccParse,
|
||||||
) {
|
) {
|
||||||
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
|
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
|
||||||
|
|
||||||
|
@ -159,7 +158,7 @@ impl State {
|
||||||
buffer: &mut gst::buffer::Buffer,
|
buffer: &mut gst::buffer::Buffer,
|
||||||
timecode: &gst_video::ValidVideoTimeCode,
|
timecode: &gst_video::ValidVideoTimeCode,
|
||||||
framerate: gst::Fraction,
|
framerate: gst::Fraction,
|
||||||
element: &gst::Element,
|
element: &super::SccParse,
|
||||||
) {
|
) {
|
||||||
let buffer = buffer.get_mut().unwrap();
|
let buffer = buffer.get_mut().unwrap();
|
||||||
gst_video::VideoTimeCodeMeta::add(buffer, &timecode);
|
gst_video::VideoTimeCodeMeta::add(buffer, &timecode);
|
||||||
|
@ -175,7 +174,7 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SccParse {
|
pub struct SccParse {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
state: Mutex<State>,
|
state: Mutex<State>,
|
||||||
|
@ -184,7 +183,7 @@ struct SccParse {
|
||||||
impl SccParse {
|
impl SccParse {
|
||||||
fn handle_buffer(
|
fn handle_buffer(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::SccParse,
|
||||||
buffer: Option<gst::Buffer>,
|
buffer: Option<gst::Buffer>,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
@ -234,7 +233,7 @@ impl SccParse {
|
||||||
&self,
|
&self,
|
||||||
tc: TimeCode,
|
tc: TimeCode,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
element: &gst::Element,
|
element: &super::SccParse,
|
||||||
mut state: MutexGuard<State>,
|
mut state: MutexGuard<State>,
|
||||||
) -> Result<MutexGuard<State>, gst::FlowError> {
|
) -> Result<MutexGuard<State>, gst::FlowError> {
|
||||||
gst_trace!(
|
gst_trace!(
|
||||||
|
@ -309,7 +308,7 @@ impl SccParse {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::SccParse,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
@ -317,7 +316,7 @@ impl SccParse {
|
||||||
self.handle_buffer(element, Some(buffer))
|
self.handle_buffer(element, Some(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::SccParse, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -367,7 +366,7 @@ impl SccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn src_event(&self, pad: &gst::Pad, element: &super::SccParse, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -380,7 +379,12 @@ impl SccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_query(&self, pad: &gst::Pad, element: &gst::Element, query: &mut gst::QueryRef) -> bool {
|
fn src_query(
|
||||||
|
&self,
|
||||||
|
pad: &gst::Pad,
|
||||||
|
element: &super::SccParse,
|
||||||
|
query: &mut gst::QueryRef,
|
||||||
|
) -> bool {
|
||||||
use gst::QueryView;
|
use gst::QueryView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||||
|
@ -413,13 +417,14 @@ impl SccParse {
|
||||||
|
|
||||||
impl ObjectSubclass for SccParse {
|
impl ObjectSubclass for SccParse {
|
||||||
const NAME: &'static str = "RsSccParse";
|
const NAME: &'static str = "RsSccParse";
|
||||||
|
type Type = super::SccParse;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.chain_function(|pad, parent, buffer| {
|
.chain_function(|pad, parent, buffer| {
|
||||||
|
@ -463,7 +468,7 @@ impl ObjectSubclass for SccParse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"Scc Parse",
|
"Scc Parse",
|
||||||
"Parser/ClosedCaption",
|
"Parser/ClosedCaption",
|
||||||
|
@ -500,19 +505,18 @@ impl ObjectSubclass for SccParse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for SccParse {
|
impl ObjectImpl for SccParse {
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
element.add_pad(&self.srcpad).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for SccParse {
|
impl ElementImpl for SccParse {
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &Self::Type,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||||
|
@ -529,12 +533,3 @@ impl ElementImpl for SccParse {
|
||||||
self.parent_change_state(element, transition)
|
self.parent_change_state(element, transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"sccparse",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
SccParse::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
40
video/closedcaption/src/scc_parse/mod.rs
Normal file
40
video/closedcaption/src/scc_parse/mod.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
// Copyright (C) 2019 Jordan Petridis <jordan@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 glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
mod parser;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct SccParse(ObjectSubclass<imp::SccParse>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for SccParse {}
|
||||||
|
unsafe impl Sync for SccParse {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"sccparse",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
SccParse::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -455,7 +455,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parser() {
|
fn test_parser() {
|
||||||
let scc_file = include_bytes!("../tests/dn2018-1217.scc");
|
let scc_file = include_bytes!("../../tests/dn2018-1217.scc");
|
||||||
let mut reader = crate::line_reader::LineReader::new();
|
let mut reader = crate::line_reader::LineReader::new();
|
||||||
let mut parser = SccParser::new();
|
let mut parser = SccParser::new();
|
||||||
let mut line_cnt = 0;
|
let mut line_cnt = 0;
|
|
@ -18,13 +18,14 @@
|
||||||
use glib::prelude::*;
|
use glib::prelude::*;
|
||||||
use glib::subclass;
|
use glib::subclass;
|
||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
use glib::GEnum;
|
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
|
|
||||||
use super::cea608tott_ffi as ffi;
|
use crate::ffi;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use super::Mode;
|
||||||
|
|
||||||
fn decrement_pts(
|
fn decrement_pts(
|
||||||
min_frame_no: u64,
|
min_frame_no: u64,
|
||||||
frame_no: &mut u64,
|
frame_no: &mut u64,
|
||||||
|
@ -208,16 +209,6 @@ const DEFAULT_FPS_D: i32 = 1;
|
||||||
*/
|
*/
|
||||||
const LATENCY_BUFFERS: u64 = 74;
|
const LATENCY_BUFFERS: u64 = 74;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
|
|
||||||
#[repr(u32)]
|
|
||||||
#[genum(type_name = "GstTtToCea608Mode")]
|
|
||||||
enum Mode {
|
|
||||||
PopOn,
|
|
||||||
RollUp2,
|
|
||||||
RollUp3,
|
|
||||||
RollUp4,
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_MODE: Mode = Mode::RollUp2;
|
const DEFAULT_MODE: Mode = Mode::RollUp2;
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("mode", |name| {
|
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("mode", |name| {
|
||||||
|
@ -264,7 +255,7 @@ impl Default for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TtToCea608 {
|
pub struct TtToCea608 {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
|
|
||||||
|
@ -346,7 +337,7 @@ impl TtToCea608 {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::TtToCea608,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let pts = match buffer.get_pts() {
|
let pts = match buffer.get_pts() {
|
||||||
|
@ -628,7 +619,12 @@ impl TtToCea608 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_query(&self, pad: &gst::Pad, element: &gst::Element, query: &mut gst::QueryRef) -> bool {
|
fn src_query(
|
||||||
|
&self,
|
||||||
|
pad: &gst::Pad,
|
||||||
|
element: &super::TtToCea608,
|
||||||
|
query: &mut gst::QueryRef,
|
||||||
|
) -> bool {
|
||||||
use gst::QueryView;
|
use gst::QueryView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||||
|
@ -671,7 +667,7 @@ impl TtToCea608 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::TtToCea608, event: gst::Event) -> bool {
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
@ -785,13 +781,14 @@ impl TtToCea608 {
|
||||||
|
|
||||||
impl ObjectSubclass for TtToCea608 {
|
impl ObjectSubclass for TtToCea608 {
|
||||||
const NAME: &'static str = "TtToCea608";
|
const NAME: &'static str = "TtToCea608";
|
||||||
|
type Type = super::TtToCea608;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.chain_function(|pad, parent, buffer| {
|
.chain_function(|pad, parent, buffer| {
|
||||||
|
@ -831,7 +828,7 @@ impl ObjectSubclass for TtToCea608 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"TT to CEA-608",
|
"TT to CEA-608",
|
||||||
"Generic",
|
"Generic",
|
||||||
|
@ -874,15 +871,14 @@ impl ObjectSubclass for TtToCea608 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for TtToCea608 {
|
impl ObjectImpl for TtToCea608 {
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
element.add_pad(&self.srcpad).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
|
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -894,7 +890,7 @@ impl ObjectImpl for TtToCea608 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -910,7 +906,7 @@ impl ObjectImpl for TtToCea608 {
|
||||||
impl ElementImpl for TtToCea608 {
|
impl ElementImpl for TtToCea608 {
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &Self::Type,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||||
|
@ -941,12 +937,3 @@ impl ElementImpl for TtToCea608 {
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"tttocea608",
|
|
||||||
gst::Rank::None,
|
|
||||||
TtToCea608::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
49
video/closedcaption/src/tttocea608/mod.rs
Normal file
49
video/closedcaption/src/tttocea608/mod.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (C) 2020 Mathieu Duponchelle <mathieu@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 glib::prelude::*;
|
||||||
|
use glib::GEnum;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
|
||||||
|
#[repr(u32)]
|
||||||
|
#[genum(type_name = "GstTtToCea608Mode")]
|
||||||
|
enum Mode {
|
||||||
|
PopOn,
|
||||||
|
RollUp2,
|
||||||
|
RollUp3,
|
||||||
|
RollUp4,
|
||||||
|
}
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct TtToCea608(ObjectSubclass<imp::TtToCea608>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for TtToCea608 {}
|
||||||
|
unsafe impl Sync for TtToCea608 {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"tttocea608",
|
||||||
|
gst::Rank::None,
|
||||||
|
TtToCea608::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ use std::i32;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
pub struct NegotiationInfos {
|
struct NegotiationInfos {
|
||||||
input_state:
|
input_state:
|
||||||
Option<gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>>,
|
Option<gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>>,
|
||||||
output_info: Option<gst_video::VideoInfo>,
|
output_info: Option<gst_video::VideoInfo>,
|
||||||
|
@ -84,7 +84,7 @@ impl Dav1dDec {
|
||||||
|
|
||||||
pub fn handle_resolution_change(
|
pub fn handle_resolution_change(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &super::Dav1dDec,
|
||||||
pic: &dav1d::Picture,
|
pic: &dav1d::Picture,
|
||||||
format: gst_video::VideoFormat,
|
format: gst_video::VideoFormat,
|
||||||
) -> Result<(), gst::FlowError> {
|
) -> Result<(), gst::FlowError> {
|
||||||
|
@ -251,7 +251,7 @@ impl Dav1dDec {
|
||||||
|
|
||||||
fn handle_picture(
|
fn handle_picture(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &super::Dav1dDec,
|
||||||
pic: &dav1d::Picture,
|
pic: &dav1d::Picture,
|
||||||
format: gst_video::VideoFormat,
|
format: gst_video::VideoFormat,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
|
@ -297,7 +297,7 @@ impl Dav1dDec {
|
||||||
|
|
||||||
fn forward_pending_pictures(
|
fn forward_pending_pictures(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &super::Dav1dDec,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
for (pic, format) in self.get_pending_pictures()? {
|
for (pic, format) in self.get_pending_pictures()? {
|
||||||
self.handle_picture(element, &pic, format)?;
|
self.handle_picture(element, &pic, format)?;
|
||||||
|
@ -344,6 +344,7 @@ fn video_output_formats() -> Vec<glib::SendValue> {
|
||||||
|
|
||||||
impl ObjectSubclass for Dav1dDec {
|
impl ObjectSubclass for Dav1dDec {
|
||||||
const NAME: &'static str = "RsDav1dDec";
|
const NAME: &'static str = "RsDav1dDec";
|
||||||
|
type Type = super::Dav1dDec;
|
||||||
type ParentType = gst_video::VideoDecoder;
|
type ParentType = gst_video::VideoDecoder;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
@ -361,7 +362,7 @@ impl ObjectSubclass for Dav1dDec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"Dav1d AV1 Decoder",
|
"Dav1d AV1 Decoder",
|
||||||
"Codec/Decoder/Video",
|
"Codec/Decoder/Video",
|
||||||
|
@ -410,7 +411,7 @@ impl ObjectImpl for Dav1dDec {}
|
||||||
impl ElementImpl for Dav1dDec {}
|
impl ElementImpl for Dav1dDec {}
|
||||||
|
|
||||||
impl VideoDecoderImpl for Dav1dDec {
|
impl VideoDecoderImpl for Dav1dDec {
|
||||||
fn start(&self, element: &gst_video::VideoDecoder) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
{
|
{
|
||||||
let mut infos = self.negotiation_infos.lock().unwrap();
|
let mut infos = self.negotiation_infos.lock().unwrap();
|
||||||
infos.output_info = None;
|
infos.output_info = None;
|
||||||
|
@ -421,7 +422,7 @@ impl VideoDecoderImpl for Dav1dDec {
|
||||||
|
|
||||||
fn set_format(
|
fn set_format(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &Self::Type,
|
||||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
{
|
{
|
||||||
|
@ -434,7 +435,7 @@ impl VideoDecoderImpl for Dav1dDec {
|
||||||
|
|
||||||
fn handle_frame(
|
fn handle_frame(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &Self::Type,
|
||||||
frame: gst_video::VideoCodecFrame,
|
frame: gst_video::VideoCodecFrame,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let input_buffer = frame
|
let input_buffer = frame
|
||||||
|
@ -447,24 +448,21 @@ impl VideoDecoderImpl for Dav1dDec {
|
||||||
Ok(gst::FlowSuccess::Ok)
|
Ok(gst::FlowSuccess::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&self, element: &gst_video::VideoDecoder) -> bool {
|
fn flush(&self, element: &Self::Type) -> bool {
|
||||||
gst_info!(CAT, obj: element, "Flushing");
|
gst_info!(CAT, obj: element, "Flushing");
|
||||||
self.flush_decoder();
|
self.flush_decoder();
|
||||||
self.drop_decoded_pictures();
|
self.drop_decoded_pictures();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain(&self, element: &gst_video::VideoDecoder) -> Result<gst::FlowSuccess, gst::FlowError> {
|
fn drain(&self, element: &Self::Type) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_info!(CAT, obj: element, "Draining");
|
gst_info!(CAT, obj: element, "Draining");
|
||||||
self.flush_decoder();
|
self.flush_decoder();
|
||||||
self.forward_pending_pictures(element)?;
|
self.forward_pending_pictures(element)?;
|
||||||
self.parent_drain(element)
|
self.parent_drain(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(
|
fn finish(&self, element: &Self::Type) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
&self,
|
|
||||||
element: &gst_video::VideoDecoder,
|
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
|
||||||
gst_info!(CAT, obj: element, "Finishing");
|
gst_info!(CAT, obj: element, "Finishing");
|
||||||
self.flush_decoder();
|
self.flush_decoder();
|
||||||
self.forward_pending_pictures(element)?;
|
self.forward_pending_pictures(element)?;
|
||||||
|
@ -473,7 +471,7 @@ impl VideoDecoderImpl for Dav1dDec {
|
||||||
|
|
||||||
fn decide_allocation(
|
fn decide_allocation(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoDecoder,
|
element: &Self::Type,
|
||||||
query: &mut gst::QueryRef,
|
query: &mut gst::QueryRef,
|
||||||
) -> Result<(), gst::ErrorMessage> {
|
) -> Result<(), gst::ErrorMessage> {
|
||||||
if let gst::query::QueryView::Allocation(allocation) = query.view() {
|
if let gst::query::QueryView::Allocation(allocation) = query.view() {
|
||||||
|
@ -498,12 +496,3 @@ impl VideoDecoderImpl for Dav1dDec {
|
||||||
self.parent_decide_allocation(element, query)
|
self.parent_decide_allocation(element, query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"rsdav1ddec",
|
|
||||||
gst::Rank::Primary + 1,
|
|
||||||
Dav1dDec::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
29
video/dav1d/src/dav1ddec/mod.rs
Normal file
29
video/dav1d/src/dav1ddec/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (C) 2019 Philippe Normand <philn@igalia.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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct Dav1dDec(ObjectSubclass<imp::Dav1dDec>) @extends gst_video::VideoDecoder, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for Dav1dDec {}
|
||||||
|
unsafe impl Sync for Dav1dDec {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"rsdav1ddec",
|
||||||
|
gst::Rank::Primary + 1,
|
||||||
|
Dav1dDec::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -31,8 +31,7 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct FlvDemux {
|
||||||
struct FlvDemux {
|
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
audio_srcpad: Mutex<Option<gst::Pad>>,
|
audio_srcpad: Mutex<Option<gst::Pad>>,
|
||||||
video_srcpad: Mutex<Option<gst::Pad>>,
|
video_srcpad: Mutex<Option<gst::Pad>>,
|
||||||
|
@ -42,7 +41,6 @@ struct FlvDemux {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Debug)]
|
|
||||||
enum State {
|
enum State {
|
||||||
Stopped,
|
Stopped,
|
||||||
NeedHeader,
|
NeedHeader,
|
||||||
|
@ -60,14 +58,13 @@ enum Stream {
|
||||||
Video,
|
Video,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
enum Event {
|
enum Event {
|
||||||
StreamChanged(Stream, gst::Caps),
|
StreamChanged(Stream, gst::Caps),
|
||||||
Buffer(Stream, gst::Buffer),
|
Buffer(Stream, gst::Buffer),
|
||||||
HaveAllStreams,
|
HaveAllStreams,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct StreamingState {
|
struct StreamingState {
|
||||||
audio: Option<AudioFormat>,
|
audio: Option<AudioFormat>,
|
||||||
expect_audio: bool,
|
expect_audio: bool,
|
||||||
|
@ -124,13 +121,14 @@ struct Metadata {
|
||||||
|
|
||||||
impl ObjectSubclass for FlvDemux {
|
impl ObjectSubclass for FlvDemux {
|
||||||
const NAME: &'static str = "RsFlvDemux";
|
const NAME: &'static str = "RsFlvDemux";
|
||||||
|
type Type = super::FlvDemux;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib_object_subclass!();
|
glib_object_subclass!();
|
||||||
|
|
||||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.get_pad_template("sink").unwrap();
|
let templ = klass.get_pad_template("sink").unwrap();
|
||||||
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||||
.activate_function(|pad, parent| {
|
.activate_function(|pad, parent| {
|
||||||
|
@ -178,7 +176,7 @@ impl ObjectSubclass for FlvDemux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"FLV Demuxer",
|
"FLV Demuxer",
|
||||||
"Codec/Demuxer",
|
"Codec/Demuxer",
|
||||||
|
@ -274,11 +272,10 @@ impl ObjectSubclass for FlvDemux {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for FlvDemux {
|
impl ObjectImpl for FlvDemux {
|
||||||
fn constructed(&self, obj: &glib::Object) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
obj.add_pad(&self.sinkpad).unwrap();
|
||||||
element.add_pad(&self.sinkpad).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +285,7 @@ impl FlvDemux {
|
||||||
fn sink_activate(
|
fn sink_activate(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
_element: &gst::Element,
|
_element: &super::FlvDemux,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
let mode = {
|
let mode = {
|
||||||
let mut query = gst::query::Scheduling::new();
|
let mut query = gst::query::Scheduling::new();
|
||||||
|
@ -317,7 +314,7 @@ impl FlvDemux {
|
||||||
fn sink_activatemode(
|
fn sink_activatemode(
|
||||||
&self,
|
&self,
|
||||||
_pad: &gst::Pad,
|
_pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
mode: gst::PadMode,
|
mode: gst::PadMode,
|
||||||
active: bool,
|
active: bool,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
|
@ -346,13 +343,17 @@ impl FlvDemux {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&self, _element: &gst::Element, _mode: gst::PadMode) -> Result<(), gst::ErrorMessage> {
|
fn start(
|
||||||
|
&self,
|
||||||
|
_element: &super::FlvDemux,
|
||||||
|
_mode: gst::PadMode,
|
||||||
|
) -> Result<(), gst::ErrorMessage> {
|
||||||
*self.state.lock().unwrap() = State::NeedHeader;
|
*self.state.lock().unwrap() = State::NeedHeader;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, element: &super::FlvDemux) -> Result<(), gst::ErrorMessage> {
|
||||||
*self.state.lock().unwrap() = State::Stopped;
|
*self.state.lock().unwrap() = State::Stopped;
|
||||||
self.adapter.lock().unwrap().clear();
|
self.adapter.lock().unwrap().clear();
|
||||||
|
|
||||||
|
@ -372,7 +373,7 @@ impl FlvDemux {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::FlvDemux, event: gst::Event) -> bool {
|
||||||
use crate::gst::EventView;
|
use crate::gst::EventView;
|
||||||
|
|
||||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
@ -397,7 +398,12 @@ impl FlvDemux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_query(&self, pad: &gst::Pad, element: &gst::Element, query: &mut gst::QueryRef) -> bool {
|
fn src_query(
|
||||||
|
&self,
|
||||||
|
pad: &gst::Pad,
|
||||||
|
element: &super::FlvDemux,
|
||||||
|
query: &mut gst::QueryRef,
|
||||||
|
) -> bool {
|
||||||
use crate::gst::QueryView;
|
use crate::gst::QueryView;
|
||||||
|
|
||||||
match query.view_mut() {
|
match query.view_mut() {
|
||||||
|
@ -445,7 +451,7 @@ impl FlvDemux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
fn src_event(&self, pad: &gst::Pad, element: &super::FlvDemux, event: gst::Event) -> bool {
|
||||||
use crate::gst::EventView;
|
use crate::gst::EventView;
|
||||||
|
|
||||||
match event.view() {
|
match event.view() {
|
||||||
|
@ -460,7 +466,7 @@ impl FlvDemux {
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
@ -541,7 +547,7 @@ impl FlvDemux {
|
||||||
|
|
||||||
fn find_header(
|
fn find_header(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
adapter: &mut gst_base::UniqueAdapter,
|
adapter: &mut gst_base::UniqueAdapter,
|
||||||
) -> Result<flavors::Header, ()> {
|
) -> Result<flavors::Header, ()> {
|
||||||
while adapter.available() >= 9 {
|
while adapter.available() >= 9 {
|
||||||
|
@ -564,7 +570,7 @@ impl FlvDemux {
|
||||||
|
|
||||||
fn handle_events(
|
fn handle_events(
|
||||||
&self,
|
&self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
events: SmallVec<[Event; 4]>,
|
events: SmallVec<[Event; 4]>,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
for event in events {
|
for event in events {
|
||||||
|
@ -633,7 +639,7 @@ impl FlvDemux {
|
||||||
Ok(gst::FlowSuccess::Ok)
|
Ok(gst::FlowSuccess::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_srcpad(&self, element: &gst::Element, name: &str, caps: &gst::Caps) -> gst::Pad {
|
fn create_srcpad(&self, element: &super::FlvDemux, name: &str, caps: &gst::Caps) -> gst::Pad {
|
||||||
let templ = element.get_element_class().get_pad_template(name).unwrap();
|
let templ = element.get_element_class().get_pad_template(name).unwrap();
|
||||||
let srcpad = gst::Pad::builder_with_template(&templ, Some(name))
|
let srcpad = gst::Pad::builder_with_template(&templ, Some(name))
|
||||||
.event_function(|pad, parent, event| {
|
.event_function(|pad, parent, event| {
|
||||||
|
@ -688,7 +694,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn handle_tag(
|
fn handle_tag(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
adapter: &mut gst_base::UniqueAdapter,
|
adapter: &mut gst_base::UniqueAdapter,
|
||||||
) -> Result<Option<SmallVec<[Event; 4]>>, gst::ErrorMessage> {
|
) -> Result<Option<SmallVec<[Event; 4]>>, gst::ErrorMessage> {
|
||||||
use nom::number::complete::be_u32;
|
use nom::number::complete::be_u32;
|
||||||
|
@ -750,7 +756,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn handle_script_tag(
|
fn handle_script_tag(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
tag_header: &flavors::TagHeader,
|
tag_header: &flavors::TagHeader,
|
||||||
adapter: &mut gst_base::UniqueAdapter,
|
adapter: &mut gst_base::UniqueAdapter,
|
||||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||||
|
@ -811,7 +817,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn update_audio_stream(
|
fn update_audio_stream(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
data_header: &flavors::AudioDataHeader,
|
data_header: &flavors::AudioDataHeader,
|
||||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||||
let mut events = SmallVec::new();
|
let mut events = SmallVec::new();
|
||||||
|
@ -855,7 +861,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn handle_aac_audio_packet_header(
|
fn handle_aac_audio_packet_header(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
tag_header: &flavors::TagHeader,
|
tag_header: &flavors::TagHeader,
|
||||||
adapter: &mut gst_base::UniqueAdapter,
|
adapter: &mut gst_base::UniqueAdapter,
|
||||||
) -> Result<bool, gst::ErrorMessage> {
|
) -> Result<bool, gst::ErrorMessage> {
|
||||||
|
@ -912,7 +918,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn handle_audio_tag(
|
fn handle_audio_tag(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
tag_header: &flavors::TagHeader,
|
tag_header: &flavors::TagHeader,
|
||||||
adapter: &mut gst_base::UniqueAdapter,
|
adapter: &mut gst_base::UniqueAdapter,
|
||||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||||
|
@ -981,7 +987,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn update_video_stream(
|
fn update_video_stream(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
data_header: &flavors::VideoDataHeader,
|
data_header: &flavors::VideoDataHeader,
|
||||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||||
let mut events = SmallVec::new();
|
let mut events = SmallVec::new();
|
||||||
|
@ -1025,7 +1031,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn handle_avc_video_packet_header(
|
fn handle_avc_video_packet_header(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
tag_header: &flavors::TagHeader,
|
tag_header: &flavors::TagHeader,
|
||||||
adapter: &mut gst_base::UniqueAdapter,
|
adapter: &mut gst_base::UniqueAdapter,
|
||||||
) -> Result<Option<i32>, gst::ErrorMessage> {
|
) -> Result<Option<i32>, gst::ErrorMessage> {
|
||||||
|
@ -1093,7 +1099,7 @@ impl StreamingState {
|
||||||
|
|
||||||
fn handle_video_tag(
|
fn handle_video_tag(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &gst::Element,
|
element: &super::FlvDemux,
|
||||||
tag_header: &flavors::TagHeader,
|
tag_header: &flavors::TagHeader,
|
||||||
adapter: &mut gst_base::UniqueAdapter,
|
adapter: &mut gst_base::UniqueAdapter,
|
||||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||||
|
@ -1565,12 +1571,3 @@ impl Metadata {
|
||||||
metadata
|
metadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"rsflvdemux",
|
|
||||||
gst::Rank::None,
|
|
||||||
FlvDemux::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
29
video/flavors/src/flvdemux/mod.rs
Normal file
29
video/flavors/src/flvdemux/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (C) 2016-2018 Sebastian Dröge <sebastian@centricular.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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct FlvDemux(ObjectSubclass<imp::FlvDemux>) @extends gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for FlvDemux {}
|
||||||
|
unsafe impl Sync for FlvDemux {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"rsflvdemux",
|
||||||
|
gst::Rank::None,
|
||||||
|
FlvDemux::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ use glib::subclass::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
use gst_video::prelude::*;
|
use gst_video::prelude::*;
|
||||||
use gst_video::subclass::prelude::*;
|
use gst_video::subclass::prelude::*;
|
||||||
use gstreamer_video as gst_video;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
|
@ -29,6 +28,7 @@ const DEFAULT_REPEAT: i32 = 0;
|
||||||
struct CacheBuffer {
|
struct CacheBuffer {
|
||||||
buffer: AtomicRefCell<Vec<u8>>,
|
buffer: AtomicRefCell<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CacheBuffer {
|
impl CacheBuffer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -47,6 +47,7 @@ impl CacheBuffer {
|
||||||
std::mem::replace(&mut *buffer, Vec::new())
|
std::mem::replace(&mut *buffer, Vec::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writer for a CacheBuffer instance. This class is passed to the gif::Encoder.
|
/// Writer for a CacheBuffer instance. This class is passed to the gif::Encoder.
|
||||||
/// Everything written to the CacheBufferWriter is stored in the underlying CacheBuffer.
|
/// Everything written to the CacheBufferWriter is stored in the underlying CacheBuffer.
|
||||||
struct CacheBufferWriter {
|
struct CacheBufferWriter {
|
||||||
|
@ -98,6 +99,7 @@ struct State {
|
||||||
last_actual_pts: gst::ClockTime,
|
last_actual_pts: gst::ClockTime,
|
||||||
context: Option<gif::Encoder<CacheBufferWriter>>,
|
context: Option<gif::Encoder<CacheBufferWriter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new(video_info: gst_video::VideoInfo) -> Self {
|
pub fn new(video_info: gst_video::VideoInfo) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -130,7 +132,7 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GifEnc {
|
pub struct GifEnc {
|
||||||
state: AtomicRefCell<Option<State>>,
|
state: AtomicRefCell<Option<State>>,
|
||||||
settings: Mutex<Settings>,
|
settings: Mutex<Settings>,
|
||||||
}
|
}
|
||||||
|
@ -141,6 +143,7 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
|
|
||||||
impl ObjectSubclass for GifEnc {
|
impl ObjectSubclass for GifEnc {
|
||||||
const NAME: &'static str = "GifEnc";
|
const NAME: &'static str = "GifEnc";
|
||||||
|
type Type = super::GifEnc;
|
||||||
type ParentType = gst_video::VideoEncoder;
|
type ParentType = gst_video::VideoEncoder;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
@ -154,7 +157,7 @@ impl ObjectSubclass for GifEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"GIF encoder",
|
"GIF encoder",
|
||||||
"Encoder/Video",
|
"Encoder/Video",
|
||||||
|
@ -211,7 +214,7 @@ impl ObjectSubclass for GifEnc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for GifEnc {
|
impl ObjectImpl for GifEnc {
|
||||||
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
|
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -223,7 +226,7 @@ impl ObjectImpl for GifEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -239,14 +242,14 @@ impl ObjectImpl for GifEnc {
|
||||||
impl ElementImpl for GifEnc {}
|
impl ElementImpl for GifEnc {}
|
||||||
|
|
||||||
impl VideoEncoderImpl for GifEnc {
|
impl VideoEncoderImpl for GifEnc {
|
||||||
fn stop(&self, _element: &gst_video::VideoEncoder) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
*self.state.borrow_mut() = None;
|
*self.state.borrow_mut() = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_format(
|
fn set_format(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoEncoder,
|
element: &Self::Type,
|
||||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
self.flush_encoder(element)
|
self.flush_encoder(element)
|
||||||
|
@ -272,16 +275,13 @@ impl VideoEncoderImpl for GifEnc {
|
||||||
self.parent_set_format(element, state)
|
self.parent_set_format(element, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(
|
fn finish(&self, element: &Self::Type) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
&self,
|
|
||||||
element: &gst_video::VideoEncoder,
|
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
|
||||||
self.flush_encoder(element)
|
self.flush_encoder(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_frame(
|
fn handle_frame(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoEncoder,
|
element: &Self::Type,
|
||||||
mut frame: gst_video::VideoCodecFrame,
|
mut frame: gst_video::VideoCodecFrame,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let mut state_guard = self.state.borrow_mut();
|
let mut state_guard = self.state.borrow_mut();
|
||||||
|
@ -388,10 +388,7 @@ impl VideoEncoderImpl for GifEnc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GifEnc {
|
impl GifEnc {
|
||||||
fn flush_encoder(
|
fn flush_encoder(&self, element: &super::GifEnc) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
&self,
|
|
||||||
element: &gst_video::VideoEncoder,
|
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
|
||||||
gst_debug!(CAT, obj: element, "Flushing");
|
gst_debug!(CAT, obj: element, "Flushing");
|
||||||
|
|
||||||
let trailer_buffer = self.state.borrow_mut().as_mut().map(|state| {
|
let trailer_buffer = self.state.borrow_mut().as_mut().map(|state| {
|
||||||
|
@ -440,12 +437,3 @@ fn get_tightly_packed_framebuffer(frame: &gst_video::VideoFrameRef<&gst::BufferR
|
||||||
|
|
||||||
raw_frame
|
raw_frame
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"gifenc",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
GifEnc::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
29
video/gif/src/gifenc/mod.rs
Normal file
29
video/gif/src/gifenc/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (C) 2020 Markus Ebner <info@ebner-markus.de>
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct GifEnc(ObjectSubclass<imp::GifEnc>) @extends gst_video::VideoEncoder, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for GifEnc {}
|
||||||
|
unsafe impl Sync for GifEnc {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"gifenc",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
GifEnc::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
extern crate glib;
|
extern crate glib;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate gstreamer as gst;
|
extern crate gstreamer as gst;
|
||||||
|
extern crate gstreamer_video as gst_video;
|
||||||
|
|
||||||
mod gifenc;
|
mod gifenc;
|
||||||
|
|
||||||
|
|
|
@ -281,7 +281,7 @@ struct State {
|
||||||
video_info: gst_video::VideoInfo,
|
video_info: gst_video::VideoInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Rav1Enc {
|
pub struct Rav1Enc {
|
||||||
state: AtomicRefCell<Option<State>>,
|
state: AtomicRefCell<Option<State>>,
|
||||||
settings: Mutex<Settings>,
|
settings: Mutex<Settings>,
|
||||||
}
|
}
|
||||||
|
@ -296,6 +296,7 @@ lazy_static! {
|
||||||
|
|
||||||
impl ObjectSubclass for Rav1Enc {
|
impl ObjectSubclass for Rav1Enc {
|
||||||
const NAME: &'static str = "Rav1Enc";
|
const NAME: &'static str = "Rav1Enc";
|
||||||
|
type Type = super::Rav1Enc;
|
||||||
type ParentType = gst_video::VideoEncoder;
|
type ParentType = gst_video::VideoEncoder;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
@ -309,7 +310,7 @@ impl ObjectSubclass for Rav1Enc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"rav1e AV1 encoder",
|
"rav1e AV1 encoder",
|
||||||
"Encoder/Video",
|
"Encoder/Video",
|
||||||
|
@ -370,7 +371,7 @@ impl ObjectSubclass for Rav1Enc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Rav1Enc {
|
impl ObjectImpl for Rav1Enc {
|
||||||
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
|
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -421,7 +422,7 @@ impl ObjectImpl for Rav1Enc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -473,7 +474,7 @@ impl ObjectImpl for Rav1Enc {
|
||||||
impl ElementImpl for Rav1Enc {}
|
impl ElementImpl for Rav1Enc {}
|
||||||
|
|
||||||
impl VideoEncoderImpl for Rav1Enc {
|
impl VideoEncoderImpl for Rav1Enc {
|
||||||
fn stop(&self, _element: &gst_video::VideoEncoder) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
*self.state.borrow_mut() = None;
|
*self.state.borrow_mut() = None;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -483,7 +484,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
||||||
#[allow(clippy::wildcard_in_or_patterns)]
|
#[allow(clippy::wildcard_in_or_patterns)]
|
||||||
fn set_format(
|
fn set_format(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoEncoder,
|
element: &Self::Type,
|
||||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
self.finish(element)
|
self.finish(element)
|
||||||
|
@ -629,7 +630,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
||||||
self.parent_set_format(element, state)
|
self.parent_set_format(element, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&self, element: &gst_video::VideoEncoder) -> bool {
|
fn flush(&self, element: &Self::Type) -> bool {
|
||||||
gst_debug!(CAT, obj: element, "Flushing");
|
gst_debug!(CAT, obj: element, "Flushing");
|
||||||
|
|
||||||
let mut state_guard = self.state.borrow_mut();
|
let mut state_guard = self.state.borrow_mut();
|
||||||
|
@ -643,10 +644,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(
|
fn finish(&self, element: &Self::Type) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
&self,
|
|
||||||
element: &gst_video::VideoEncoder,
|
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
|
||||||
gst_debug!(CAT, obj: element, "Finishing");
|
gst_debug!(CAT, obj: element, "Finishing");
|
||||||
|
|
||||||
let mut state_guard = self.state.borrow_mut();
|
let mut state_guard = self.state.borrow_mut();
|
||||||
|
@ -663,7 +661,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
||||||
|
|
||||||
fn handle_frame(
|
fn handle_frame(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoEncoder,
|
element: &Self::Type,
|
||||||
frame: gst_video::VideoCodecFrame,
|
frame: gst_video::VideoCodecFrame,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let mut state_guard = self.state.borrow_mut();
|
let mut state_guard = self.state.borrow_mut();
|
||||||
|
@ -721,7 +719,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
||||||
impl Rav1Enc {
|
impl Rav1Enc {
|
||||||
fn output_frames(
|
fn output_frames(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoEncoder,
|
element: &super::Rav1Enc,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
loop {
|
loop {
|
||||||
|
@ -768,12 +766,3 @@ impl Rav1Enc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"rav1enc",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
Rav1Enc::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
29
video/rav1e/src/rav1enc/mod.rs
Normal file
29
video/rav1e/src/rav1enc/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct Rav1Enc(ObjectSubclass<imp::Rav1Enc>) @extends gst_video::VideoEncoder, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for Rav1Enc {}
|
||||||
|
unsafe impl Sync for Rav1Enc {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"rav1enc",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
Rav1Enc::static_type(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
use std::{io, io::Write, sync::Arc};
|
use std::{io, io::Write, sync::Arc};
|
||||||
|
|
||||||
|
use glib::glib_object_subclass;
|
||||||
use glib::subclass;
|
use glib::subclass;
|
||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
use glib::{glib_object_subclass, GEnum};
|
|
||||||
|
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
|
@ -22,77 +22,12 @@ use atomic_refcell::AtomicRefCell;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use super::CompressionLevel;
|
||||||
|
use super::FilterType;
|
||||||
|
|
||||||
const DEFAULT_COMPRESSION_LEVEL: CompressionLevel = CompressionLevel::Default;
|
const DEFAULT_COMPRESSION_LEVEL: CompressionLevel = CompressionLevel::Default;
|
||||||
const DEFAULT_FILTER_TYPE: FilterType = FilterType::NoFilter;
|
const DEFAULT_FILTER_TYPE: FilterType = FilterType::NoFilter;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
|
|
||||||
#[repr(u32)]
|
|
||||||
#[genum(type_name = "GstRsPngCompressionLevel")]
|
|
||||||
pub(crate) enum CompressionLevel {
|
|
||||||
#[genum(name = "Default: Use the default compression level.", nick = "default")]
|
|
||||||
Default,
|
|
||||||
#[genum(name = "Fast: A fast compression algorithm.", nick = "fast")]
|
|
||||||
Fast,
|
|
||||||
#[genum(
|
|
||||||
name = "Best: Uses the algorithm with the best results.",
|
|
||||||
nick = "best"
|
|
||||||
)]
|
|
||||||
Best,
|
|
||||||
#[genum(name = "Huffman: Huffman compression.", nick = "huffman")]
|
|
||||||
Huffman,
|
|
||||||
#[genum(name = "Rle: Rle compression.", nick = "rle")]
|
|
||||||
Rle,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
|
|
||||||
#[repr(u32)]
|
|
||||||
#[genum(type_name = "GstRsPngFilterType")]
|
|
||||||
pub(crate) enum FilterType {
|
|
||||||
#[genum(
|
|
||||||
name = "NoFilter: No filtering applied to the output.",
|
|
||||||
nick = "nofilter"
|
|
||||||
)]
|
|
||||||
NoFilter,
|
|
||||||
#[genum(name = "Sub: filter applied to each pixel.", nick = "sub")]
|
|
||||||
Sub,
|
|
||||||
#[genum(name = "Up: Up filter similar to Sub.", nick = "up")]
|
|
||||||
Up,
|
|
||||||
#[genum(
|
|
||||||
name = "Avg: The Average filter uses the average of the two neighboring pixels.",
|
|
||||||
nick = "avg"
|
|
||||||
)]
|
|
||||||
Avg,
|
|
||||||
#[genum(
|
|
||||||
name = "Paeth: The Paeth filter computes a simple linear function of the three neighboring pixels.",
|
|
||||||
nick = "paeth"
|
|
||||||
)]
|
|
||||||
Paeth,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CompressionLevel> for png::Compression {
|
|
||||||
fn from(value: CompressionLevel) -> Self {
|
|
||||||
match value {
|
|
||||||
CompressionLevel::Default => png::Compression::Default,
|
|
||||||
CompressionLevel::Fast => png::Compression::Fast,
|
|
||||||
CompressionLevel::Best => png::Compression::Best,
|
|
||||||
CompressionLevel::Huffman => png::Compression::Huffman,
|
|
||||||
CompressionLevel::Rle => png::Compression::Rle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FilterType> for png::FilterType {
|
|
||||||
fn from(value: FilterType) -> Self {
|
|
||||||
match value {
|
|
||||||
FilterType::NoFilter => png::FilterType::NoFilter,
|
|
||||||
FilterType::Sub => png::FilterType::Sub,
|
|
||||||
FilterType::Up => png::FilterType::Up,
|
|
||||||
FilterType::Avg => png::FilterType::Avg,
|
|
||||||
FilterType::Paeth => png::FilterType::Paeth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
gst::DebugCategory::new(
|
gst::DebugCategory::new(
|
||||||
"rspngenc",
|
"rspngenc",
|
||||||
|
@ -249,13 +184,14 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PngEncoder {
|
pub struct PngEncoder {
|
||||||
state: Mutex<Option<State>>,
|
state: Mutex<Option<State>>,
|
||||||
settings: Mutex<Settings>,
|
settings: Mutex<Settings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectSubclass for PngEncoder {
|
impl ObjectSubclass for PngEncoder {
|
||||||
const NAME: &'static str = "PngEncoder";
|
const NAME: &'static str = "PngEncoder";
|
||||||
|
type Type = super::PngEncoder;
|
||||||
type ParentType = gst_video::VideoEncoder;
|
type ParentType = gst_video::VideoEncoder;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
@ -269,7 +205,7 @@ impl ObjectSubclass for PngEncoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
klass.set_metadata(
|
klass.set_metadata(
|
||||||
"PNG encoder",
|
"PNG encoder",
|
||||||
"Encoder/Video",
|
"Encoder/Video",
|
||||||
|
@ -323,7 +259,7 @@ impl ObjectSubclass for PngEncoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for PngEncoder {
|
impl ObjectImpl for PngEncoder {
|
||||||
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
|
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -343,7 +279,7 @@ impl ObjectImpl for PngEncoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||||
let prop = &PROPERTIES[id];
|
let prop = &PROPERTIES[id];
|
||||||
|
|
||||||
match *prop {
|
match *prop {
|
||||||
|
@ -363,14 +299,14 @@ impl ObjectImpl for PngEncoder {
|
||||||
impl ElementImpl for PngEncoder {}
|
impl ElementImpl for PngEncoder {}
|
||||||
|
|
||||||
impl VideoEncoderImpl for PngEncoder {
|
impl VideoEncoderImpl for PngEncoder {
|
||||||
fn stop(&self, _element: &gst_video::VideoEncoder) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
*self.state.lock() = None;
|
*self.state.lock() = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_format(
|
fn set_format(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoEncoder,
|
element: &Self::Type,
|
||||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||||
) -> Result<(), gst::LoggableError> {
|
) -> Result<(), gst::LoggableError> {
|
||||||
let video_info = state.get_info();
|
let video_info = state.get_info();
|
||||||
|
@ -392,7 +328,7 @@ impl VideoEncoderImpl for PngEncoder {
|
||||||
|
|
||||||
fn handle_frame(
|
fn handle_frame(
|
||||||
&self,
|
&self,
|
||||||
element: &gst_video::VideoEncoder,
|
element: &Self::Type,
|
||||||
mut frame: gst_video::VideoCodecFrame,
|
mut frame: gst_video::VideoCodecFrame,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let mut state_guard = self.state.lock();
|
let mut state_guard = self.state.lock();
|
||||||
|
@ -427,12 +363,3 @@ impl VideoEncoderImpl for PngEncoder {
|
||||||
element.finish_frame(Some(frame))
|
element.finish_frame(Some(frame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"rspngenc",
|
|
||||||
gst::Rank::Primary,
|
|
||||||
PngEncoder::get_type(),
|
|
||||||
)
|
|
||||||
}
|
|
98
video/rspng/src/pngenc/mod.rs
Normal file
98
video/rspng/src/pngenc/mod.rs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright (C) 2020 Natanael Mojica <neithanmo@gmail.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.
|
||||||
|
|
||||||
|
use glib::prelude::*;
|
||||||
|
use glib::{glib_wrapper, GEnum};
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
|
||||||
|
#[repr(u32)]
|
||||||
|
#[genum(type_name = "GstRsPngCompressionLevel")]
|
||||||
|
pub(crate) enum CompressionLevel {
|
||||||
|
#[genum(name = "Default: Use the default compression level.", nick = "default")]
|
||||||
|
Default,
|
||||||
|
#[genum(name = "Fast: A fast compression algorithm.", nick = "fast")]
|
||||||
|
Fast,
|
||||||
|
#[genum(
|
||||||
|
name = "Best: Uses the algorithm with the best results.",
|
||||||
|
nick = "best"
|
||||||
|
)]
|
||||||
|
Best,
|
||||||
|
#[genum(name = "Huffman: Huffman compression.", nick = "huffman")]
|
||||||
|
Huffman,
|
||||||
|
#[genum(name = "Rle: Rle compression.", nick = "rle")]
|
||||||
|
Rle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
|
||||||
|
#[repr(u32)]
|
||||||
|
#[genum(type_name = "GstRsPngFilterType")]
|
||||||
|
pub(crate) enum FilterType {
|
||||||
|
#[genum(
|
||||||
|
name = "NoFilter: No filtering applied to the output.",
|
||||||
|
nick = "nofilter"
|
||||||
|
)]
|
||||||
|
NoFilter,
|
||||||
|
#[genum(name = "Sub: filter applied to each pixel.", nick = "sub")]
|
||||||
|
Sub,
|
||||||
|
#[genum(name = "Up: Up filter similar to Sub.", nick = "up")]
|
||||||
|
Up,
|
||||||
|
#[genum(
|
||||||
|
name = "Avg: The Average filter uses the average of the two neighboring pixels.",
|
||||||
|
nick = "avg"
|
||||||
|
)]
|
||||||
|
Avg,
|
||||||
|
#[genum(
|
||||||
|
name = "Paeth: The Paeth filter computes a simple linear function of the three neighboring pixels.",
|
||||||
|
nick = "paeth"
|
||||||
|
)]
|
||||||
|
Paeth,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompressionLevel> for png::Compression {
|
||||||
|
fn from(value: CompressionLevel) -> Self {
|
||||||
|
match value {
|
||||||
|
CompressionLevel::Default => png::Compression::Default,
|
||||||
|
CompressionLevel::Fast => png::Compression::Fast,
|
||||||
|
CompressionLevel::Best => png::Compression::Best,
|
||||||
|
CompressionLevel::Huffman => png::Compression::Huffman,
|
||||||
|
CompressionLevel::Rle => png::Compression::Rle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FilterType> for png::FilterType {
|
||||||
|
fn from(value: FilterType) -> Self {
|
||||||
|
match value {
|
||||||
|
FilterType::NoFilter => png::FilterType::NoFilter,
|
||||||
|
FilterType::Sub => png::FilterType::Sub,
|
||||||
|
FilterType::Up => png::FilterType::Up,
|
||||||
|
FilterType::Avg => png::FilterType::Avg,
|
||||||
|
FilterType::Paeth => png::FilterType::Paeth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glib_wrapper! {
|
||||||
|
pub struct PngEncoder(ObjectSubclass<imp::PngEncoder>) @extends gst_video::VideoEncoder, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||||
|
// enforced but for the public wrapper type we need to specify this manually.
|
||||||
|
unsafe impl Send for PngEncoder {}
|
||||||
|
unsafe impl Sync for PngEncoder {}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"rspngenc",
|
||||||
|
gst::Rank::Primary,
|
||||||
|
PngEncoder::static_type(),
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in a new issue