diff --git a/gst-plugin-flv/src/flvdemux.rs b/gst-plugin-flv/src/flvdemux.rs index 7feed304..553aeb81 100644 --- a/gst-plugin-flv/src/flvdemux.rs +++ b/gst-plugin-flv/src/flvdemux.rs @@ -16,6 +16,7 @@ // Boston, MA 02110-1301, USA. use std::cmp; +use std::io::{Write, Cursor}; use nom; use nom::IResult; @@ -31,6 +32,7 @@ use gst_plugin::utils::Element; use gst_plugin::log::*; use gst_plugin::caps::Caps; use gst_plugin::caps; +use gst_plugin::bytes::*; use slog::*; @@ -195,8 +197,42 @@ impl AudioFormat { }) } flavors::SoundFormat::SPEEX => { - // TODO: This requires creating a Speex streamheader... - None + let mut header = Buffer::new_with_size(80).unwrap(); + { + let mut map = header.map_readwrite().unwrap(); + let mut data = Cursor::new(map.as_mut_slice()); + data.write(b"Speex 1.1.12").unwrap(); + data.write(&[0; 14]).unwrap(); + data.write_u32le(1).unwrap(); // version + data.write_u32le(80).unwrap(); // header size + data.write_u32le(16000).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 + } + + let comment_size = 4 + 7 /* nothing */ + 4 + 1; + + let mut comment = Buffer::new_with_size(comment_size).unwrap(); + { + let mut map = comment.map_readwrite().unwrap(); + let mut data = Cursor::new(map.as_mut_slice()); + data.write_u32le(7).unwrap(); // length of "nothing" + data.write(b"nothing").unwrap(); // "vendor" string + data.write_u32le(0).unwrap(); // number of elements + data.write_u8(1); + } + Some(Caps::new_simple("audio/x-speex", + &[("streamheader", + &caps::Value::Array(vec![caps::Value::Buffer(header), + caps::Value::Buffer(comment)]))])) } flavors::SoundFormat::DEVICE_SPECIFIC => { // Nobody knows diff --git a/gst-plugin/Cargo.toml b/gst-plugin/Cargo.toml index f25849f0..15415636 100644 --- a/gst-plugin/Cargo.toml +++ b/gst-plugin/Cargo.toml @@ -12,6 +12,7 @@ url = "1.1" bitflags = "0.7" slog = { version = "1.3", features = ["max_level_trace"] } lazy_static = "0.2" +byteorder = "1.0" [build-dependencies] gcc = "0.3" diff --git a/gst-plugin/src/bytes.rs b/gst-plugin/src/bytes.rs new file mode 100644 index 00000000..8590885a --- /dev/null +++ b/gst-plugin/src/bytes.rs @@ -0,0 +1,149 @@ +// Copyright (C) 2016 Sebastian Dröge +// +// 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 St, Fifth Floor, +// Boston, MA 02110-1301, USA. + +pub use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; +use std::io; + +pub trait ReadBytesExtShort: io::Read { + fn read_u16le(&mut self) -> io::Result { + self.read_u16::() + } + fn read_i16le(&mut self) -> io::Result { + self.read_i16::() + } + fn read_u32le(&mut self) -> io::Result { + self.read_u32::() + } + fn read_i32le(&mut self) -> io::Result { + self.read_i32::() + } + fn read_u64le(&mut self) -> io::Result { + self.read_u64::() + } + fn read_i64le(&mut self) -> io::Result { + self.read_i64::() + } + fn read_uintle(&mut self, nbytes: usize) -> io::Result { + self.read_uint::(nbytes) + } + fn read_intle(&mut self, nbytes: usize) -> io::Result { + self.read_int::(nbytes) + } + fn read_f32le(&mut self) -> io::Result { + self.read_f32::() + } + fn read_f64le(&mut self) -> io::Result { + self.read_f64::() + } + fn read_u16be(&mut self) -> io::Result { + self.read_u16::() + } + fn read_i16be(&mut self) -> io::Result { + self.read_i16::() + } + fn read_u32be(&mut self) -> io::Result { + self.read_u32::() + } + fn read_i32be(&mut self) -> io::Result { + self.read_i32::() + } + fn read_u64be(&mut self) -> io::Result { + self.read_u64::() + } + fn read_i64be(&mut self) -> io::Result { + self.read_i64::() + } + fn read_uintbe(&mut self, nbytes: usize) -> io::Result { + self.read_uint::(nbytes) + } + fn read_intbe(&mut self, nbytes: usize) -> io::Result { + self.read_int::(nbytes) + } + fn read_f32be(&mut self) -> io::Result { + self.read_f32::() + } + fn read_f64be(&mut self) -> io::Result { + self.read_f64::() + } +} + +impl ReadBytesExtShort for T where T: ReadBytesExt {} + +pub trait WriteBytesExtShort: WriteBytesExt { + fn write_u16le(&mut self, n: u16) -> io::Result<()> { + self.write_u16::(n) + } + fn write_i16le(&mut self, n: i16) -> io::Result<()> { + self.write_i16::(n) + } + fn write_u32le(&mut self, n: u32) -> io::Result<()> { + self.write_u32::(n) + } + fn write_i32le(&mut self, n: i32) -> io::Result<()> { + self.write_i32::(n) + } + fn write_u64le(&mut self, n: u64) -> io::Result<()> { + self.write_u64::(n) + } + fn write_i64le(&mut self, n: i64) -> io::Result<()> { + self.write_i64::(n) + } + fn write_uintle(&mut self, n: u64, nbytes: usize) -> io::Result<()> { + self.write_uint::(n, nbytes) + } + fn write_intle(&mut self, n: i64, nbytes: usize) -> io::Result<()> { + self.write_int::(n, nbytes) + } + fn write_f32le(&mut self, n: f32) -> io::Result<()> { + self.write_f32::(n) + } + fn write_f64le(&mut self, n: f64) -> io::Result<()> { + self.write_f64::(n) + } + fn write_u16be(&mut self, n: u16) -> io::Result<()> { + self.write_u16::(n) + } + fn write_i16be(&mut self, n: i16) -> io::Result<()> { + self.write_i16::(n) + } + fn write_u32be(&mut self, n: u32) -> io::Result<()> { + self.write_u32::(n) + } + fn write_i32be(&mut self, n: i32) -> io::Result<()> { + self.write_i32::(n) + } + fn write_u64be(&mut self, n: u64) -> io::Result<()> { + self.write_u64::(n) + } + fn write_i64be(&mut self, n: i64) -> io::Result<()> { + self.write_i64::(n) + } + fn write_uintbe(&mut self, n: u64, nbytes: usize) -> io::Result<()> { + self.write_uint::(n, nbytes) + } + fn write_intbe(&mut self, n: i64, nbytes: usize) -> io::Result<()> { + self.write_int::(n, nbytes) + } + fn write_f32be(&mut self, n: f32) -> io::Result<()> { + self.write_f32::(n) + } + fn write_f64be(&mut self, n: f64) -> io::Result<()> { + self.write_f64::(n) + } +} + +impl WriteBytesExtShort for T where T: WriteBytesExt {} diff --git a/gst-plugin/src/caps.rs b/gst-plugin/src/caps.rs index cc1ea66f..a154380d 100644 --- a/gst-plugin/src/caps.rs +++ b/gst-plugin/src/caps.rs @@ -20,18 +20,18 @@ use std::os::raw::c_void; use std::ffi::CString; use std::ffi::CStr; use std::mem; -use std::borrow::Cow; use std::fmt; use buffer::*; #[derive(Debug, PartialEq, Eq, Clone)] -pub enum Value<'a> { +pub enum Value { Bool(bool), Int(i32), - String(Cow<'a, str>), + String(String), Fraction(i32, i32), Buffer(Buffer), + Array(Vec), } pub struct Caps(*mut c_void); @@ -47,6 +47,75 @@ const TYPE_BOOLEAN: usize = (5 << 2); const TYPE_INT: usize = (6 << 2); const TYPE_STRING: usize = (16 << 2); +impl Value { + fn to_gvalue(&self) -> GValue { + extern "C" { + fn g_value_init(value: *mut GValue, gtype: usize); + fn g_value_set_boolean(value: *mut GValue, value: i32); + fn g_value_set_int(value: *mut GValue, value: i32); + fn g_value_set_string(value: *mut GValue, value: *const c_char); + fn gst_value_set_fraction(value: *mut GValue, value_n: i32, value_d: i32); + fn gst_fraction_get_type() -> usize; + fn g_value_set_boxed(value: *mut GValue, boxed: *const c_void); + fn gst_buffer_get_type() -> usize; + fn gst_value_array_get_type() -> usize; + fn gst_value_array_append_and_take_value(value: *mut GValue, element: *mut GValue); + } + + let mut gvalue: GValue = unsafe { mem::zeroed() }; + + match *self { + Value::Bool(v) => unsafe { + g_value_init(&mut gvalue as *mut GValue, TYPE_BOOLEAN); + g_value_set_boolean(&mut gvalue as *mut GValue, if v { 1 } else { 0 }); + }, + Value::Int(v) => unsafe { + g_value_init(&mut gvalue as *mut GValue, TYPE_INT); + g_value_set_int(&mut gvalue as *mut GValue, v); + }, + Value::String(ref v) => unsafe { + let v_cstr = CString::new(String::from(v.clone())).unwrap(); + + g_value_init(&mut gvalue as *mut GValue, TYPE_STRING); + g_value_set_string(&mut gvalue as *mut GValue, v_cstr.as_ptr()); + }, + Value::Fraction(v_n, v_d) => unsafe { + g_value_init(&mut gvalue as *mut GValue, gst_fraction_get_type()); + gst_value_set_fraction(&mut gvalue as *mut GValue, v_n, v_d); + }, + Value::Buffer(ref buffer) => unsafe { + g_value_init(&mut gvalue as *mut GValue, gst_buffer_get_type()); + g_value_set_boxed(&mut gvalue as *mut GValue, buffer.as_ptr()); + }, + Value::Array(ref array) => unsafe { + g_value_init(&mut gvalue as *mut GValue, gst_value_array_get_type()); + + for e in array { + let mut e_value = e.to_gvalue(); + gst_value_array_append_and_take_value(&mut gvalue as *mut GValue, + &mut e_value as *mut GValue); + // Takes ownership, invalidate GValue + e_value.typ = 0; + } + }, + } + + gvalue + } +} + +impl Drop for GValue { + fn drop(&mut self) { + extern "C" { + fn g_value_unset(value: *mut GValue); + } + + if self.typ != 0 { + unsafe { g_value_unset(self as *mut GValue) } + } + } +} + impl Caps { pub fn new_empty() -> Self { extern "C" { @@ -64,7 +133,7 @@ impl Caps { Caps(unsafe { gst_caps_new_any() }) } - pub fn new_simple<'a>(name: &str, values: &[(&'a str, &'a Value<'a>)]) -> Self { + pub fn new_simple(name: &str, values: &[(&str, &Value)]) -> Self { extern "C" { fn gst_caps_append_structure(caps: *mut c_void, structure: *mut c_void); fn gst_structure_new_empty(name: *const c_char) -> *mut c_void; @@ -103,54 +172,14 @@ impl Caps { pub fn set_simple(&mut self, values: &[(&str, &Value)]) { extern "C" { fn gst_caps_set_value(caps: *mut c_void, name: *const c_char, value: *const GValue); - fn g_value_init(value: *mut GValue, gtype: usize); - fn g_value_unset(value: *mut GValue); - fn g_value_set_boolean(value: *mut GValue, value: i32); - fn g_value_set_int(value: *mut GValue, value: i32); - fn g_value_set_string(value: *mut GValue, value: *const c_char); - fn gst_value_set_fraction(value: *mut GValue, value_n: i32, value_d: i32); - fn gst_fraction_get_type() -> usize; - fn g_value_set_boxed(value: *mut GValue, boxed: *const c_void); - fn gst_buffer_get_type() -> usize; } for value in values { let name_cstr = CString::new(value.0).unwrap(); - let mut gvalue: GValue = unsafe { mem::zeroed() }; + let mut gvalue = value.1.to_gvalue(); - match *value.1 { - Value::Bool(v) => unsafe { - g_value_init(&mut gvalue as *mut GValue, TYPE_BOOLEAN); - g_value_set_boolean(&mut gvalue as *mut GValue, if v { 1 } else { 0 }); - gst_caps_set_value(self.0, name_cstr.as_ptr(), &mut gvalue as *mut GValue); - g_value_unset(&mut gvalue as *mut GValue); - }, - Value::Int(v) => unsafe { - g_value_init(&mut gvalue as *mut GValue, TYPE_INT); - g_value_set_int(&mut gvalue as *mut GValue, v); - gst_caps_set_value(self.0, name_cstr.as_ptr(), &mut gvalue as *mut GValue); - g_value_unset(&mut gvalue as *mut GValue); - }, - Value::String(ref v) => unsafe { - let v_cstr = CString::new(String::from((*v).clone())).unwrap(); - - g_value_init(&mut gvalue as *mut GValue, TYPE_STRING); - g_value_set_string(&mut gvalue as *mut GValue, v_cstr.as_ptr()); - gst_caps_set_value(self.0, name_cstr.as_ptr(), &mut gvalue as *mut GValue); - g_value_unset(&mut gvalue as *mut GValue); - }, - Value::Fraction(v_n, v_d) => unsafe { - g_value_init(&mut gvalue as *mut GValue, gst_fraction_get_type()); - gst_value_set_fraction(&mut gvalue as *mut GValue, v_n, v_d); - gst_caps_set_value(self.0, name_cstr.as_ptr(), &mut gvalue as *mut GValue); - g_value_unset(&mut gvalue as *mut GValue); - }, - Value::Buffer(ref buffer) => unsafe { - g_value_init(&mut gvalue as *mut GValue, gst_buffer_get_type()); - g_value_set_boxed(&mut gvalue as *mut GValue, buffer.as_ptr()); - gst_caps_set_value(self.0, name_cstr.as_ptr(), &mut gvalue as *mut GValue); - g_value_unset(&mut gvalue as *mut GValue); - }, + unsafe { + gst_caps_set_value(self.0, name_cstr.as_ptr(), &mut gvalue as *mut GValue); } } } @@ -195,6 +224,16 @@ impl Clone for Caps { } } +impl Drop for Caps { + fn drop(&mut self) { + extern "C" { + fn gst_mini_object_unref(mini_object: *mut c_void); + } + + unsafe { gst_mini_object_unref(self.0) } + } +} + impl fmt::Debug for Caps { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.to_string()) @@ -222,12 +261,14 @@ mod tests { init(); let caps = Caps::new_simple("foo/bar", - vec![("int", &Value::Int(12)), - ("bool", &Value::Bool(true)), - ("string", &Value::String("bla".into())), - ("fraction", &Value::Fraction(1, 2))]); + &[("int", &Value::Int(12)), + ("bool", &Value::Bool(true)), + ("string", &Value::String("bla".into())), + ("fraction", &Value::Fraction(1, 2)), + ("array", + &Value::Array(vec![Value::Int(1), Value::Int(2)]))]); assert_eq!(caps.to_string(), "foo/bar, int=(int)12, bool=(boolean)true, string=(string)bla, \ - fraction=(fraction)1/2"); + fraction=(fraction)1/2, array=(int)< 1, 2 >"); } } diff --git a/gst-plugin/src/lib.rs b/gst-plugin/src/lib.rs index 147e19b0..07b64e83 100644 --- a/gst-plugin/src/lib.rs +++ b/gst-plugin/src/lib.rs @@ -24,6 +24,7 @@ extern crate bitflags; extern crate slog; #[macro_use] extern crate lazy_static; +extern crate byteorder; #[macro_use] pub mod utils; @@ -38,3 +39,4 @@ pub mod sink; pub mod demuxer; pub mod log; pub mod caps; +pub mod bytes;