2019-01-03 21:53:06 +00:00
|
|
|
// Copyright (C) 2016-2018 Sebastian Dröge <sebastian@centricular.com>
|
2016-11-24 14:29:43 +00:00
|
|
|
//
|
2017-02-16 15:52:27 +00:00
|
|
|
// 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.
|
2016-11-24 14:29:43 +00:00
|
|
|
|
|
|
|
use std::cmp;
|
2019-01-03 21:53:06 +00:00
|
|
|
use std::sync::Mutex;
|
2016-11-24 14:29:43 +00:00
|
|
|
|
|
|
|
use nom;
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
// FIXME: rustfmt removes the :: but they're required here
|
|
|
|
#[rustfmt::skip]
|
|
|
|
use ::flavors::parser as flavors;
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
use crate::gst;
|
|
|
|
use crate::gst::prelude::*;
|
|
|
|
use crate::gst::subclass::prelude::*;
|
|
|
|
use crate::gst_base;
|
|
|
|
use glib;
|
|
|
|
use glib::subclass;
|
2017-09-16 17:35:01 +00:00
|
|
|
|
|
|
|
use num_rational::Rational32;
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
use smallvec::SmallVec;
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
static ref CAT: gst::DebugCategory = {
|
|
|
|
gst::DebugCategory::new(
|
|
|
|
"rsflvdemux",
|
|
|
|
gst::DebugColorFlags::empty(),
|
|
|
|
"Rust FLV demuxer",
|
|
|
|
)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct FlvDemux {
|
|
|
|
sinkpad: gst::Pad,
|
|
|
|
audio_srcpad: Mutex<Option<gst::Pad>>,
|
|
|
|
video_srcpad: Mutex<Option<gst::Pad>>,
|
|
|
|
adapter: Mutex<gst_base::UniqueAdapter>,
|
|
|
|
flow_combiner: Mutex<gst_base::UniqueFlowCombiner>,
|
|
|
|
state: Mutex<State>,
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2016-12-03 22:57:42 +00:00
|
|
|
enum State {
|
|
|
|
Stopped,
|
2016-11-24 14:29:43 +00:00
|
|
|
NeedHeader,
|
2016-12-04 10:58:59 +00:00
|
|
|
Skipping {
|
|
|
|
audio: bool,
|
|
|
|
video: bool,
|
|
|
|
skip_left: u32,
|
|
|
|
},
|
2019-01-03 21:53:06 +00:00
|
|
|
Streaming(StreamingState),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
enum Stream {
|
|
|
|
Audio,
|
|
|
|
Video,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
enum Event {
|
|
|
|
StreamChanged(Stream, gst::Caps),
|
|
|
|
Buffer(Stream, gst::Buffer),
|
|
|
|
HaveAllStreams,
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2016-12-03 22:57:42 +00:00
|
|
|
struct StreamingState {
|
2016-12-04 10:58:59 +00:00
|
|
|
audio: Option<AudioFormat>,
|
|
|
|
expect_audio: bool,
|
|
|
|
video: Option<VideoFormat>,
|
|
|
|
expect_video: bool,
|
2016-11-24 14:29:43 +00:00
|
|
|
got_all_streams: bool,
|
2017-11-11 12:02:55 +00:00
|
|
|
last_position: gst::ClockTime,
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2016-12-04 18:24:44 +00:00
|
|
|
metadata: Option<Metadata>,
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2017-09-16 17:35:01 +00:00
|
|
|
aac_sequence_header: Option<gst::Buffer>,
|
|
|
|
avc_sequence_header: Option<gst::Buffer>,
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Eq, Clone)]
|
|
|
|
struct AudioFormat {
|
|
|
|
format: flavors::SoundFormat,
|
|
|
|
rate: u16,
|
|
|
|
width: u8,
|
|
|
|
channels: u8,
|
|
|
|
bitrate: Option<u32>,
|
2017-09-16 17:35:01 +00:00
|
|
|
aac_sequence_header: Option<gst::Buffer>,
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
#[derive(Debug, Eq, Clone)]
|
|
|
|
struct VideoFormat {
|
|
|
|
format: flavors::CodecId,
|
|
|
|
width: Option<u32>,
|
|
|
|
height: Option<u32>,
|
|
|
|
pixel_aspect_ratio: Option<Rational32>,
|
|
|
|
framerate: Option<Rational32>,
|
|
|
|
bitrate: Option<u32>,
|
|
|
|
avc_sequence_header: Option<gst::Buffer>,
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Default)]
|
|
|
|
struct Metadata {
|
|
|
|
duration: gst::ClockTime,
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
creation_date: Option<String>,
|
|
|
|
creator: Option<String>,
|
|
|
|
title: Option<String>,
|
|
|
|
metadata_creator: Option<String>, /* TODO: seek_table: _,
|
|
|
|
* filepositions / times metadata arrays */
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
audio_bitrate: Option<u32>,
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
video_width: Option<u32>,
|
|
|
|
video_height: Option<u32>,
|
|
|
|
video_pixel_aspect_ratio: Option<Rational32>,
|
|
|
|
video_framerate: Option<Rational32>,
|
|
|
|
video_bitrate: Option<u32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ObjectSubclass for FlvDemux {
|
|
|
|
const NAME: &'static str = "RsFlvDemux";
|
|
|
|
type ParentType = gst::Element;
|
|
|
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
|
|
|
type Class = subclass::simple::ClassStruct<Self>;
|
|
|
|
|
|
|
|
glib_object_subclass!();
|
|
|
|
|
|
|
|
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
|
|
|
let templ = klass.get_pad_template("sink").unwrap();
|
|
|
|
let sinkpad = gst::Pad::new_from_template(&templ, "sink");
|
|
|
|
|
|
|
|
sinkpad.set_activate_function(|pad, parent| {
|
|
|
|
FlvDemux::catch_panic_pad_function(
|
|
|
|
parent,
|
2019-01-24 20:19:39 +00:00
|
|
|
|| Err(gst_loggable_error!(CAT, "Panic activating sink pad")),
|
2019-01-03 21:53:06 +00:00
|
|
|
|demux, element| demux.sink_activate(pad, element),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
sinkpad.set_activatemode_function(|pad, parent, mode, active| {
|
|
|
|
FlvDemux::catch_panic_pad_function(
|
|
|
|
parent,
|
2019-01-24 20:19:39 +00:00
|
|
|
|| {
|
|
|
|
Err(gst_loggable_error!(
|
|
|
|
CAT,
|
|
|
|
"Panic activating sink pad with mode"
|
|
|
|
))
|
|
|
|
},
|
2019-01-03 21:53:06 +00:00
|
|
|
|demux, element| demux.sink_activatemode(pad, element, mode, active),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
sinkpad.set_chain_function(|pad, parent, buffer| {
|
|
|
|
FlvDemux::catch_panic_pad_function(
|
|
|
|
parent,
|
2019-01-11 23:45:05 +00:00
|
|
|
|| Err(gst::FlowError::Error),
|
2019-01-03 21:53:06 +00:00
|
|
|
|demux, element| demux.sink_chain(pad, element, buffer),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
sinkpad.set_event_function(|pad, parent, event| {
|
|
|
|
FlvDemux::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| false,
|
|
|
|
|demux, element| demux.sink_event(pad, element, event),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
FlvDemux {
|
|
|
|
sinkpad,
|
|
|
|
audio_srcpad: Mutex::new(None),
|
|
|
|
video_srcpad: Mutex::new(None),
|
|
|
|
state: Mutex::new(State::Stopped),
|
|
|
|
adapter: Mutex::new(gst_base::UniqueAdapter::new()),
|
|
|
|
flow_combiner: Mutex::new(gst_base::UniqueFlowCombiner::new()),
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
|
|
|
klass.set_metadata(
|
|
|
|
"FLV Demuxer",
|
|
|
|
"Codec/Demuxer",
|
|
|
|
"Demuxes FLV Streams",
|
|
|
|
"Sebastian Dröge <sebastian@centricular.com>",
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut caps = gst::Caps::new_empty();
|
|
|
|
{
|
|
|
|
let caps = caps.get_mut().unwrap();
|
|
|
|
|
|
|
|
caps.append(
|
|
|
|
gst::Caps::builder("audio/mpeg")
|
|
|
|
.field("mpegversion", &1i32)
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
caps.append(
|
|
|
|
gst::Caps::builder("audio/x-raw")
|
|
|
|
.field("layout", &"interleaved")
|
|
|
|
.field("format", &gst::List::new(&[&"U8", &"S16LE"]))
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
caps.append(
|
|
|
|
gst::Caps::builder("audio/x-adpcm")
|
|
|
|
.field("layout", &"swf")
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
caps.append(gst::Caps::builder("audio/x-nellymoser").build());
|
|
|
|
caps.append(gst::Caps::builder("audio/x-alaw").build());
|
|
|
|
caps.append(gst::Caps::builder("audio/x-mulaw").build());
|
|
|
|
caps.append(
|
|
|
|
gst::Caps::builder("audio/mpeg")
|
|
|
|
.field("mpegversion", &4i32)
|
|
|
|
.field("framed", &true)
|
|
|
|
.field("stream-format", &"raw")
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
caps.append(gst::Caps::builder("audio/x-speex").build());
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
let audiosrc_pad_template = gst::PadTemplate::new(
|
|
|
|
"audio",
|
|
|
|
gst::PadDirection::Src,
|
|
|
|
gst::PadPresence::Sometimes,
|
|
|
|
&caps,
|
2019-01-29 15:26:40 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
2019-01-03 21:53:06 +00:00
|
|
|
klass.add_pad_template(audiosrc_pad_template);
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let mut caps = gst::Caps::new_empty();
|
|
|
|
{
|
|
|
|
let caps = caps.get_mut().unwrap();
|
2017-01-14 17:04:55 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
caps.append(
|
|
|
|
gst::Caps::builder("video/x-flash-video")
|
|
|
|
.field("flvversion", &1i32)
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
caps.append(gst::Caps::builder("video/x-flash-screen").build());
|
|
|
|
caps.append(gst::Caps::builder("video/x-vp6-flash").build());
|
|
|
|
caps.append(gst::Caps::builder("video/x-vp6-flash-alpha").build());
|
|
|
|
caps.append(gst::Caps::builder("video/x-flash-screen2").build());
|
|
|
|
caps.append(
|
|
|
|
gst::Caps::builder("video/x-h264")
|
|
|
|
.field("stream-format", &"avc")
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
caps.append(gst::Caps::builder("video/x-h263").build());
|
|
|
|
caps.append(
|
|
|
|
gst::Caps::builder("video/mpeg")
|
|
|
|
.field("mpegversion", &4i32)
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let videosrc_pad_template = gst::PadTemplate::new(
|
|
|
|
"video",
|
|
|
|
gst::PadDirection::Src,
|
|
|
|
gst::PadPresence::Sometimes,
|
|
|
|
&caps,
|
2019-01-29 15:26:40 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
2019-01-03 21:53:06 +00:00
|
|
|
klass.add_pad_template(videosrc_pad_template);
|
|
|
|
|
|
|
|
let caps = gst::Caps::builder("video/x-flv").build();
|
|
|
|
let sink_pad_template = gst::PadTemplate::new(
|
|
|
|
"sink",
|
|
|
|
gst::PadDirection::Sink,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
2019-01-29 15:26:40 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
2019-01-03 21:53:06 +00:00
|
|
|
klass.add_pad_template(sink_pad_template);
|
|
|
|
}
|
|
|
|
}
|
2017-01-14 17:04:55 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
impl ObjectImpl for FlvDemux {
|
|
|
|
glib_object_impl!();
|
2017-01-13 10:52:41 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn constructed(&self, obj: &glib::Object) {
|
|
|
|
self.parent_constructed(obj);
|
2017-01-14 17:04:55 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
|
|
|
element.add_pad(&self.sinkpad).unwrap();
|
|
|
|
}
|
|
|
|
}
|
2017-01-14 17:04:55 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
impl ElementImpl for FlvDemux {}
|
2017-01-14 17:04:55 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
impl FlvDemux {
|
2019-01-17 18:30:46 +00:00
|
|
|
fn sink_activate(
|
|
|
|
&self,
|
|
|
|
pad: &gst::Pad,
|
|
|
|
_element: &gst::Element,
|
2019-01-24 20:19:39 +00:00
|
|
|
) -> Result<(), gst::LoggableError> {
|
2019-01-03 21:53:06 +00:00
|
|
|
let mode = {
|
|
|
|
let mut query = gst::Query::new_scheduling();
|
|
|
|
if !pad.peer_query(&mut query) {
|
2019-01-24 20:19:39 +00:00
|
|
|
return Err(gst_loggable_error!(CAT, "Scheduling query failed on peer"));
|
2016-12-04 21:51:38 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
|
|
|
|
// TODO: pull mode
|
|
|
|
if false
|
|
|
|
&& query.has_scheduling_mode_with_flags(
|
|
|
|
gst::PadMode::Pull,
|
|
|
|
gst::SchedulingFlags::SEEKABLE,
|
|
|
|
)
|
|
|
|
{
|
|
|
|
gst_debug!(CAT, obj: pad, "Activating in Pull mode");
|
|
|
|
gst::PadMode::Pull
|
|
|
|
} else {
|
|
|
|
gst_debug!(CAT, obj: pad, "Activating in Push mode");
|
|
|
|
gst::PadMode::Push
|
2016-12-04 21:51:38 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-01-24 20:19:39 +00:00
|
|
|
pad.activate_mode(mode, true)?;
|
|
|
|
Ok(())
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_activatemode(
|
|
|
|
&self,
|
|
|
|
_pad: &gst::Pad,
|
|
|
|
element: &gst::Element,
|
|
|
|
mode: gst::PadMode,
|
|
|
|
active: bool,
|
2019-01-24 20:19:39 +00:00
|
|
|
) -> Result<(), gst::LoggableError> {
|
2019-01-03 21:53:06 +00:00
|
|
|
if active {
|
2019-01-17 18:30:46 +00:00
|
|
|
self.start(element, mode).map_err(|err| {
|
2019-01-03 21:53:06 +00:00
|
|
|
element.post_error_message(&err);
|
2019-01-24 20:19:39 +00:00
|
|
|
gst_loggable_error!(CAT, "Failed to start element with mode {:?}", mode)
|
2019-01-17 18:30:46 +00:00
|
|
|
})?;
|
2019-01-03 21:53:06 +00:00
|
|
|
|
|
|
|
if mode == gst::PadMode::Pull {
|
|
|
|
// TODO implement pull mode
|
|
|
|
// self.sinkpad.start_task(...)
|
|
|
|
unimplemented!();
|
2018-10-11 10:49:10 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
} else {
|
|
|
|
if mode == gst::PadMode::Pull {
|
|
|
|
let _ = self.sinkpad.stop_task();
|
|
|
|
}
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2019-01-17 18:30:46 +00:00
|
|
|
self.stop(element).map_err(|err| {
|
2019-01-03 21:53:06 +00:00
|
|
|
element.post_error_message(&err);
|
2019-01-24 20:19:39 +00:00
|
|
|
gst_loggable_error!(CAT, "Failed to stop element")
|
2019-01-17 18:30:46 +00:00
|
|
|
})?;
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
2019-01-17 18:30:46 +00:00
|
|
|
|
|
|
|
Ok(())
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn start(&self, _element: &gst::Element, _mode: gst::PadMode) -> Result<(), gst::ErrorMessage> {
|
|
|
|
*self.state.lock().unwrap() = State::NeedHeader;
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn stop(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
|
|
|
|
*self.state.lock().unwrap() = State::Stopped;
|
|
|
|
self.adapter.lock().unwrap().clear();
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let mut flow_combiner = self.flow_combiner.lock().unwrap();
|
|
|
|
if let Some(pad) = self.audio_srcpad.lock().unwrap().take() {
|
|
|
|
element.remove_pad(&pad).unwrap();
|
|
|
|
flow_combiner.remove_pad(&pad);
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if let Some(pad) = self.video_srcpad.lock().unwrap().take() {
|
|
|
|
element.remove_pad(&pad).unwrap();
|
|
|
|
flow_combiner.remove_pad(&pad);
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
flow_combiner.reset();
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
Ok(())
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
|
|
|
use crate::gst::EventView;
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
|
|
|
match event.view() {
|
|
|
|
EventView::Eos(..) => {
|
|
|
|
// TODO implement
|
|
|
|
pad.event_default(element, event)
|
|
|
|
}
|
|
|
|
EventView::Segment(..) => {
|
|
|
|
// TODO implement
|
|
|
|
pad.event_default(element, event)
|
2018-10-11 10:49:10 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
EventView::FlushStart(..) => {
|
|
|
|
// TODO implement
|
|
|
|
pad.event_default(element, event)
|
|
|
|
}
|
|
|
|
EventView::FlushStop(..) => {
|
|
|
|
// TODO implement
|
|
|
|
pad.event_default(element, event)
|
|
|
|
}
|
|
|
|
_ => pad.event_default(element, event),
|
2016-12-04 21:51:38 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
2016-12-04 21:51:38 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn src_query(&self, pad: &gst::Pad, element: &gst::Element, query: &mut gst::QueryRef) -> bool {
|
|
|
|
use crate::gst::QueryView;
|
|
|
|
|
|
|
|
match query.view_mut() {
|
|
|
|
QueryView::Position(ref mut q) => {
|
|
|
|
let fmt = q.get_format();
|
|
|
|
if fmt == gst::Format::Time {
|
|
|
|
if self.sinkpad.peer_query(q.get_mut_query()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let State::Streaming(StreamingState { last_position, .. }) =
|
|
|
|
*self.state.lock().unwrap()
|
|
|
|
{
|
|
|
|
q.set(last_position);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
false
|
2018-10-11 10:49:10 +00:00
|
|
|
}
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
QueryView::Duration(ref mut q) => {
|
|
|
|
let fmt = q.get_format();
|
|
|
|
if fmt == gst::Format::Time {
|
|
|
|
if self.sinkpad.peer_query(q.get_mut_query()) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-12-04 21:51:38 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if let State::Streaming(StreamingState {
|
|
|
|
metadata: Some(Metadata { duration, .. }),
|
|
|
|
..
|
|
|
|
}) = *self.state.lock().unwrap()
|
|
|
|
{
|
|
|
|
q.set(duration);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
false
|
2018-10-11 10:49:10 +00:00
|
|
|
}
|
2016-12-04 21:51:38 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
_ => pad.query_default(element, query),
|
2016-12-04 21:51:38 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
2016-12-04 21:51:38 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
|
|
|
|
use crate::gst::EventView;
|
|
|
|
|
|
|
|
match event.view() {
|
|
|
|
EventView::Seek(..) => {
|
|
|
|
// TODO: Implement
|
|
|
|
false
|
|
|
|
}
|
|
|
|
_ => pad.event_default(element, event),
|
|
|
|
}
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn sink_chain(
|
|
|
|
&self,
|
|
|
|
pad: &gst::Pad,
|
|
|
|
element: &gst::Element,
|
|
|
|
buffer: gst::Buffer,
|
2019-01-11 23:45:05 +00:00
|
|
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
|
|
|
|
|
|
|
let mut adapter = self.adapter.lock().unwrap();
|
|
|
|
adapter.push(buffer);
|
|
|
|
|
|
|
|
let mut state = self.state.lock().unwrap();
|
|
|
|
loop {
|
|
|
|
match *state {
|
|
|
|
State::Stopped => unreachable!(),
|
|
|
|
State::NeedHeader => {
|
|
|
|
let header = match self.find_header(element, &mut *adapter) {
|
|
|
|
Ok(header) => header,
|
|
|
|
Err(_) => {
|
|
|
|
gst_trace!(CAT, obj: element, "Need more data");
|
2019-01-11 23:45:05 +00:00
|
|
|
return Ok(gst::FlowSuccess::Ok);
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let skip = if header.offset < 9 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
header.offset - 9
|
|
|
|
};
|
|
|
|
|
|
|
|
*state = State::Skipping {
|
|
|
|
audio: header.audio,
|
|
|
|
video: header.video,
|
|
|
|
skip_left: skip,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
State::Skipping {
|
|
|
|
audio,
|
|
|
|
video,
|
|
|
|
skip_left: 0,
|
|
|
|
} => {
|
|
|
|
*state = State::Streaming(StreamingState::new(audio, video));
|
|
|
|
}
|
|
|
|
State::Skipping {
|
|
|
|
ref mut skip_left, ..
|
|
|
|
} => {
|
|
|
|
let avail = adapter.available();
|
|
|
|
if avail == 0 {
|
|
|
|
gst_trace!(CAT, obj: element, "Need more data");
|
2019-01-11 23:45:05 +00:00
|
|
|
return Ok(gst::FlowSuccess::Ok);
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
|
|
|
let skip = cmp::min(avail, *skip_left as usize);
|
|
|
|
adapter.flush(skip);
|
|
|
|
*skip_left -= skip as u32;
|
|
|
|
}
|
|
|
|
State::Streaming(ref mut sstate) => {
|
|
|
|
let res = sstate.handle_tag(element, &mut *adapter);
|
|
|
|
|
|
|
|
match res {
|
|
|
|
Ok(None) => {
|
|
|
|
gst_trace!(CAT, obj: element, "Need more data");
|
2019-01-11 23:45:05 +00:00
|
|
|
return Ok(gst::FlowSuccess::Ok);
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
|
|
|
Ok(Some(events)) => {
|
|
|
|
drop(state);
|
|
|
|
drop(adapter);
|
|
|
|
|
2019-01-11 23:45:05 +00:00
|
|
|
self.handle_events(element, events)?;
|
2019-01-03 21:53:06 +00:00
|
|
|
|
|
|
|
adapter = self.adapter.lock().unwrap();
|
|
|
|
state = self.state.lock().unwrap();
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
element.post_error_message(&err);
|
2019-01-11 23:45:05 +00:00
|
|
|
return Err(gst::FlowError::Error);
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-04 10:58:59 +00:00
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn find_header(
|
|
|
|
&self,
|
|
|
|
element: &gst::Element,
|
|
|
|
adapter: &mut gst_base::UniqueAdapter,
|
|
|
|
) -> Result<flavors::Header, ()> {
|
|
|
|
while adapter.available() >= 9 {
|
|
|
|
let data = adapter.map(9).unwrap();
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-02-06 12:48:50 +00:00
|
|
|
if let Ok((_, header)) = flavors::header(&*data) {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_debug!(CAT, obj: element, "Found FLV header: {:?}", header);
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(9);
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
return Ok(header);
|
|
|
|
}
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
drop(data);
|
|
|
|
adapter.flush(1);
|
|
|
|
}
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
Err(())
|
|
|
|
}
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn handle_events(
|
|
|
|
&self,
|
|
|
|
element: &gst::Element,
|
|
|
|
events: SmallVec<[Event; 4]>,
|
|
|
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
|
|
|
for event in events {
|
|
|
|
match event {
|
|
|
|
Event::StreamChanged(stream, caps) => {
|
|
|
|
let pad = match stream {
|
|
|
|
Stream::Audio => {
|
|
|
|
let mut audio_srcpad = self.audio_srcpad.lock().unwrap();
|
|
|
|
if let Some(ref srcpad) = *audio_srcpad {
|
|
|
|
srcpad.clone()
|
|
|
|
} else {
|
|
|
|
let srcpad = self.create_srcpad(element, "audio", &caps);
|
|
|
|
*audio_srcpad = Some(srcpad.clone());
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
srcpad
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Stream::Video => {
|
|
|
|
let mut video_srcpad = self.video_srcpad.lock().unwrap();
|
|
|
|
if let Some(ref srcpad) = *video_srcpad {
|
|
|
|
srcpad.clone()
|
|
|
|
} else {
|
|
|
|
let srcpad = self.create_srcpad(element, "video", &caps);
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
*video_srcpad = Some(srcpad.clone());
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
srcpad
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pad.push_event(gst::Event::new_caps(&caps).build());
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
Event::Buffer(stream, buffer) => {
|
|
|
|
let pad = match stream {
|
|
|
|
Stream::Audio => {
|
|
|
|
self.audio_srcpad.lock().unwrap().as_ref().map(Clone::clone)
|
|
|
|
}
|
|
|
|
Stream::Video => {
|
|
|
|
self.video_srcpad.lock().unwrap().as_ref().map(Clone::clone)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(pad) = pad {
|
|
|
|
let res = pad.push(buffer);
|
|
|
|
gst_trace!(
|
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Pushing buffer for stream {:?} returned {:?}",
|
|
|
|
stream,
|
|
|
|
res
|
|
|
|
);
|
|
|
|
|
|
|
|
self.flow_combiner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2019-01-11 23:45:05 +00:00
|
|
|
.update_pad_flow(&pad, res)?;
|
2016-12-05 17:47:10 +00:00
|
|
|
}
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
Event::HaveAllStreams => {
|
|
|
|
element.no_more_pads();
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
Ok(gst::FlowSuccess::Ok)
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn create_srcpad(&self, element: &gst::Element, name: &str, caps: &gst::Caps) -> gst::Pad {
|
|
|
|
let templ = element.get_element_class().get_pad_template(name).unwrap();
|
|
|
|
let srcpad = gst::Pad::new_from_template(&templ, name);
|
|
|
|
|
|
|
|
srcpad.set_event_function(|pad, parent, event| {
|
|
|
|
FlvDemux::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| false,
|
|
|
|
|demux, element| demux.src_event(pad, element, event),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
srcpad.set_query_function(|pad, parent, query| {
|
|
|
|
FlvDemux::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| false,
|
|
|
|
|demux, element| demux.src_query(pad, element, query),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
srcpad.set_active(true).unwrap();
|
|
|
|
|
|
|
|
let full_stream_id = srcpad.create_stream_id(element, name).unwrap();
|
|
|
|
// FIXME group id
|
|
|
|
srcpad.push_event(gst::Event::new_stream_start(&full_stream_id).build());
|
|
|
|
srcpad.push_event(gst::Event::new_caps(&caps).build());
|
|
|
|
|
|
|
|
// FIXME proper segment handling
|
|
|
|
let segment = gst::FormattedSegment::<gst::ClockTime>::default();
|
|
|
|
srcpad.push_event(gst::Event::new_segment(&segment).build());
|
|
|
|
|
|
|
|
self.flow_combiner.lock().unwrap().add_pad(&srcpad);
|
|
|
|
|
|
|
|
element.add_pad(&srcpad).unwrap();
|
|
|
|
|
|
|
|
srcpad
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
impl StreamingState {
|
|
|
|
fn new(audio: bool, video: bool) -> StreamingState {
|
|
|
|
StreamingState {
|
|
|
|
audio: None,
|
|
|
|
expect_audio: audio,
|
|
|
|
video: None,
|
|
|
|
expect_video: video,
|
|
|
|
got_all_streams: false,
|
|
|
|
last_position: gst::CLOCK_TIME_NONE,
|
|
|
|
metadata: None,
|
|
|
|
aac_sequence_header: None,
|
|
|
|
avc_sequence_header: None,
|
2016-12-03 22:57:42 +00:00
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn handle_tag(
|
|
|
|
&mut self,
|
|
|
|
element: &gst::Element,
|
|
|
|
adapter: &mut gst_base::UniqueAdapter,
|
|
|
|
) -> Result<Option<SmallVec<[Event; 4]>>, gst::ErrorMessage> {
|
|
|
|
if adapter.available() < 15 {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
let data = adapter.map(15).unwrap();
|
|
|
|
|
|
|
|
match nom::be_u32(&data[0..4]) {
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(_) => unreachable!(),
|
|
|
|
Ok((_, previous_size)) => {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_trace!(CAT, obj: element, "Previous tag size {}", previous_size);
|
|
|
|
// Nothing to do here, we just consume it for now
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let tag_header = match flavors::tag_header(&data[4..]) {
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
|
2019-01-14 20:09:46 +00:00
|
|
|
return Err(gst_error_msg!(
|
|
|
|
gst::StreamError::Demux,
|
|
|
|
["Invalid tag header: {:?}", err]
|
|
|
|
));
|
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Incomplete(_)) => unreachable!(),
|
|
|
|
Ok((_, tag_header)) => tag_header,
|
2019-01-03 21:53:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
gst_trace!(CAT, obj: element, "Parsed tag header {:?}", tag_header);
|
|
|
|
|
|
|
|
drop(data);
|
|
|
|
|
|
|
|
if adapter.available() < (15 + tag_header.data_size) as usize {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
adapter.flush(15);
|
|
|
|
|
|
|
|
match tag_header.tag_type {
|
|
|
|
flavors::TagType::Script => {
|
|
|
|
gst_trace!(CAT, obj: element, "Found script tag");
|
|
|
|
|
|
|
|
self.handle_script_tag(element, &tag_header, adapter)
|
|
|
|
}
|
|
|
|
flavors::TagType::Audio => {
|
|
|
|
gst_trace!(CAT, obj: element, "Found audio tag");
|
|
|
|
|
|
|
|
self.handle_audio_tag(element, &tag_header, adapter)
|
|
|
|
}
|
|
|
|
flavors::TagType::Video => {
|
|
|
|
gst_trace!(CAT, obj: element, "Found video tag");
|
|
|
|
|
|
|
|
self.handle_video_tag(element, &tag_header, adapter)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.map(Option::Some)
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2017-07-31 13:36:35 +00:00
|
|
|
fn handle_script_tag(
|
|
|
|
&mut self,
|
2019-01-03 21:53:06 +00:00
|
|
|
element: &gst::Element,
|
2017-07-31 13:36:35 +00:00
|
|
|
tag_header: &flavors::TagHeader,
|
2019-01-03 21:53:06 +00:00
|
|
|
adapter: &mut gst_base::UniqueAdapter,
|
|
|
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
|
|
|
assert!(adapter.available() >= tag_header.data_size as usize);
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let mut events = SmallVec::new();
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let data = adapter.map(tag_header.data_size as usize).unwrap();
|
2016-12-04 18:24:44 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
match flavors::script_data(&*data) {
|
2019-02-06 12:48:50 +00:00
|
|
|
Ok((_, ref script_data)) if script_data.name == "onMetaData" => {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_trace!(CAT, obj: element, "Got script tag: {:?}", script_data);
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2016-12-04 18:24:44 +00:00
|
|
|
let metadata = Metadata::new(script_data);
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_debug!(CAT, obj: element, "Got metadata: {:?}", metadata);
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let audio_changed = self
|
2017-04-12 13:44:34 +00:00
|
|
|
.audio
|
2016-12-04 18:24:44 +00:00
|
|
|
.as_mut()
|
|
|
|
.map(|a| a.update_with_metadata(&metadata))
|
|
|
|
.unwrap_or(false);
|
2019-01-03 21:53:06 +00:00
|
|
|
let video_changed = self
|
2017-04-12 13:44:34 +00:00
|
|
|
.video
|
2016-12-04 18:24:44 +00:00
|
|
|
.as_mut()
|
|
|
|
.map(|v| v.update_with_metadata(&metadata))
|
|
|
|
.unwrap_or(false);
|
2019-01-03 21:53:06 +00:00
|
|
|
self.metadata = Some(metadata);
|
2016-12-04 18:24:44 +00:00
|
|
|
|
|
|
|
if audio_changed || video_changed {
|
|
|
|
if audio_changed {
|
2019-01-03 21:53:06 +00:00
|
|
|
if let Some(caps) = self.audio.as_ref().and_then(|a| a.to_caps()) {
|
|
|
|
events.push(Event::StreamChanged(Stream::Audio, caps));
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if video_changed {
|
2019-01-03 21:53:06 +00:00
|
|
|
if let Some(caps) = self.video.as_ref().and_then(|v| v.to_caps()) {
|
|
|
|
events.push(Event::StreamChanged(Stream::Video, caps));
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Ok((_, ref script_data)) => {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_trace!(CAT, obj: element, "Got script tag: {:?}", script_data);
|
2016-12-27 16:14:39 +00:00
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_error!(CAT, obj: element, "Error parsing script tag: {:?}", err);
|
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Incomplete(_)) => {
|
2016-12-04 18:24:44 +00:00
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
drop(data);
|
|
|
|
adapter.flush(tag_header.data_size as usize);
|
|
|
|
|
|
|
|
Ok(events)
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2017-07-31 13:36:35 +00:00
|
|
|
fn update_audio_stream(
|
|
|
|
&mut self,
|
2019-01-03 21:53:06 +00:00
|
|
|
element: &gst::Element,
|
2017-07-31 13:36:35 +00:00
|
|
|
data_header: &flavors::AudioDataHeader,
|
2019-01-03 21:53:06 +00:00
|
|
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
|
|
|
let mut events = SmallVec::new();
|
|
|
|
|
2017-09-16 17:35:01 +00:00
|
|
|
gst_trace!(
|
2019-01-03 21:53:06 +00:00
|
|
|
CAT,
|
|
|
|
obj: element,
|
2017-09-18 17:46:34 +00:00
|
|
|
"Got audio data header: {:?}",
|
2017-09-16 17:35:01 +00:00
|
|
|
data_header
|
|
|
|
);
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let new_audio_format =
|
|
|
|
AudioFormat::new(data_header, &self.metadata, &self.aac_sequence_header);
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.audio.as_ref() != Some(&new_audio_format) {
|
2017-09-16 17:35:01 +00:00
|
|
|
gst_debug!(
|
2019-01-03 21:53:06 +00:00
|
|
|
CAT,
|
|
|
|
obj: element,
|
2017-09-18 17:46:34 +00:00
|
|
|
"Got new audio format: {:?}",
|
2017-09-16 17:35:01 +00:00
|
|
|
new_audio_format
|
|
|
|
);
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2016-12-30 11:11:30 +00:00
|
|
|
let caps = new_audio_format.to_caps();
|
|
|
|
if let Some(caps) = caps {
|
2019-01-03 21:53:06 +00:00
|
|
|
self.audio = Some(new_audio_format);
|
|
|
|
events.push(Event::StreamChanged(Stream::Audio, caps));
|
2016-12-27 16:14:39 +00:00
|
|
|
} else {
|
2019-01-03 21:53:06 +00:00
|
|
|
self.audio = None;
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if (!self.expect_video || self.video != None) && self.audio != None && !self.got_all_streams
|
2017-07-31 13:36:35 +00:00
|
|
|
{
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_debug!(CAT, obj: element, "Have all expected streams now");
|
|
|
|
self.got_all_streams = true;
|
|
|
|
events.push(Event::HaveAllStreams);
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
Ok(events)
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn handle_aac_audio_packet_header(
|
2017-07-31 13:36:35 +00:00
|
|
|
&mut self,
|
2019-01-03 21:53:06 +00:00
|
|
|
element: &gst::Element,
|
2017-07-31 13:36:35 +00:00
|
|
|
tag_header: &flavors::TagHeader,
|
2019-01-03 21:53:06 +00:00
|
|
|
adapter: &mut gst_base::UniqueAdapter,
|
|
|
|
) -> Result<bool, gst::ErrorMessage> {
|
|
|
|
// Not big enough for the AAC packet header, ship!
|
|
|
|
if tag_header.data_size < 1 + 1 {
|
|
|
|
adapter.flush((tag_header.data_size - 1) as usize);
|
|
|
|
gst_warning!(
|
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Too small packet for AAC packet header {}",
|
|
|
|
tag_header.data_size
|
|
|
|
);
|
|
|
|
return Ok(true);
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let data = adapter.map(1).unwrap();
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
match flavors::aac_audio_packet_header(&*data) {
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
|
2019-01-14 20:09:46 +00:00
|
|
|
gst_error!(
|
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Invalid AAC audio packet header: {:?}",
|
|
|
|
err
|
|
|
|
);
|
|
|
|
drop(data);
|
|
|
|
adapter.flush((tag_header.data_size - 1) as usize);
|
2019-02-21 18:12:09 +00:00
|
|
|
Ok(true)
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Incomplete(_)) => unreachable!(),
|
|
|
|
Ok((_, header)) => {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_trace!(CAT, obj: element, "Got AAC packet header {:?}", header);
|
|
|
|
match header.packet_type {
|
|
|
|
flavors::AACPacketType::SequenceHeader => {
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(1);
|
|
|
|
let buffer = adapter
|
|
|
|
.take_buffer((tag_header.data_size - 1 - 1) as usize)
|
|
|
|
.unwrap();
|
|
|
|
gst_debug!(CAT, obj: element, "Got AAC sequence header {:?}", buffer,);
|
|
|
|
|
|
|
|
self.aac_sequence_header = Some(buffer);
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
flavors::AACPacketType::Raw => {
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(1);
|
|
|
|
Ok(false)
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_audio_tag(
|
|
|
|
&mut self,
|
|
|
|
element: &gst::Element,
|
|
|
|
tag_header: &flavors::TagHeader,
|
|
|
|
adapter: &mut gst_base::UniqueAdapter,
|
|
|
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
|
|
|
assert!(adapter.available() >= tag_header.data_size as usize);
|
|
|
|
|
|
|
|
let data = adapter.map(1).unwrap();
|
|
|
|
let data_header = match flavors::audio_data_header(&*data) {
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
|
2019-01-14 20:09:46 +00:00
|
|
|
gst_error!(CAT, obj: element, "Invalid audio data header: {:?}", err);
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(tag_header.data_size as usize);
|
|
|
|
return Ok(SmallVec::new());
|
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Incomplete(_)) => unreachable!(),
|
|
|
|
Ok((_, data_header)) => data_header,
|
2019-01-03 21:53:06 +00:00
|
|
|
};
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(1);
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let mut events = self.update_audio_stream(element, &data_header)?;
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
// AAC special case
|
|
|
|
if data_header.sound_format == flavors::SoundFormat::AAC
|
|
|
|
&& self.handle_aac_audio_packet_header(element, &tag_header, adapter)?
|
|
|
|
{
|
|
|
|
return Ok(events);
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let offset = match data_header.sound_format {
|
|
|
|
flavors::SoundFormat::AAC => 2,
|
|
|
|
_ => 1,
|
2017-01-05 22:46:59 +00:00
|
|
|
};
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if tag_header.data_size == offset {
|
|
|
|
return Ok(events);
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.audio == None {
|
|
|
|
adapter.flush((tag_header.data_size - offset) as usize);
|
|
|
|
return Ok(events);
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let mut buffer = adapter
|
|
|
|
.take_buffer((tag_header.data_size - offset) as usize)
|
2017-04-12 13:44:34 +00:00
|
|
|
.unwrap();
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2017-02-03 14:34:17 +00:00
|
|
|
{
|
|
|
|
let buffer = buffer.get_mut().unwrap();
|
2017-11-11 12:02:55 +00:00
|
|
|
buffer.set_pts(gst::ClockTime::from_mseconds(tag_header.timestamp as u64));
|
2017-02-03 14:34:17 +00:00
|
|
|
}
|
|
|
|
|
2017-09-16 17:35:01 +00:00
|
|
|
gst_trace!(
|
2019-01-03 21:53:06 +00:00
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Outputting audio buffer {:?} for tag {:?}",
|
2017-07-31 13:36:35 +00:00
|
|
|
buffer,
|
|
|
|
tag_header,
|
|
|
|
);
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
self.update_position(&buffer);
|
|
|
|
|
|
|
|
events.push(Event::Buffer(Stream::Audio, buffer));
|
|
|
|
|
|
|
|
Ok(events)
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2017-07-31 13:36:35 +00:00
|
|
|
fn update_video_stream(
|
|
|
|
&mut self,
|
2019-01-03 21:53:06 +00:00
|
|
|
element: &gst::Element,
|
2017-07-31 13:36:35 +00:00
|
|
|
data_header: &flavors::VideoDataHeader,
|
2019-01-03 21:53:06 +00:00
|
|
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
|
|
|
let mut events = SmallVec::new();
|
|
|
|
|
2017-09-16 17:35:01 +00:00
|
|
|
gst_trace!(
|
2019-01-03 21:53:06 +00:00
|
|
|
CAT,
|
|
|
|
obj: element,
|
2017-09-18 17:46:34 +00:00
|
|
|
"Got video data header: {:?}",
|
2017-09-16 17:35:01 +00:00
|
|
|
data_header
|
|
|
|
);
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let new_video_format =
|
|
|
|
VideoFormat::new(data_header, &self.metadata, &self.avc_sequence_header);
|
2016-12-03 22:57:42 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.video.as_ref() != Some(&new_video_format) {
|
2017-09-16 17:35:01 +00:00
|
|
|
gst_debug!(
|
2019-01-03 21:53:06 +00:00
|
|
|
CAT,
|
|
|
|
obj: element,
|
2017-09-18 17:46:34 +00:00
|
|
|
"Got new video format: {:?}",
|
2017-09-16 17:35:01 +00:00
|
|
|
new_video_format
|
|
|
|
);
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2016-12-30 11:11:30 +00:00
|
|
|
let caps = new_video_format.to_caps();
|
|
|
|
if let Some(caps) = caps {
|
2019-01-03 21:53:06 +00:00
|
|
|
self.video = Some(new_video_format);
|
|
|
|
events.push(Event::StreamChanged(Stream::Video, caps));
|
2016-12-27 16:14:39 +00:00
|
|
|
} else {
|
2019-01-03 21:53:06 +00:00
|
|
|
self.video = None;
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if (!self.expect_audio || self.audio != None) && self.video != None && !self.got_all_streams
|
2017-07-31 13:36:35 +00:00
|
|
|
{
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_debug!(CAT, obj: element, "Have all expected streams now");
|
|
|
|
self.got_all_streams = true;
|
|
|
|
events.push(Event::HaveAllStreams);
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
Ok(events)
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn handle_avc_video_packet_header(
|
2017-07-31 13:36:35 +00:00
|
|
|
&mut self,
|
2019-01-03 21:53:06 +00:00
|
|
|
element: &gst::Element,
|
2017-07-31 13:36:35 +00:00
|
|
|
tag_header: &flavors::TagHeader,
|
2019-01-03 21:53:06 +00:00
|
|
|
adapter: &mut gst_base::UniqueAdapter,
|
|
|
|
) -> Result<Option<i32>, gst::ErrorMessage> {
|
|
|
|
// Not big enough for the AVC packet header, skip!
|
|
|
|
if tag_header.data_size < 1 + 4 {
|
|
|
|
adapter.flush((tag_header.data_size - 1) as usize);
|
|
|
|
gst_warning!(
|
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Too small packet for AVC packet header {}",
|
|
|
|
tag_header.data_size
|
|
|
|
);
|
|
|
|
return Ok(None);
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let data = adapter.map(4).unwrap();
|
|
|
|
match flavors::avc_video_packet_header(&*data) {
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
|
2019-01-14 20:09:46 +00:00
|
|
|
gst_error!(
|
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Invalid AVC video packet header: {:?}",
|
|
|
|
err
|
|
|
|
);
|
|
|
|
drop(data);
|
|
|
|
adapter.flush((tag_header.data_size - 1) as usize);
|
2019-02-21 18:12:09 +00:00
|
|
|
Ok(None)
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Incomplete(_)) => unreachable!(),
|
|
|
|
Ok((_, header)) => {
|
2019-01-03 21:53:06 +00:00
|
|
|
gst_trace!(CAT, obj: element, "Got AVC packet header {:?}", header);
|
|
|
|
match header.packet_type {
|
|
|
|
flavors::AVCPacketType::SequenceHeader => {
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(4);
|
|
|
|
let buffer = adapter
|
|
|
|
.take_buffer((tag_header.data_size - 1 - 4) as usize)
|
|
|
|
.unwrap();
|
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Got AVC sequence header {:?} of size {}",
|
|
|
|
buffer,
|
|
|
|
tag_header.data_size - 1 - 4
|
|
|
|
);
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
self.avc_sequence_header = Some(buffer);
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
flavors::AVCPacketType::NALU => {
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(4);
|
|
|
|
Ok(Some(header.composition_time))
|
|
|
|
}
|
|
|
|
flavors::AVCPacketType::EndOfSequence => {
|
|
|
|
// Skip
|
|
|
|
drop(data);
|
|
|
|
adapter.flush((tag_header.data_size - 1) as usize);
|
|
|
|
Ok(None)
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_video_tag(
|
|
|
|
&mut self,
|
|
|
|
element: &gst::Element,
|
|
|
|
tag_header: &flavors::TagHeader,
|
|
|
|
adapter: &mut gst_base::UniqueAdapter,
|
|
|
|
) -> Result<SmallVec<[Event; 4]>, gst::ErrorMessage> {
|
|
|
|
assert!(adapter.available() >= tag_header.data_size as usize);
|
|
|
|
|
|
|
|
let data = adapter.map(1).unwrap();
|
|
|
|
let data_header = match flavors::video_data_header(&*data) {
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
|
2019-01-14 20:09:46 +00:00
|
|
|
gst_error!(CAT, obj: element, "Invalid video data header: {:?}", err);
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(tag_header.data_size as usize);
|
|
|
|
return Ok(SmallVec::new());
|
|
|
|
}
|
2019-02-06 12:48:50 +00:00
|
|
|
Err(nom::Err::Incomplete(_)) => unreachable!(),
|
|
|
|
Ok((_, data_header)) => data_header,
|
2019-01-03 21:53:06 +00:00
|
|
|
};
|
|
|
|
drop(data);
|
|
|
|
adapter.flush(1);
|
|
|
|
|
|
|
|
let mut events = self.update_video_stream(element, &data_header)?;
|
|
|
|
|
|
|
|
// AVC/H264 special case
|
|
|
|
let cts = if data_header.codec_id == flavors::CodecId::H264 {
|
|
|
|
match self.handle_avc_video_packet_header(element, tag_header, adapter)? {
|
|
|
|
Some(cts) => cts,
|
|
|
|
None => {
|
|
|
|
return Ok(events);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let offset = match data_header.codec_id {
|
|
|
|
flavors::CodecId::H264 => 5,
|
|
|
|
_ => 1,
|
|
|
|
};
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if tag_header.data_size == offset {
|
|
|
|
return Ok(events);
|
2017-01-05 22:46:59 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.video == None {
|
|
|
|
adapter.flush((tag_header.data_size - offset) as usize);
|
|
|
|
return Ok(events);
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let is_keyframe = data_header.frame_type == flavors::FrameType::Key;
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let skip = match data_header.codec_id {
|
2017-07-31 13:36:35 +00:00
|
|
|
flavors::CodecId::VP6 | flavors::CodecId::VP6A => 1,
|
2017-01-05 22:46:59 +00:00
|
|
|
_ => 0,
|
2016-11-24 14:29:43 +00:00
|
|
|
};
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if skip > 0 {
|
|
|
|
adapter.flush(skip as usize);
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if tag_header.data_size == offset + skip {
|
|
|
|
return Ok(events);
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let mut buffer = adapter
|
|
|
|
.take_buffer((tag_header.data_size - offset - skip) as usize)
|
2017-04-12 13:44:34 +00:00
|
|
|
.unwrap();
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2017-02-03 14:34:17 +00:00
|
|
|
{
|
|
|
|
let buffer = buffer.get_mut().unwrap();
|
|
|
|
if !is_keyframe {
|
2017-10-01 14:19:43 +00:00
|
|
|
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
|
2017-02-03 14:34:17 +00:00
|
|
|
}
|
2017-11-11 12:02:55 +00:00
|
|
|
buffer.set_dts(gst::ClockTime::from_mseconds(tag_header.timestamp as u64));
|
2017-02-03 14:34:17 +00:00
|
|
|
|
|
|
|
// Prevent negative numbers
|
|
|
|
let pts = if cts < 0 && tag_header.timestamp < (-cts) as u32 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
((tag_header.timestamp as i64) + (cts as i64)) as u64
|
|
|
|
};
|
2017-11-11 12:02:55 +00:00
|
|
|
buffer.set_pts(gst::ClockTime::from_mseconds(pts));
|
2017-02-03 14:34:17 +00:00
|
|
|
}
|
2017-01-05 22:46:59 +00:00
|
|
|
|
2017-09-16 17:35:01 +00:00
|
|
|
gst_trace!(
|
2019-01-03 21:53:06 +00:00
|
|
|
CAT,
|
|
|
|
obj: element,
|
|
|
|
"Outputting video buffer {:?} for tag {:?}, keyframe: {}",
|
2017-07-31 13:36:35 +00:00
|
|
|
buffer,
|
|
|
|
tag_header,
|
|
|
|
is_keyframe
|
|
|
|
);
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
self.update_position(&buffer);
|
|
|
|
|
|
|
|
events.push(Event::Buffer(Stream::Video, buffer));
|
|
|
|
|
|
|
|
Ok(events)
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn update_position(&mut self, buffer: &gst::Buffer) {
|
|
|
|
if buffer.get_pts() != gst::CLOCK_TIME_NONE {
|
|
|
|
let pts = buffer.get_pts();
|
|
|
|
self.last_position = self
|
|
|
|
.last_position
|
|
|
|
.map(|last| cmp::max(last.into(), pts))
|
|
|
|
.unwrap_or(pts);
|
|
|
|
} else if buffer.get_dts() != gst::CLOCK_TIME_NONE {
|
|
|
|
let dts = buffer.get_dts();
|
|
|
|
self.last_position = self
|
|
|
|
.last_position
|
|
|
|
.map(|last| cmp::max(last.into(), dts))
|
|
|
|
.unwrap_or(dts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
// Ignores bitrate
|
|
|
|
impl PartialEq for AudioFormat {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.format.eq(&other.format)
|
|
|
|
&& self.rate.eq(&other.rate)
|
|
|
|
&& self.width.eq(&other.width)
|
|
|
|
&& self.channels.eq(&other.channels)
|
|
|
|
&& self.aac_sequence_header.eq(&other.aac_sequence_header)
|
|
|
|
}
|
|
|
|
}
|
2016-12-04 10:58:59 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
impl AudioFormat {
|
|
|
|
fn new(
|
|
|
|
data_header: &flavors::AudioDataHeader,
|
|
|
|
metadata: &Option<Metadata>,
|
|
|
|
aac_sequence_header: &Option<gst::Buffer>,
|
|
|
|
) -> AudioFormat {
|
|
|
|
let numeric_rate = match (data_header.sound_format, data_header.sound_rate) {
|
|
|
|
(flavors::SoundFormat::NELLYMOSER_16KHZ_MONO, _) => 16_000,
|
2019-02-08 13:12:19 +00:00
|
|
|
(flavors::SoundFormat::NELLYMOSER_8KHZ_MONO, _)
|
|
|
|
| (flavors::SoundFormat::PCM_ALAW, _)
|
|
|
|
| (flavors::SoundFormat::PCM_ULAW, _)
|
|
|
|
| (flavors::SoundFormat::MP3_8KHZ, _) => 8_000,
|
2019-01-03 21:53:06 +00:00
|
|
|
(flavors::SoundFormat::SPEEX, _) => 16_000,
|
|
|
|
(_, flavors::SoundRate::_5_5KHZ) => 5_512,
|
|
|
|
(_, flavors::SoundRate::_11KHZ) => 11_025,
|
|
|
|
(_, flavors::SoundRate::_22KHZ) => 22_050,
|
|
|
|
(_, flavors::SoundRate::_44KHZ) => 44_100,
|
|
|
|
};
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let numeric_width = match data_header.sound_size {
|
|
|
|
flavors::SoundSize::Snd8bit => 8,
|
|
|
|
flavors::SoundSize::Snd16bit => 16,
|
|
|
|
};
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let numeric_channels = match data_header.sound_type {
|
|
|
|
flavors::SoundType::SndMono => 1,
|
|
|
|
flavors::SoundType::SndStereo => 2,
|
|
|
|
};
|
2016-12-03 22:57:42 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
AudioFormat {
|
|
|
|
format: data_header.sound_format,
|
|
|
|
rate: numeric_rate,
|
|
|
|
width: numeric_width,
|
|
|
|
channels: numeric_channels,
|
|
|
|
bitrate: metadata.as_ref().and_then(|m| m.audio_bitrate),
|
|
|
|
aac_sequence_header: aac_sequence_header.clone(),
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 22:57:42 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn update_with_metadata(&mut self, metadata: &Metadata) -> bool {
|
|
|
|
if self.bitrate != metadata.audio_bitrate {
|
|
|
|
self.bitrate = metadata.audio_bitrate;
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_caps(&self) -> Option<gst::Caps> {
|
|
|
|
let mut caps = match self.format {
|
|
|
|
flavors::SoundFormat::MP3 | flavors::SoundFormat::MP3_8KHZ => Some(
|
|
|
|
gst::Caps::new_simple("audio/mpeg", &[("mpegversion", &1i32), ("layer", &3i32)]),
|
|
|
|
),
|
|
|
|
flavors::SoundFormat::PCM_NE | flavors::SoundFormat::PCM_LE => {
|
|
|
|
if self.rate != 0 && self.channels != 0 {
|
|
|
|
// Assume little-endian for "PCM_NE", it's probably more common and we have no
|
|
|
|
// way to know what the endianness of the system creating the stream was
|
|
|
|
Some(gst::Caps::new_simple(
|
|
|
|
"audio/x-raw",
|
|
|
|
&[
|
|
|
|
("layout", &"interleaved"),
|
|
|
|
("format", &if self.width == 8 { "U8" } else { "S16LE" }),
|
|
|
|
],
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
flavors::SoundFormat::ADPCM => Some(gst::Caps::new_simple(
|
|
|
|
"audio/x-adpcm",
|
|
|
|
&[("layout", &"swf")],
|
|
|
|
)),
|
|
|
|
flavors::SoundFormat::NELLYMOSER_16KHZ_MONO
|
|
|
|
| flavors::SoundFormat::NELLYMOSER_8KHZ_MONO
|
|
|
|
| flavors::SoundFormat::NELLYMOSER => {
|
|
|
|
Some(gst::Caps::new_simple("audio/x-nellymoser", &[]))
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
flavors::SoundFormat::PCM_ALAW => Some(gst::Caps::new_simple("audio/x-alaw", &[])),
|
|
|
|
flavors::SoundFormat::PCM_ULAW => Some(gst::Caps::new_simple("audio/x-mulaw", &[])),
|
|
|
|
flavors::SoundFormat::AAC => self.aac_sequence_header.as_ref().map(|header| {
|
|
|
|
gst::Caps::new_simple(
|
|
|
|
"audio/mpeg",
|
|
|
|
&[
|
|
|
|
("mpegversion", &4i32),
|
|
|
|
("framed", &true),
|
|
|
|
("stream-format", &"raw"),
|
|
|
|
("codec_data", &header),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
flavors::SoundFormat::SPEEX => {
|
|
|
|
use crate::bytes::*;
|
|
|
|
use std::io::{Cursor, Write};
|
2016-12-03 22:57:42 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let header = {
|
|
|
|
let header_size = 80;
|
|
|
|
let mut data = Cursor::new(Vec::with_capacity(header_size));
|
|
|
|
data.write_all(b"Speex 1.1.12").unwrap();
|
|
|
|
data.write_all(&[0; 14]).unwrap();
|
|
|
|
data.write_u32le(1).unwrap(); // version
|
|
|
|
data.write_u32le(80).unwrap(); // header size
|
|
|
|
data.write_u32le(16_000).unwrap(); // sample rate
|
|
|
|
data.write_u32le(1).unwrap(); // mode = wideband
|
|
|
|
data.write_u32le(4).unwrap(); // mode bitstream version
|
|
|
|
data.write_u32le(1).unwrap(); // channels
|
|
|
|
data.write_i32le(-1).unwrap(); // bitrate
|
|
|
|
data.write_u32le(0x50).unwrap(); // frame size
|
|
|
|
data.write_u32le(0).unwrap(); // VBR
|
|
|
|
data.write_u32le(1).unwrap(); // frames per packet
|
|
|
|
data.write_u32le(0).unwrap(); // extra headers
|
|
|
|
data.write_u32le(0).unwrap(); // reserved 1
|
|
|
|
data.write_u32le(0).unwrap(); // reserved 2
|
2016-12-03 22:57:42 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
assert_eq!(data.position() as usize, header_size);
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
data.into_inner()
|
2016-11-24 14:29:43 +00:00
|
|
|
};
|
2019-01-03 21:53:06 +00:00
|
|
|
let header = gst::Buffer::from_mut_slice(header);
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
let comment = {
|
|
|
|
let comment_size = 4 + 7 /* nothing */ + 4 + 1;
|
|
|
|
let mut data = Cursor::new(Vec::with_capacity(comment_size));
|
|
|
|
data.write_u32le(7).unwrap(); // length of "nothing"
|
|
|
|
data.write_all(b"nothing").unwrap(); // "vendor" string
|
|
|
|
data.write_u32le(0).unwrap(); // number of elements
|
|
|
|
data.write_u8(1).unwrap();
|
2016-12-27 16:14:39 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
assert_eq!(data.position() as usize, comment_size);
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
data.into_inner()
|
2016-12-03 22:57:42 +00:00
|
|
|
};
|
2019-01-03 21:53:06 +00:00
|
|
|
let comment = gst::Buffer::from_mut_slice(comment);
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
Some(gst::Caps::new_simple(
|
|
|
|
"audio/x-speex",
|
|
|
|
&[("streamheader", &gst::Array::new(&[&header, &comment]))],
|
|
|
|
))
|
|
|
|
}
|
|
|
|
flavors::SoundFormat::DEVICE_SPECIFIC => {
|
|
|
|
// Nobody knows
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.rate != 0 {
|
|
|
|
if let Some(ref mut caps) = caps.as_mut() {
|
|
|
|
caps.get_mut()
|
|
|
|
.unwrap()
|
|
|
|
.set_simple(&[("rate", &(self.rate as i32))])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if self.channels != 0 {
|
|
|
|
if let Some(ref mut caps) = caps.as_mut() {
|
|
|
|
caps.get_mut()
|
|
|
|
.unwrap()
|
|
|
|
.set_simple(&[("channels", &(self.channels as i32))])
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
|
|
|
|
caps
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
// Ignores bitrate
|
|
|
|
impl PartialEq for VideoFormat {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.format.eq(&other.format)
|
|
|
|
&& self.width.eq(&other.width)
|
|
|
|
&& self.height.eq(&other.height)
|
|
|
|
&& self.pixel_aspect_ratio.eq(&other.pixel_aspect_ratio)
|
|
|
|
&& self.framerate.eq(&other.framerate)
|
|
|
|
&& self.avc_sequence_header.eq(&other.avc_sequence_header)
|
|
|
|
}
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
impl VideoFormat {
|
|
|
|
fn new(
|
|
|
|
data_header: &flavors::VideoDataHeader,
|
|
|
|
metadata: &Option<Metadata>,
|
|
|
|
avc_sequence_header: &Option<gst::Buffer>,
|
|
|
|
) -> VideoFormat {
|
|
|
|
VideoFormat {
|
|
|
|
format: data_header.codec_id,
|
|
|
|
width: metadata.as_ref().and_then(|m| m.video_width),
|
|
|
|
height: metadata.as_ref().and_then(|m| m.video_height),
|
|
|
|
pixel_aspect_ratio: metadata.as_ref().and_then(|m| m.video_pixel_aspect_ratio),
|
|
|
|
framerate: metadata.as_ref().and_then(|m| m.video_framerate),
|
|
|
|
bitrate: metadata.as_ref().and_then(|m| m.video_bitrate),
|
|
|
|
avc_sequence_header: avc_sequence_header.clone(),
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn update_with_metadata(&mut self, metadata: &Metadata) -> bool {
|
|
|
|
let mut changed = false;
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.width != metadata.video_width {
|
|
|
|
self.width = metadata.video_width;
|
|
|
|
changed = true;
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.height != metadata.video_height {
|
|
|
|
self.height = metadata.video_height;
|
|
|
|
changed = true;
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.pixel_aspect_ratio != metadata.video_pixel_aspect_ratio {
|
|
|
|
self.pixel_aspect_ratio = metadata.video_pixel_aspect_ratio;
|
|
|
|
changed = true;
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.framerate != metadata.video_framerate {
|
|
|
|
self.framerate = metadata.video_framerate;
|
|
|
|
changed = true;
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
if self.bitrate != metadata.video_bitrate {
|
|
|
|
self.bitrate = metadata.video_bitrate;
|
|
|
|
changed = true;
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
changed
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
fn to_caps(&self) -> Option<gst::Caps> {
|
|
|
|
let mut caps = match self.format {
|
|
|
|
flavors::CodecId::SORENSON_H263 => Some(gst::Caps::new_simple(
|
|
|
|
"video/x-flash-video",
|
|
|
|
&[("flvversion", &1i32)],
|
|
|
|
)),
|
|
|
|
flavors::CodecId::SCREEN => Some(gst::Caps::new_simple("video/x-flash-screen", &[])),
|
|
|
|
flavors::CodecId::VP6 => Some(gst::Caps::new_simple("video/x-vp6-flash", &[])),
|
|
|
|
flavors::CodecId::VP6A => Some(gst::Caps::new_simple("video/x-vp6-flash-alpha", &[])),
|
|
|
|
flavors::CodecId::SCREEN2 => Some(gst::Caps::new_simple("video/x-flash-screen2", &[])),
|
|
|
|
flavors::CodecId::H264 => self.avc_sequence_header.as_ref().map(|header| {
|
|
|
|
gst::Caps::new_simple(
|
|
|
|
"video/x-h264",
|
|
|
|
&[("stream-format", &"avc"), ("codec_data", &header)],
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
flavors::CodecId::H263 => Some(gst::Caps::new_simple("video/x-h263", &[])),
|
|
|
|
flavors::CodecId::MPEG4Part2 => Some(gst::Caps::new_simple(
|
|
|
|
"video/mpeg",
|
|
|
|
&[("mpegversion", &4i32), ("systemstream", &false)],
|
|
|
|
)),
|
|
|
|
flavors::CodecId::JPEG => {
|
|
|
|
// Unused according to spec
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let (Some(width), Some(height)) = (self.width, self.height) {
|
|
|
|
if let Some(ref mut caps) = caps.as_mut() {
|
|
|
|
caps.get_mut()
|
|
|
|
.unwrap()
|
|
|
|
.set_simple(&[("width", &(width as i32)), ("height", &(height as i32))])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(par) = self.pixel_aspect_ratio {
|
|
|
|
if *par.numer() != 0 && par.numer() != par.denom() {
|
|
|
|
if let Some(ref mut caps) = caps.as_mut() {
|
|
|
|
caps.get_mut().unwrap().set_simple(&[(
|
|
|
|
"pixel-aspect-ratio",
|
|
|
|
&gst::Fraction::new(*par.numer(), *par.denom()),
|
|
|
|
)])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(fps) = self.framerate {
|
|
|
|
if *fps.numer() != 0 {
|
|
|
|
if let Some(ref mut caps) = caps.as_mut() {
|
|
|
|
caps.get_mut().unwrap().set_simple(&[(
|
|
|
|
"framerate",
|
|
|
|
&gst::Fraction::new(*fps.numer(), *fps.denom()),
|
|
|
|
)])
|
|
|
|
}
|
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
caps
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
}
|
2016-11-24 14:29:43 +00:00
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
impl Metadata {
|
|
|
|
fn new(script_data: &flavors::ScriptData) -> Metadata {
|
|
|
|
assert_eq!(script_data.name, "onMetaData");
|
|
|
|
|
|
|
|
let mut metadata = Metadata::default();
|
|
|
|
|
|
|
|
let args = match script_data.arguments {
|
|
|
|
flavors::ScriptDataValue::Object(ref objects)
|
|
|
|
| flavors::ScriptDataValue::ECMAArray(ref objects) => objects,
|
|
|
|
_ => return metadata,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut par_n = None;
|
|
|
|
let mut par_d = None;
|
|
|
|
|
|
|
|
for arg in args {
|
|
|
|
match (arg.name, &arg.data) {
|
|
|
|
("duration", &flavors::ScriptDataValue::Number(duration)) => {
|
|
|
|
metadata.duration = ((duration * 1000.0 * 1000.0 * 1000.0) as u64).into();
|
|
|
|
}
|
|
|
|
("creationdate", &flavors::ScriptDataValue::String(date)) => {
|
|
|
|
metadata.creation_date = Some(String::from(date));
|
|
|
|
}
|
|
|
|
("creator", &flavors::ScriptDataValue::String(creator)) => {
|
|
|
|
metadata.creator = Some(String::from(creator));
|
|
|
|
}
|
|
|
|
("title", &flavors::ScriptDataValue::String(title)) => {
|
|
|
|
metadata.title = Some(String::from(title));
|
|
|
|
}
|
|
|
|
("metadatacreator", &flavors::ScriptDataValue::String(creator)) => {
|
|
|
|
metadata.metadata_creator = Some(String::from(creator));
|
|
|
|
}
|
|
|
|
("audiodatarate", &flavors::ScriptDataValue::Number(datarate)) => {
|
|
|
|
metadata.audio_bitrate = Some((datarate * 1024.0) as u32);
|
|
|
|
}
|
|
|
|
("width", &flavors::ScriptDataValue::Number(width)) => {
|
|
|
|
metadata.video_width = Some(width as u32);
|
|
|
|
}
|
|
|
|
("height", &flavors::ScriptDataValue::Number(height)) => {
|
|
|
|
metadata.video_height = Some(height as u32);
|
|
|
|
}
|
|
|
|
("framerate", &flavors::ScriptDataValue::Number(framerate)) if framerate >= 0.0 => {
|
|
|
|
if let Some(framerate) = Rational32::approximate_float(framerate) {
|
|
|
|
metadata.video_framerate = Some(framerate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
("AspectRatioX", &flavors::ScriptDataValue::Number(par_x)) if par_x > 0.0 => {
|
|
|
|
par_n = Some(par_x as i32);
|
|
|
|
}
|
|
|
|
("AspectRatioY", &flavors::ScriptDataValue::Number(par_y)) if par_y > 0.0 => {
|
|
|
|
par_d = Some(par_y as i32);
|
|
|
|
}
|
|
|
|
("videodatarate", &flavors::ScriptDataValue::Number(datarate)) => {
|
|
|
|
metadata.video_bitrate = Some((datarate * 1024.0) as u32);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let (Some(par_n), Some(par_d)) = (par_n, par_d) {
|
|
|
|
metadata.video_pixel_aspect_ratio = Some(Rational32::new(par_n, par_d));
|
2016-12-04 18:24:44 +00:00
|
|
|
}
|
|
|
|
|
2019-01-03 21:53:06 +00:00
|
|
|
metadata
|
2016-11-24 14:29:43 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-03 21:53:06 +00:00
|
|
|
|
|
|
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
|
|
gst::Element::register(plugin, "rsflvdemux", 256 + 100, FlvDemux::get_type())
|
|
|
|
}
|