mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-12-24 02:50:34 +00:00
video: Update for subclassing API changes
This commit is contained in:
parent
b021a8bf10
commit
717477fd36
39 changed files with 943 additions and 539 deletions
|
@ -17,18 +17,19 @@ use std::sync::Mutex;
|
|||
|
||||
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! {
|
||||
static ref CAT: gst::DebugCategory =
|
||||
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 {
|
||||
const NAME: &'static str = "CdgDec";
|
||||
type Type = super::CdgDec;
|
||||
type ParentType = gst_video::VideoDecoder;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<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(
|
||||
"CDG decoder",
|
||||
"Decoder/Video",
|
||||
|
@ -85,14 +86,14 @@ impl ObjectImpl for CdgDec {}
|
|||
impl ElementImpl 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();
|
||||
*out_info = None;
|
||||
|
||||
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();
|
||||
cdg_inter.reset(true);
|
||||
|
@ -102,7 +103,7 @@ impl VideoDecoderImpl for CdgDec {
|
|||
|
||||
fn handle_frame(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &Self::Type,
|
||||
mut frame: gst_video::VideoCodecFrame,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
{
|
||||
|
@ -192,7 +193,7 @@ impl VideoDecoderImpl for CdgDec {
|
|||
|
||||
fn decide_allocation(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &Self::Type,
|
||||
query: &mut gst::QueryRef,
|
||||
) -> Result<(), gst::ErrorMessage> {
|
||||
if let gst::query::QueryView::Allocation(allocation) = query.view() {
|
||||
|
@ -216,7 +217,7 @@ impl VideoDecoderImpl for CdgDec {
|
|||
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");
|
||||
|
||||
let mut cdg_inter = self.cdg_inter.lock().unwrap();
|
||||
|
@ -224,12 +225,3 @@ impl VideoDecoderImpl for CdgDec {
|
|||
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_2: u8 = 31;
|
||||
|
||||
struct CdgParse;
|
||||
pub struct CdgParse;
|
||||
|
||||
lazy_static! {
|
||||
static ref CAT: gst::DebugCategory = gst::DebugCategory::new(
|
||||
|
@ -35,6 +35,7 @@ lazy_static! {
|
|||
|
||||
impl ObjectSubclass for CdgParse {
|
||||
const NAME: &'static str = "CdgParse";
|
||||
type Type = super::CdgParse;
|
||||
type ParentType = gst_base::BaseParse;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -45,7 +46,7 @@ impl ObjectSubclass for CdgParse {
|
|||
Self
|
||||
}
|
||||
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.set_metadata(
|
||||
"CDG parser",
|
||||
"Codec/Parser/Video",
|
||||
|
@ -108,7 +109,7 @@ fn time_to_bytes(time: gst::ClockTime) -> Bytes {
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Set duration */
|
||||
|
@ -125,7 +126,7 @@ impl BaseParseImpl for CdgParse {
|
|||
|
||||
fn handle_frame(
|
||||
&self,
|
||||
element: &gst_base::BaseParse,
|
||||
element: &Self::Type,
|
||||
mut frame: gst_base::BaseParseFrame,
|
||||
) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
|
||||
let pad = element.get_src_pad();
|
||||
|
@ -212,7 +213,7 @@ impl BaseParseImpl for CdgParse {
|
|||
|
||||
fn convert<V: Into<gst::GenericFormattedValue>>(
|
||||
&self,
|
||||
_element: &gst_base::BaseParse,
|
||||
_element: &Self::Type,
|
||||
src_val: V,
|
||||
dest_format: gst::Format,
|
||||
) -> 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 cdgparse;
|
||||
mod constants;
|
||||
|
||||
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", &[]));
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
mod typefind;
|
||||
|
||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
cdgdec::register(plugin)?;
|
||||
cdgparse::register(plugin)?;
|
||||
typefind_register(plugin)?;
|
||||
typefind::register(plugin)?;
|
||||
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
|
||||
// except according to those terms.
|
||||
|
||||
use super::cea608tott_ffi as ffi;
|
||||
use super::ffi;
|
||||
use std::mem;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
@ -69,7 +69,7 @@ struct State {
|
|||
last_cc708_change: gst::ClockTime,
|
||||
}
|
||||
|
||||
struct CCDetect {
|
||||
pub struct CCDetect {
|
||||
settings: Mutex<Settings>,
|
||||
state: Mutex<Option<State>>,
|
||||
}
|
||||
|
@ -321,7 +321,7 @@ impl CCDetect {
|
|||
|
||||
fn maybe_update_properties(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &super::CCDetect,
|
||||
ts: gst::ClockTime,
|
||||
cc_packet: CCPacketContents,
|
||||
) -> Result<(), gst::FlowError> {
|
||||
|
@ -382,6 +382,7 @@ impl CCDetect {
|
|||
|
||||
impl ObjectSubclass for CCDetect {
|
||||
const NAME: &'static str = "CCDetect";
|
||||
type Type = super::CCDetect;
|
||||
type ParentType = gst_base::BaseTransform;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<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(
|
||||
"Closed Caption Detect",
|
||||
"Filter/Video/ClosedCaption/Detect",
|
||||
|
@ -441,7 +442,7 @@ impl ObjectSubclass 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];
|
||||
|
||||
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];
|
||||
|
||||
match *prop {
|
||||
|
@ -479,7 +480,7 @@ impl ElementImpl for CCDetect {}
|
|||
impl BaseTransformImpl for CCDetect {
|
||||
fn transform_ip_passthrough(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
buf: &gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let map = buf.map_readable().map_err(|_| gst::FlowError::Error)?;
|
||||
|
@ -517,7 +518,7 @@ impl BaseTransformImpl for CCDetect {
|
|||
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() {
|
||||
gst::event::EventView::Gap(gap) => {
|
||||
let _ = self.maybe_update_properties(
|
||||
|
@ -536,7 +537,7 @@ impl BaseTransformImpl for CCDetect {
|
|||
|
||||
fn set_caps(
|
||||
&self,
|
||||
_element: &gst_base::BaseTransform,
|
||||
_element: &Self::Type,
|
||||
incaps: &gst::Caps,
|
||||
outcaps: &gst::Caps,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
|
@ -569,19 +570,10 @@ impl BaseTransformImpl for CCDetect {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&self, _element: &gst_base::BaseTransform) -> Result<(), gst::ErrorMessage> {
|
||||
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||
// Drop state
|
||||
let _ = self.state.lock().unwrap().take();
|
||||
|
||||
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,
|
||||
// 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::prelude::*;
|
||||
use gst::prelude::*;
|
||||
|
@ -68,7 +61,7 @@ impl Default for State {
|
|||
|
||||
unsafe impl Send for State {}
|
||||
|
||||
struct Cea608Overlay {
|
||||
pub struct Cea608Overlay {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
state: Mutex<State>,
|
||||
|
@ -86,7 +79,7 @@ impl Cea608Overlay {
|
|||
// TODO: switch to the API presented in this post once it's been exposed
|
||||
fn recalculate_layout(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::Cea608Overlay,
|
||||
state: &mut State,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let video_info = state.video_info.as_ref().unwrap();
|
||||
|
@ -242,7 +235,7 @@ impl Cea608Overlay {
|
|||
|
||||
fn negotiate(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::Cea608Overlay,
|
||||
state: &mut State,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let video_info = match state.video_info.as_ref() {
|
||||
|
@ -294,7 +287,7 @@ impl Cea608Overlay {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::Cea608Overlay,
|
||||
mut buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -358,7 +351,12 @@ impl Cea608Overlay {
|
|||
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;
|
||||
|
||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||
|
@ -388,13 +386,14 @@ impl Cea608Overlay {
|
|||
|
||||
impl ObjectSubclass for Cea608Overlay {
|
||||
const NAME: &'static str = "RsCea608Overlay";
|
||||
type Type = super::Cea608Overlay;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"Cea 608 overlay",
|
||||
"Video/Overlay/Subtitle",
|
||||
|
@ -460,19 +459,18 @@ impl ObjectSubclass for Cea608Overlay {
|
|||
}
|
||||
|
||||
impl ObjectImpl for Cea608Overlay {
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for Cea608Overlay {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -489,12 +487,3 @@ impl ElementImpl for Cea608Overlay {
|
|||
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
|
||||
// except according to those terms.
|
||||
|
||||
use glib::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use gst::prelude::*;
|
||||
|
@ -42,7 +41,7 @@ impl Default for State {
|
|||
}
|
||||
}
|
||||
|
||||
struct Cea608ToTt {
|
||||
pub struct Cea608ToTt {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
|
||||
|
@ -61,7 +60,7 @@ impl Cea608ToTt {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
_element: &gst::Element,
|
||||
_element: &super::Cea608ToTt,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -271,7 +270,7 @@ impl Cea608ToTt {
|
|||
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;
|
||||
|
||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||
|
@ -371,13 +370,14 @@ impl Cea608ToTt {
|
|||
|
||||
impl ObjectSubclass for Cea608ToTt {
|
||||
const NAME: &'static str = "Cea608ToTt";
|
||||
type Type = super::Cea608ToTt;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"CEA-608 to TT",
|
||||
"Generic",
|
||||
|
@ -461,19 +461,18 @@ impl ObjectSubclass for Cea608ToTt {
|
|||
}
|
||||
|
||||
impl ObjectImpl for Cea608ToTt {
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for Cea608ToTt {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -499,12 +498,3 @@ impl ElementImpl for Cea608ToTt {
|
|||
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"]
|
||||
|
||||
// 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]
|
||||
extern crate glib;
|
||||
#[macro_use]
|
||||
|
@ -33,21 +28,20 @@ extern crate lazy_static;
|
|||
#[macro_use]
|
||||
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 ccdetect;
|
||||
mod cea608overlay;
|
||||
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 mcc_enc;
|
||||
mod mcc_parse;
|
||||
mod mcc_parser;
|
||||
mod scc_enc;
|
||||
mod scc_parse;
|
||||
mod scc_parser;
|
||||
mod tttocea608;
|
||||
|
||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
|
|
|
@ -28,9 +28,7 @@ use uuid::Uuid;
|
|||
use std::io::Write;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[path = "mcc_enc_headers.rs"]
|
||||
mod mcc_enc_headers;
|
||||
use self::mcc_enc_headers::*;
|
||||
use super::headers::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum Format {
|
||||
|
@ -89,7 +87,7 @@ static PROPERTIES: [subclass::Property; 2] = [
|
|||
}),
|
||||
];
|
||||
|
||||
struct MccEnc {
|
||||
pub struct MccEnc {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
state: Mutex<State>,
|
||||
|
@ -291,7 +289,7 @@ impl MccEnc {
|
|||
|
||||
fn generate_caption(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccEnc,
|
||||
state: &State,
|
||||
buffer: &gst::Buffer,
|
||||
outbuf: &mut Vec<u8>,
|
||||
|
@ -359,7 +357,7 @@ impl MccEnc {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::MccEnc,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -383,7 +381,7 @@ impl MccEnc {
|
|||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||
|
@ -462,13 +465,14 @@ impl MccEnc {
|
|||
|
||||
impl ObjectSubclass for MccEnc {
|
||||
const NAME: &'static str = "RsMccEnc";
|
||||
type Type = super::MccEnc;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"Mcc Encoder",
|
||||
"Encoder/ClosedCaption",
|
||||
|
@ -571,7 +575,7 @@ impl ObjectSubclass 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];
|
||||
|
||||
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];
|
||||
|
||||
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);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for MccEnc {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -632,12 +635,3 @@ impl ElementImpl for MccEnc {
|
|||
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::sync::{Mutex, MutexGuard};
|
||||
|
||||
use super::parser::{MccLine, MccParser, TimeCode};
|
||||
use crate::line_reader::LineReader;
|
||||
use crate::mcc_parser::{MccLine, MccParser, TimeCode};
|
||||
|
||||
lazy_static! {
|
||||
static ref CAT: gst::DebugCategory = {
|
||||
|
@ -54,7 +54,7 @@ struct PullState {
|
|||
}
|
||||
|
||||
impl PullState {
|
||||
fn new(element: &gst::Element, pad: &gst::Pad) -> Self {
|
||||
fn new(element: &super::MccParse, pad: &gst::Pad) -> Self {
|
||||
Self {
|
||||
need_stream_start: true,
|
||||
stream_id: pad
|
||||
|
@ -180,7 +180,7 @@ impl State {
|
|||
|
||||
fn handle_timecode(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
framerate: gst::Fraction,
|
||||
drop_frame: bool,
|
||||
tc: TimeCode,
|
||||
|
@ -219,7 +219,7 @@ impl State {
|
|||
/// not produce timestamps jumping backwards
|
||||
fn update_timestamp(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
timecode: &gst_video::ValidVideoTimeCode,
|
||||
) {
|
||||
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
|
||||
|
@ -255,7 +255,7 @@ impl State {
|
|||
|
||||
fn add_buffer_metadata(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
buffer: &mut gst::buffer::Buffer,
|
||||
timecode: &gst_video::ValidVideoTimeCode,
|
||||
framerate: gst::Fraction,
|
||||
|
@ -281,7 +281,7 @@ impl State {
|
|||
|
||||
fn create_events(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
format: Option<Format>,
|
||||
framerate: gst::Fraction,
|
||||
) -> Vec<gst::Event> {
|
||||
|
@ -341,7 +341,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
struct MccParse {
|
||||
pub struct MccParse {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
state: Mutex<State>,
|
||||
|
@ -369,7 +369,7 @@ impl AsMut<[u8]> for OffsetVec {
|
|||
impl MccParse {
|
||||
fn handle_buffer(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
buffer: Option<gst::Buffer>,
|
||||
scan_tc_rate: bool,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
|
@ -513,7 +513,7 @@ impl MccParse {
|
|||
|
||||
fn handle_skipped_line(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
tc: TimeCode,
|
||||
mut state: MutexGuard<State>,
|
||||
) -> Result<MutexGuard<State>, gst::FlowError> {
|
||||
|
@ -537,7 +537,7 @@ impl MccParse {
|
|||
|
||||
fn handle_line(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
tc: TimeCode,
|
||||
data: Vec<u8>,
|
||||
format: Format,
|
||||
|
@ -587,7 +587,7 @@ impl MccParse {
|
|||
fn sink_activate(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
let mode = {
|
||||
let mut query = gst::query::Scheduling::new();
|
||||
|
@ -616,7 +616,7 @@ impl MccParse {
|
|||
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 pad_weak = self.sinkpad.downgrade();
|
||||
let res = self.sinkpad.start_task(move || {
|
||||
|
@ -642,7 +642,7 @@ impl MccParse {
|
|||
fn sink_activatemode(
|
||||
&self,
|
||||
_pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
mode: gst::PadMode,
|
||||
active: bool,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
|
@ -659,7 +659,7 @@ impl MccParse {
|
|||
|
||||
fn scan_duration(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
) -> Result<Option<ValidVideoTimeCode>, gst::LoggableError> {
|
||||
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();
|
||||
|
||||
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 State {
|
||||
timecode_rate: ref tc_rate,
|
||||
|
@ -882,7 +882,7 @@ impl MccParse {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::MccParse,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -910,7 +910,7 @@ impl MccParse {
|
|||
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;
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||
|
@ -1120,13 +1125,14 @@ impl MccParse {
|
|||
|
||||
impl ObjectSubclass for MccParse {
|
||||
const NAME: &'static str = "RsMccParse";
|
||||
type Type = super::MccParse;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"Mcc Parse",
|
||||
"Parser/ClosedCaption",
|
||||
|
@ -1241,19 +1247,18 @@ impl ObjectSubclass for MccParse {
|
|||
}
|
||||
|
||||
impl ObjectImpl for MccParse {
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for MccParse {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -1270,12 +1275,3 @@ impl ElementImpl for MccParse {
|
|||
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]
|
||||
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 parser = MccParser::new();
|
||||
let mut line_cnt = 0;
|
|
@ -16,7 +16,6 @@
|
|||
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||
// Boston, MA 02110-1335, USA.
|
||||
|
||||
use glib::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use gst::prelude::*;
|
||||
|
@ -70,7 +69,7 @@ impl State {
|
|||
|
||||
fn generate_caption(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::SccEnc,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<Option<gst::Buffer>, gst::FlowError> {
|
||||
// Arbitrary number that was chosen to keep in order
|
||||
|
@ -141,7 +140,7 @@ impl State {
|
|||
// Flush the internal buffers into a line
|
||||
fn write_line(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::SccEnc,
|
||||
) -> Result<Option<gst::Buffer>, gst::FlowError> {
|
||||
let mut outbuf = Vec::new();
|
||||
let mut line_start = true;
|
||||
|
@ -218,7 +217,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
struct SccEnc {
|
||||
pub struct SccEnc {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
state: Mutex<State>,
|
||||
|
@ -228,7 +227,7 @@ impl SccEnc {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::SccEnc,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -246,7 +245,7 @@ impl SccEnc {
|
|||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||
|
@ -330,13 +334,14 @@ impl SccEnc {
|
|||
|
||||
impl ObjectSubclass for SccEnc {
|
||||
const NAME: &'static str = "RsSccEnc";
|
||||
type Type = super::SccEnc;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"Scc Encoder",
|
||||
"Encoder/ClosedCaption",
|
||||
|
@ -416,19 +421,18 @@ impl ObjectSubclass for SccEnc {
|
|||
}
|
||||
|
||||
impl ObjectImpl for SccEnc {
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for SccEnc {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -445,12 +449,3 @@ impl ElementImpl for SccEnc {
|
|||
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,
|
||||
// Boston, MA 02110-1335, USA.
|
||||
|
||||
use glib::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use gst::prelude::*;
|
||||
|
@ -24,8 +23,8 @@ use gst::subclass::prelude::*;
|
|||
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
use super::parser::{SccLine, SccParser, TimeCode};
|
||||
use crate::line_reader::LineReader;
|
||||
use crate::scc_parser::{SccLine, SccParser, TimeCode};
|
||||
|
||||
lazy_static! {
|
||||
static ref CAT: gst::DebugCategory = {
|
||||
|
@ -83,7 +82,7 @@ impl State {
|
|||
&mut self,
|
||||
tc: TimeCode,
|
||||
framerate: gst::Fraction,
|
||||
element: &gst::Element,
|
||||
element: &super::SccParse,
|
||||
) -> Result<gst_video::ValidVideoTimeCode, gst::FlowError> {
|
||||
use std::convert::TryInto;
|
||||
|
||||
|
@ -137,7 +136,7 @@ impl State {
|
|||
fn update_timestamp(
|
||||
&mut self,
|
||||
timecode: &gst_video::ValidVideoTimeCode,
|
||||
element: &gst::Element,
|
||||
element: &super::SccParse,
|
||||
) {
|
||||
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
|
||||
|
||||
|
@ -159,7 +158,7 @@ impl State {
|
|||
buffer: &mut gst::buffer::Buffer,
|
||||
timecode: &gst_video::ValidVideoTimeCode,
|
||||
framerate: gst::Fraction,
|
||||
element: &gst::Element,
|
||||
element: &super::SccParse,
|
||||
) {
|
||||
let buffer = buffer.get_mut().unwrap();
|
||||
gst_video::VideoTimeCodeMeta::add(buffer, &timecode);
|
||||
|
@ -175,7 +174,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
struct SccParse {
|
||||
pub struct SccParse {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
state: Mutex<State>,
|
||||
|
@ -184,7 +183,7 @@ struct SccParse {
|
|||
impl SccParse {
|
||||
fn handle_buffer(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::SccParse,
|
||||
buffer: Option<gst::Buffer>,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
@ -234,7 +233,7 @@ impl SccParse {
|
|||
&self,
|
||||
tc: TimeCode,
|
||||
data: Vec<u8>,
|
||||
element: &gst::Element,
|
||||
element: &super::SccParse,
|
||||
mut state: MutexGuard<State>,
|
||||
) -> Result<MutexGuard<State>, gst::FlowError> {
|
||||
gst_trace!(
|
||||
|
@ -309,7 +308,7 @@ impl SccParse {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::SccParse,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -317,7 +316,7 @@ impl SccParse {
|
|||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||
|
@ -413,13 +417,14 @@ impl SccParse {
|
|||
|
||||
impl ObjectSubclass for SccParse {
|
||||
const NAME: &'static str = "RsSccParse";
|
||||
type Type = super::SccParse;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"Scc Parse",
|
||||
"Parser/ClosedCaption",
|
||||
|
@ -500,19 +505,18 @@ impl ObjectSubclass for SccParse {
|
|||
}
|
||||
|
||||
impl ObjectImpl for SccParse {
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for SccParse {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -529,12 +533,3 @@ impl ElementImpl for SccParse {
|
|||
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]
|
||||
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 parser = SccParser::new();
|
||||
let mut line_cnt = 0;
|
|
@ -18,13 +18,14 @@
|
|||
use glib::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::GEnum;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
|
||||
use super::cea608tott_ffi as ffi;
|
||||
use crate::ffi;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::Mode;
|
||||
|
||||
fn decrement_pts(
|
||||
min_frame_no: u64,
|
||||
frame_no: &mut u64,
|
||||
|
@ -208,16 +209,6 @@ const DEFAULT_FPS_D: i32 = 1;
|
|||
*/
|
||||
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;
|
||||
|
||||
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,
|
||||
sinkpad: gst::Pad,
|
||||
|
||||
|
@ -346,7 +337,7 @@ impl TtToCea608 {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::TtToCea608,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
use gst::EventView;
|
||||
|
@ -785,13 +781,14 @@ impl TtToCea608 {
|
|||
|
||||
impl ObjectSubclass for TtToCea608 {
|
||||
const NAME: &'static str = "TtToCea608";
|
||||
type Type = super::TtToCea608;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"TT to CEA-608",
|
||||
"Generic",
|
||||
|
@ -874,15 +871,14 @@ impl ObjectSubclass for TtToCea608 {
|
|||
}
|
||||
|
||||
impl ObjectImpl for TtToCea608 {
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.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];
|
||||
|
||||
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];
|
||||
|
||||
match *prop {
|
||||
|
@ -910,7 +906,7 @@ impl ObjectImpl for TtToCea608 {
|
|||
impl ElementImpl for TtToCea608 {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -941,12 +937,3 @@ impl ElementImpl for TtToCea608 {
|
|||
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::sync::Mutex;
|
||||
|
||||
pub struct NegotiationInfos {
|
||||
struct NegotiationInfos {
|
||||
input_state:
|
||||
Option<gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>>,
|
||||
output_info: Option<gst_video::VideoInfo>,
|
||||
|
@ -84,7 +84,7 @@ impl Dav1dDec {
|
|||
|
||||
pub fn handle_resolution_change(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &super::Dav1dDec,
|
||||
pic: &dav1d::Picture,
|
||||
format: gst_video::VideoFormat,
|
||||
) -> Result<(), gst::FlowError> {
|
||||
|
@ -251,7 +251,7 @@ impl Dav1dDec {
|
|||
|
||||
fn handle_picture(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &super::Dav1dDec,
|
||||
pic: &dav1d::Picture,
|
||||
format: gst_video::VideoFormat,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
|
@ -297,7 +297,7 @@ impl Dav1dDec {
|
|||
|
||||
fn forward_pending_pictures(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &super::Dav1dDec,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
for (pic, format) in self.get_pending_pictures()? {
|
||||
self.handle_picture(element, &pic, format)?;
|
||||
|
@ -344,6 +344,7 @@ fn video_output_formats() -> Vec<glib::SendValue> {
|
|||
|
||||
impl ObjectSubclass for Dav1dDec {
|
||||
const NAME: &'static str = "RsDav1dDec";
|
||||
type Type = super::Dav1dDec;
|
||||
type ParentType = gst_video::VideoDecoder;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<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(
|
||||
"Dav1d AV1 Decoder",
|
||||
"Codec/Decoder/Video",
|
||||
|
@ -410,7 +411,7 @@ impl ObjectImpl for Dav1dDec {}
|
|||
impl ElementImpl 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();
|
||||
infos.output_info = None;
|
||||
|
@ -421,7 +422,7 @@ impl VideoDecoderImpl for Dav1dDec {
|
|||
|
||||
fn set_format(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &Self::Type,
|
||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
{
|
||||
|
@ -434,7 +435,7 @@ impl VideoDecoderImpl for Dav1dDec {
|
|||
|
||||
fn handle_frame(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &Self::Type,
|
||||
frame: gst_video::VideoCodecFrame,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let input_buffer = frame
|
||||
|
@ -447,24 +448,21 @@ impl VideoDecoderImpl for Dav1dDec {
|
|||
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");
|
||||
self.flush_decoder();
|
||||
self.drop_decoded_pictures();
|
||||
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");
|
||||
self.flush_decoder();
|
||||
self.forward_pending_pictures(element)?;
|
||||
self.parent_drain(element)
|
||||
}
|
||||
|
||||
fn finish(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
fn finish(&self, element: &Self::Type) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_info!(CAT, obj: element, "Finishing");
|
||||
self.flush_decoder();
|
||||
self.forward_pending_pictures(element)?;
|
||||
|
@ -473,7 +471,7 @@ impl VideoDecoderImpl for Dav1dDec {
|
|||
|
||||
fn decide_allocation(
|
||||
&self,
|
||||
element: &gst_video::VideoDecoder,
|
||||
element: &Self::Type,
|
||||
query: &mut gst::QueryRef,
|
||||
) -> Result<(), gst::ErrorMessage> {
|
||||
if let gst::query::QueryView::Allocation(allocation) = query.view() {
|
||||
|
@ -498,12 +496,3 @@ impl VideoDecoderImpl for Dav1dDec {
|
|||
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)]
|
||||
struct FlvDemux {
|
||||
pub struct FlvDemux {
|
||||
sinkpad: gst::Pad,
|
||||
audio_srcpad: Mutex<Option<gst::Pad>>,
|
||||
video_srcpad: Mutex<Option<gst::Pad>>,
|
||||
|
@ -42,7 +41,6 @@ struct FlvDemux {
|
|||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Stopped,
|
||||
NeedHeader,
|
||||
|
@ -60,14 +58,13 @@ enum Stream {
|
|||
Video,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
enum Event {
|
||||
StreamChanged(Stream, gst::Caps),
|
||||
Buffer(Stream, gst::Buffer),
|
||||
HaveAllStreams,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StreamingState {
|
||||
audio: Option<AudioFormat>,
|
||||
expect_audio: bool,
|
||||
|
@ -124,13 +121,14 @@ struct Metadata {
|
|||
|
||||
impl ObjectSubclass for FlvDemux {
|
||||
const NAME: &'static str = "RsFlvDemux";
|
||||
type Type = super::FlvDemux;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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 sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
|
||||
.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(
|
||||
"FLV Demuxer",
|
||||
"Codec/Demuxer",
|
||||
|
@ -274,11 +272,10 @@ impl ObjectSubclass for FlvDemux {
|
|||
}
|
||||
|
||||
impl ObjectImpl for FlvDemux {
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +285,7 @@ impl FlvDemux {
|
|||
fn sink_activate(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
_element: &gst::Element,
|
||||
_element: &super::FlvDemux,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
let mode = {
|
||||
let mut query = gst::query::Scheduling::new();
|
||||
|
@ -317,7 +314,7 @@ impl FlvDemux {
|
|||
fn sink_activatemode(
|
||||
&self,
|
||||
_pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
mode: gst::PadMode,
|
||||
active: bool,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
|
@ -346,13 +343,17 @@ impl FlvDemux {
|
|||
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;
|
||||
|
||||
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.adapter.lock().unwrap().clear();
|
||||
|
||||
|
@ -372,7 +373,7 @@ impl FlvDemux {
|
|||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
match event.view() {
|
||||
|
@ -460,7 +466,7 @@ impl FlvDemux {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -541,7 +547,7 @@ impl FlvDemux {
|
|||
|
||||
fn find_header(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
adapter: &mut gst_base::UniqueAdapter,
|
||||
) -> Result<flavors::Header, ()> {
|
||||
while adapter.available() >= 9 {
|
||||
|
@ -564,7 +570,7 @@ impl FlvDemux {
|
|||
|
||||
fn handle_events(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
events: SmallVec<[Event; 4]>,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
for event in events {
|
||||
|
@ -633,7 +639,7 @@ impl FlvDemux {
|
|||
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 srcpad = gst::Pad::builder_with_template(&templ, Some(name))
|
||||
.event_function(|pad, parent, event| {
|
||||
|
@ -688,7 +694,7 @@ impl StreamingState {
|
|||
|
||||
fn handle_tag(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
adapter: &mut gst_base::UniqueAdapter,
|
||||
) -> Result<Option<SmallVec<[Event; 4]>>, gst::ErrorMessage> {
|
||||
use nom::number::complete::be_u32;
|
||||
|
@ -750,7 +756,7 @@ impl StreamingState {
|
|||
|
||||
fn handle_script_tag(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
tag_header: &flavors::TagHeader,
|
||||
adapter: &mut gst_base::UniqueAdapter,
|
||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||
|
@ -811,7 +817,7 @@ impl StreamingState {
|
|||
|
||||
fn update_audio_stream(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
data_header: &flavors::AudioDataHeader,
|
||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||
let mut events = SmallVec::new();
|
||||
|
@ -855,7 +861,7 @@ impl StreamingState {
|
|||
|
||||
fn handle_aac_audio_packet_header(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
tag_header: &flavors::TagHeader,
|
||||
adapter: &mut gst_base::UniqueAdapter,
|
||||
) -> Result<bool, gst::ErrorMessage> {
|
||||
|
@ -912,7 +918,7 @@ impl StreamingState {
|
|||
|
||||
fn handle_audio_tag(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
tag_header: &flavors::TagHeader,
|
||||
adapter: &mut gst_base::UniqueAdapter,
|
||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||
|
@ -981,7 +987,7 @@ impl StreamingState {
|
|||
|
||||
fn update_video_stream(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
data_header: &flavors::VideoDataHeader,
|
||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||
let mut events = SmallVec::new();
|
||||
|
@ -1025,7 +1031,7 @@ impl StreamingState {
|
|||
|
||||
fn handle_avc_video_packet_header(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
tag_header: &flavors::TagHeader,
|
||||
adapter: &mut gst_base::UniqueAdapter,
|
||||
) -> Result<Option<i32>, gst::ErrorMessage> {
|
||||
|
@ -1093,7 +1099,7 @@ impl StreamingState {
|
|||
|
||||
fn handle_video_tag(
|
||||
&mut self,
|
||||
element: &gst::Element,
|
||||
element: &super::FlvDemux,
|
||||
tag_header: &flavors::TagHeader,
|
||||
adapter: &mut gst_base::UniqueAdapter,
|
||||
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
||||
|
@ -1565,12 +1571,3 @@ impl 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_video::prelude::*;
|
||||
use gst_video::subclass::prelude::*;
|
||||
use gstreamer_video as gst_video;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
io,
|
||||
|
@ -29,6 +28,7 @@ const DEFAULT_REPEAT: i32 = 0;
|
|||
struct CacheBuffer {
|
||||
buffer: AtomicRefCell<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl CacheBuffer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
@ -47,6 +47,7 @@ impl CacheBuffer {
|
|||
std::mem::replace(&mut *buffer, Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// Writer for a CacheBuffer instance. This class is passed to the gif::Encoder.
|
||||
/// Everything written to the CacheBufferWriter is stored in the underlying CacheBuffer.
|
||||
struct CacheBufferWriter {
|
||||
|
@ -98,6 +99,7 @@ struct State {
|
|||
last_actual_pts: gst::ClockTime,
|
||||
context: Option<gif::Encoder<CacheBufferWriter>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(video_info: gst_video::VideoInfo) -> Self {
|
||||
Self {
|
||||
|
@ -130,7 +132,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
struct GifEnc {
|
||||
pub struct GifEnc {
|
||||
state: AtomicRefCell<Option<State>>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
@ -141,6 +143,7 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|||
|
||||
impl ObjectSubclass for GifEnc {
|
||||
const NAME: &'static str = "GifEnc";
|
||||
type Type = super::GifEnc;
|
||||
type ParentType = gst_video::VideoEncoder;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<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(
|
||||
"GIF encoder",
|
||||
"Encoder/Video",
|
||||
|
@ -211,7 +214,7 @@ impl ObjectSubclass 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];
|
||||
|
||||
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];
|
||||
|
||||
match *prop {
|
||||
|
@ -239,14 +242,14 @@ impl ObjectImpl for GifEnc {
|
|||
impl ElementImpl 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;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_format(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
element: &Self::Type,
|
||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
self.flush_encoder(element)
|
||||
|
@ -272,16 +275,13 @@ impl VideoEncoderImpl for GifEnc {
|
|||
self.parent_set_format(element, state)
|
||||
}
|
||||
|
||||
fn finish(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
fn finish(&self, element: &Self::Type) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
self.flush_encoder(element)
|
||||
}
|
||||
|
||||
fn handle_frame(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
element: &Self::Type,
|
||||
mut frame: gst_video::VideoCodecFrame,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let mut state_guard = self.state.borrow_mut();
|
||||
|
@ -388,10 +388,7 @@ impl VideoEncoderImpl for GifEnc {
|
|||
}
|
||||
|
||||
impl GifEnc {
|
||||
fn flush_encoder(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
fn flush_encoder(&self, element: &super::GifEnc) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_debug!(CAT, obj: element, "Flushing");
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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;
|
||||
#[macro_use]
|
||||
extern crate gstreamer as gst;
|
||||
extern crate gstreamer_video as gst_video;
|
||||
|
||||
mod gifenc;
|
||||
|
||||
|
|
|
@ -281,7 +281,7 @@ struct State {
|
|||
video_info: gst_video::VideoInfo,
|
||||
}
|
||||
|
||||
struct Rav1Enc {
|
||||
pub struct Rav1Enc {
|
||||
state: AtomicRefCell<Option<State>>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
@ -296,6 +296,7 @@ lazy_static! {
|
|||
|
||||
impl ObjectSubclass for Rav1Enc {
|
||||
const NAME: &'static str = "Rav1Enc";
|
||||
type Type = super::Rav1Enc;
|
||||
type ParentType = gst_video::VideoEncoder;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<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(
|
||||
"rav1e AV1 encoder",
|
||||
"Encoder/Video",
|
||||
|
@ -370,7 +371,7 @@ impl ObjectSubclass 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];
|
||||
|
||||
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];
|
||||
|
||||
match *prop {
|
||||
|
@ -473,7 +474,7 @@ impl ObjectImpl for Rav1Enc {
|
|||
impl ElementImpl 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;
|
||||
|
||||
Ok(())
|
||||
|
@ -483,7 +484,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
|||
#[allow(clippy::wildcard_in_or_patterns)]
|
||||
fn set_format(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
element: &Self::Type,
|
||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
self.finish(element)
|
||||
|
@ -629,7 +630,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
|||
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");
|
||||
|
||||
let mut state_guard = self.state.borrow_mut();
|
||||
|
@ -643,10 +644,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
|||
true
|
||||
}
|
||||
|
||||
fn finish(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
fn finish(&self, element: &Self::Type) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_debug!(CAT, obj: element, "Finishing");
|
||||
|
||||
let mut state_guard = self.state.borrow_mut();
|
||||
|
@ -663,7 +661,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
|||
|
||||
fn handle_frame(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
element: &Self::Type,
|
||||
frame: gst_video::VideoCodecFrame,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let mut state_guard = self.state.borrow_mut();
|
||||
|
@ -721,7 +719,7 @@ impl VideoEncoderImpl for Rav1Enc {
|
|||
impl Rav1Enc {
|
||||
fn output_frames(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
element: &super::Rav1Enc,
|
||||
state: &mut State,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
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 glib::glib_object_subclass;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::{glib_object_subclass, GEnum};
|
||||
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
|
@ -22,77 +22,12 @@ use atomic_refcell::AtomicRefCell;
|
|||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::CompressionLevel;
|
||||
use super::FilterType;
|
||||
|
||||
const DEFAULT_COMPRESSION_LEVEL: CompressionLevel = CompressionLevel::Default;
|
||||
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(|| {
|
||||
gst::DebugCategory::new(
|
||||
"rspngenc",
|
||||
|
@ -249,13 +184,14 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
struct PngEncoder {
|
||||
pub struct PngEncoder {
|
||||
state: Mutex<Option<State>>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
impl ObjectSubclass for PngEncoder {
|
||||
const NAME: &'static str = "PngEncoder";
|
||||
type Type = super::PngEncoder;
|
||||
type ParentType = gst_video::VideoEncoder;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<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(
|
||||
"PNG encoder",
|
||||
"Encoder/Video",
|
||||
|
@ -323,7 +259,7 @@ impl ObjectSubclass 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];
|
||||
|
||||
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];
|
||||
|
||||
match *prop {
|
||||
|
@ -363,14 +299,14 @@ impl ObjectImpl for PngEncoder {
|
|||
impl ElementImpl 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;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_format(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
element: &Self::Type,
|
||||
state: &gst_video::VideoCodecState<'static, gst_video::video_codec_state::Readable>,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
let video_info = state.get_info();
|
||||
|
@ -392,7 +328,7 @@ impl VideoEncoderImpl for PngEncoder {
|
|||
|
||||
fn handle_frame(
|
||||
&self,
|
||||
element: &gst_video::VideoEncoder,
|
||||
element: &Self::Type,
|
||||
mut frame: gst_video::VideoCodecFrame,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let mut state_guard = self.state.lock();
|
||||
|
@ -427,12 +363,3 @@ impl VideoEncoderImpl for PngEncoder {
|
|||
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