Implement metadata handling

This commit is contained in:
Sebastian Dröge 2016-12-04 20:24:44 +02:00
parent d804590106
commit 7ef2679cb5
3 changed files with 232 additions and 52 deletions

View file

@ -12,7 +12,7 @@ url = "1.1"
bitflags = "0.7"
reqwest = "0.2"
nom = "2.0"
flavors = {git = "https://github.com/Geal/flavors.git"}
flavors = {git = "https://github.com/sdroege/flavors.git"}
[build-dependencies]
gcc = "0.3"

View file

@ -50,13 +50,8 @@ struct StreamingState {
expect_video: bool,
got_all_streams: bool,
last_position: Option<u64>,
duration: Option<u64>,
creation_date: Option<String>,
creator: Option<String>,
title: Option<String>,
metadata_creator: Option<String>, /* TODO: seek_table: _,
* filepositions / times metadata arrays */
metadata: Option<Metadata>,
}
impl StreamingState {
@ -68,11 +63,7 @@ impl StreamingState {
expect_video: video,
got_all_streams: false,
last_position: None,
duration: None,
creation_date: None,
creator: None,
title: None,
metadata_creator: None, // seek_table: None
metadata: None,
}
}
}
@ -95,13 +86,8 @@ impl PartialEq for AudioFormat {
}
impl AudioFormat {
fn new(format: flavors::SoundFormat,
rate: flavors::SoundRate,
width: flavors::SoundSize,
channels: flavors::SoundType,
bitrate: Option<u32>)
-> AudioFormat {
let numeric_rate = match (format, rate) {
fn new(data_header: &flavors::AudioDataHeader, metadata: &Option<Metadata>) -> AudioFormat {
let numeric_rate = match (data_header.sound_format, data_header.sound_rate) {
(flavors::SoundFormat::NELLYMOSER_16KHZ_MONO, _) => 16000,
(flavors::SoundFormat::NELLYMOSER_8KHZ_MONO, _) => 8000,
(flavors::SoundFormat::MP3_8KHZ, _) => 8000,
@ -111,25 +97,36 @@ impl AudioFormat {
(_, flavors::SoundRate::_44KHZ) => 44100,
};
let numeric_width = match width {
let numeric_width = match data_header.sound_size {
flavors::SoundSize::Snd8bit => 8,
flavors::SoundSize::Snd16bit => 16,
};
let numeric_channels = match channels {
let numeric_channels = match data_header.sound_type {
flavors::SoundType::SndMono => 1,
flavors::SoundType::SndStereo => 2,
};
AudioFormat {
format: format,
format: data_header.sound_format,
rate: numeric_rate,
width: numeric_width,
channels: numeric_channels,
bitrate: bitrate,
bitrate: metadata.as_ref().and_then(|m| m.audio_bitrate),
}
}
fn update_with_metadata(&mut self, metadata: &Metadata) -> bool {
let mut changed = false;
if self.bitrate != metadata.audio_bitrate {
self.bitrate = metadata.audio_bitrate;
changed = true;
}
changed
}
fn to_string(&self) -> Option<String> {
match self.format {
flavors::SoundFormat::MP3 => {
@ -159,23 +156,48 @@ struct VideoFormat {
}
impl VideoFormat {
fn new(format: flavors::CodecId,
width: Option<u32>,
height: Option<u32>,
pixel_aspect_ratio: Option<(u32, u32)>,
framerate: Option<(u32, u32)>,
bitrate: Option<u32>)
-> VideoFormat {
fn new(data_header: &flavors::VideoDataHeader, metadata: &Option<Metadata>) -> VideoFormat {
VideoFormat {
format: format,
width: width,
height: height,
pixel_aspect_ratio: pixel_aspect_ratio,
framerate: framerate,
bitrate: bitrate,
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),
}
}
fn update_with_metadata(&mut self, metadata: &Metadata) -> bool {
let mut changed = false;
if self.width != metadata.video_width {
self.width = metadata.video_width;
changed = true;
}
if self.height != metadata.video_height {
self.height = metadata.video_height;
changed = true;
}
if self.pixel_aspect_ratio != metadata.video_pixel_aspect_ratio {
self.pixel_aspect_ratio = metadata.video_pixel_aspect_ratio;
changed = true;
}
if self.framerate != metadata.video_framerate {
self.framerate = metadata.video_framerate;
changed = true;
}
if self.bitrate != metadata.video_bitrate {
self.bitrate = metadata.video_bitrate;
changed = true;
}
changed
}
fn to_string(&self) -> Option<String> {
match self.format {
flavors::CodecId::VP6 => {
@ -213,6 +235,104 @@ impl PartialEq for VideoFormat {
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
struct Metadata {
duration: Option<u64>,
creation_date: Option<String>,
creator: Option<String>,
title: Option<String>,
metadata_creator: Option<String>, /* TODO: seek_table: _,
* filepositions / times metadata arrays */
audio_bitrate: Option<u32>,
video_width: Option<u32>,
video_height: Option<u32>,
video_pixel_aspect_ratio: Option<(u32, u32)>,
video_framerate: Option<(u32, u32)>,
video_bitrate: Option<u32>,
}
impl Metadata {
fn new(script_data: &flavors::ScriptData) -> Metadata {
assert_eq!(script_data.name, "onMetaData");
let mut metadata = Metadata {
duration: None,
creation_date: None,
creator: None,
title: None,
metadata_creator: None,
audio_bitrate: None,
video_width: None,
video_height: None,
video_pixel_aspect_ratio: None,
video_framerate: None,
video_bitrate: None,
};
let args = match script_data.arguments {
flavors::ScriptDataValue::Object(ref objects) => objects,
flavors::ScriptDataValue::ECMAArray(ref objects) => objects,
flavors::ScriptDataValue::StrictArray(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 = Some((duration * 1000.0 * 1000.0 * 1000.0) as u64);
}
("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)) => {
// FIXME: Convert properly to a fraction
metadata.video_framerate = Some((framerate as u32, 1));
}
("AspectRatioX", &flavors::ScriptDataValue::Number(par_x)) => {
par_n = Some(par_x as u32);
}
("AspectRatioY", &flavors::ScriptDataValue::Number(par_y)) => {
par_d = Some(par_y as u32);
}
("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((par_n, par_d));
}
metadata
}
}
#[derive(Debug)]
pub struct FlvDemux {
state: State,
@ -237,7 +357,62 @@ impl FlvDemux {
fn handle_script_tag(&mut self,
tag_header: &flavors::TagHeader)
-> Result<HandleBufferResult, FlowError> {
self.adapter.flush((15 + tag_header.data_size) as usize).unwrap();
if self.adapter.get_available() < (15 + tag_header.data_size) as usize {
return Ok(HandleBufferResult::NeedMoreData);
}
self.adapter.flush(15).unwrap();
let buffer = self.adapter.get_buffer(tag_header.data_size as usize).unwrap();
let map = buffer.map_read().unwrap();
let data = map.as_slice();
match flavors::script_data(data) {
IResult::Done(_, ref script_data) if script_data.name == "onMetaData" => {
let streaming_state = self.streaming_state.as_mut().unwrap();
let metadata = Metadata::new(script_data);
let audio_changed = streaming_state.audio
.as_mut()
.map(|a| a.update_with_metadata(&metadata))
.unwrap_or(false);
let video_changed = streaming_state.video
.as_mut()
.map(|v| v.update_with_metadata(&metadata))
.unwrap_or(false);
streaming_state.metadata = Some(metadata);
if audio_changed || video_changed {
let mut streams = Vec::new();
if audio_changed {
if let Some(format) = streaming_state.audio
.as_ref()
.and_then(|a| a.to_string()) {
streams.push(Stream::new(AUDIO_STREAM_ID,
format,
String::from("audio")));
}
}
if video_changed {
if let Some(format) = streaming_state.video
.as_ref()
.and_then(|v| v.to_string()) {
streams.push(Stream::new(VIDEO_STREAM_ID,
format,
String::from("video")));
}
}
return Ok(HandleBufferResult::StreamsChanged(streams));
}
}
IResult::Done(_, _) |
IResult::Error(_) |
IResult::Incomplete(_) => {
// ignore
}
}
Ok(HandleBufferResult::Again)
}
@ -247,12 +422,7 @@ impl FlvDemux {
-> Result<HandleBufferResult, FlowError> {
let streaming_state = self.streaming_state.as_mut().unwrap();
let new_audio_format =
AudioFormat::new(data_header.sound_format,
data_header.sound_rate,
data_header.sound_size,
data_header.sound_type,
streaming_state.audio.as_ref().and_then(|s| s.bitrate));
let new_audio_format = AudioFormat::new(data_header, &streaming_state.metadata);
if streaming_state.audio.as_ref() != Some(&new_audio_format) {
let new_stream = streaming_state.audio == None;
@ -309,13 +479,7 @@ impl FlvDemux {
-> Result<HandleBufferResult, FlowError> {
let streaming_state = self.streaming_state.as_mut().unwrap();
let new_video_format =
VideoFormat::new(data_header.codec_id,
streaming_state.video.as_ref().and_then(|s| s.width),
streaming_state.video.as_ref().and_then(|s| s.height),
streaming_state.video.as_ref().and_then(|s| s.pixel_aspect_ratio),
streaming_state.video.as_ref().and_then(|s| s.framerate),
streaming_state.video.as_ref().and_then(|s| s.bitrate));
let new_video_format = VideoFormat::new(data_header, &streaming_state.metadata);
if streaming_state.video.as_ref() != Some(&new_video_format) {
let new_stream = streaming_state.video == None;
@ -407,7 +571,7 @@ impl FlvDemux {
IResult::Incomplete(_) => {
// fall through
}
IResult::Done(_, header) => {
IResult::Done(_, ref header) => {
let skip = if header.offset < 9 {
0
} else {
@ -565,6 +729,11 @@ impl Demuxer for FlvDemux {
}
fn get_duration(&self) -> Option<u64> {
if let Some(StreamingState { metadata: Some(Metadata { duration, .. }), .. }) =
self.streaming_state {
return duration;
}
None
}
}

View file

@ -49,7 +49,7 @@ pub enum HandleBufferResult {
HaveAllStreams,
StreamChanged(Stream),
// StreamsAdded(Vec<Stream>), // Implies HaveAllStreams
// StreamsChanged(Vec<Stream>),
StreamsChanged(Vec<Stream>),
// TODO need something to replace/add new streams
// TODO should probably directly implement the GstStreams new world order
BufferForStream(StreamIndex, Buffer),
@ -272,6 +272,17 @@ impl DemuxerWrapper {
format_cstr.as_ptr());
}
}
HandleBufferResult::StreamsChanged(streams) => {
for stream in streams {
let format_cstr = CString::new(stream.format.as_bytes()).unwrap();
unsafe {
gst_rs_demuxer_stream_format_changed(self.raw,
stream.index,
format_cstr.as_ptr());
}
}
}
HandleBufferResult::BufferForStream(index, buffer) => {
let flow_ret = unsafe {
gst_rs_demuxer_stream_push_buffer(self.raw, index, buffer.into_ptr())