gstreamer-rs/gstreamer/src/structure.rs

711 lines
19 KiB
Rust
Raw Normal View History

2017-07-07 13:04:54 +00:00
// Copyright (C) 2016-2017 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.
2018-04-01 08:30:03 +00:00
use std::borrow::{Borrow, BorrowMut, ToOwned};
use std::ffi::CStr;
2017-07-07 13:04:54 +00:00
use std::fmt;
2018-04-01 08:30:03 +00:00
use std::marker::PhantomData;
2017-07-07 13:04:54 +00:00
use std::mem;
use std::ops::{Deref, DerefMut};
2018-04-01 08:30:03 +00:00
use std::ptr;
use std::str;
2017-07-07 13:04:54 +00:00
use Fraction;
2018-04-01 08:30:03 +00:00
use ffi;
2017-07-07 13:04:54 +00:00
use glib;
2017-08-09 22:13:28 +00:00
use glib::translate::{from_glib, from_glib_full, from_glib_none, FromGlibPtrFull, FromGlibPtrNone,
GlibPtrDefault, Stash, StashMut, ToGlib, ToGlibPtr, ToGlibPtrMut};
use glib::value::{FromValueOptional, SendValue, ToSendValue};
2017-08-09 22:13:28 +00:00
use glib_ffi::gpointer;
use gobject_ffi;
2017-07-07 13:04:54 +00:00
2018-04-01 08:29:15 +00:00
pub struct Structure(ptr::NonNull<StructureRef>, PhantomData<StructureRef>);
unsafe impl Send for Structure {}
2017-07-07 13:04:54 +00:00
impl Structure {
2017-11-11 12:14:49 +00:00
pub fn builder(name: &str) -> Builder {
assert_initialized_main_thread!();
Builder::new(name)
}
2017-07-07 13:04:54 +00:00
pub fn new_empty(name: &str) -> Structure {
assert_initialized_main_thread!();
2018-04-01 08:29:15 +00:00
unsafe {
let ptr = ffi::gst_structure_new_empty(name.to_glib_none().0) as *mut StructureRef;
assert!(!ptr.is_null());
Structure(ptr::NonNull::new_unchecked(ptr), PhantomData)
}
2017-07-07 13:04:54 +00:00
}
pub fn new(name: &str, values: &[(&str, &ToSendValue)]) -> Structure {
assert_initialized_main_thread!();
2017-07-07 13:04:54 +00:00
let mut structure = Structure::new_empty(name);
for &(f, v) in values {
structure.set_value(f, v.to_send_value());
2017-07-07 13:04:54 +00:00
}
structure
}
pub fn from_string(s: &str) -> Option<Structure> {
assert_initialized_main_thread!();
2017-07-07 13:04:54 +00:00
unsafe {
let structure = ffi::gst_structure_from_string(s.to_glib_none().0, ptr::null_mut());
if structure.is_null() {
None
} else {
2018-04-01 08:29:15 +00:00
Some(Structure(
ptr::NonNull::new_unchecked(structure as *mut StructureRef),
PhantomData,
))
2017-07-07 13:04:54 +00:00
}
}
}
pub unsafe fn into_ptr(self) -> *mut ffi::GstStructure {
2018-04-01 08:29:15 +00:00
let ptr = self.0.as_ptr() as *mut StructureRef as *mut ffi::GstStructure;
2017-07-07 13:04:54 +00:00
mem::forget(self);
ptr
}
}
impl Deref for Structure {
type Target = StructureRef;
fn deref(&self) -> &StructureRef {
2018-04-01 08:29:15 +00:00
unsafe { self.0.as_ref() }
2017-07-07 13:04:54 +00:00
}
}
impl DerefMut for Structure {
fn deref_mut(&mut self) -> &mut StructureRef {
2018-04-01 08:29:15 +00:00
unsafe { self.0.as_mut() }
2017-07-07 13:04:54 +00:00
}
}
impl AsRef<StructureRef> for Structure {
fn as_ref(&self) -> &StructureRef {
self.deref()
}
}
impl AsMut<StructureRef> for Structure {
fn as_mut(&mut self) -> &mut StructureRef {
self.deref_mut()
}
}
impl Clone for Structure {
fn clone(&self) -> Self {
2018-04-01 08:29:15 +00:00
unsafe {
let ptr = ffi::gst_structure_copy(&self.0.as_ref().0) as *mut StructureRef;
assert!(!ptr.is_null());
Structure(ptr::NonNull::new_unchecked(ptr), PhantomData)
}
2017-07-07 13:04:54 +00:00
}
}
impl Drop for Structure {
fn drop(&mut self) {
2018-04-01 08:29:15 +00:00
unsafe { ffi::gst_structure_free(&mut self.0.as_mut().0) }
2017-07-07 13:04:54 +00:00
}
}
impl fmt::Debug for Structure {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Structure")
.field(&self.as_ref().to_string())
.finish()
}
}
impl fmt::Display for Structure {
2017-07-07 13:04:54 +00:00
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.as_ref().to_string())
2017-07-07 13:04:54 +00:00
}
}
impl PartialEq for Structure {
fn eq(&self, other: &Structure) -> bool {
self.as_ref().eq(other)
}
}
impl PartialEq<StructureRef> for Structure {
fn eq(&self, other: &StructureRef) -> bool {
self.as_ref().eq(other)
}
}
impl Eq for Structure {}
impl str::FromStr for Structure {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
2017-08-30 11:39:09 +00:00
skip_assert_initialized!();
Structure::from_string(s).ok_or(())
}
}
2017-07-07 13:04:54 +00:00
impl Borrow<StructureRef> for Structure {
fn borrow(&self) -> &StructureRef {
2018-04-01 08:29:15 +00:00
unsafe { self.0.as_ref() }
2017-07-07 13:04:54 +00:00
}
}
impl BorrowMut<StructureRef> for Structure {
fn borrow_mut(&mut self) -> &mut StructureRef {
2018-04-01 08:29:15 +00:00
unsafe { self.0.as_mut() }
2017-07-07 13:04:54 +00:00
}
}
impl ToOwned for StructureRef {
type Owned = Structure;
fn to_owned(&self) -> Structure {
2018-04-01 08:29:15 +00:00
unsafe {
let ptr = ffi::gst_structure_copy(&self.0) as *mut StructureRef;
assert!(!ptr.is_null());
Structure(ptr::NonNull::new_unchecked(ptr), PhantomData)
}
2017-07-07 13:04:54 +00:00
}
}
2017-07-10 14:13:49 +00:00
impl glib::types::StaticType for Structure {
fn static_type() -> glib::types::Type {
2017-07-10 21:33:24 +00:00
unsafe { from_glib(ffi::gst_structure_get_type()) }
2017-07-10 14:13:49 +00:00
}
}
2017-07-07 13:04:54 +00:00
impl<'a> ToGlibPtr<'a, *const ffi::GstStructure> for Structure {
type Storage = &'a Self;
fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GstStructure, Self> {
2018-04-01 08:29:15 +00:00
unsafe { Stash(&self.0.as_ref().0, self) }
2017-07-07 13:04:54 +00:00
}
fn to_glib_full(&self) -> *const ffi::GstStructure {
2018-04-01 08:29:15 +00:00
unsafe { ffi::gst_structure_copy(&self.0.as_ref().0) }
2017-07-07 13:04:54 +00:00
}
}
impl<'a> ToGlibPtr<'a, *mut ffi::GstStructure> for Structure {
type Storage = &'a Self;
fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GstStructure, Self> {
2018-04-01 08:29:15 +00:00
unsafe { Stash(&self.0.as_ref().0 as *const _ as *mut _, self) }
2017-07-07 13:04:54 +00:00
}
fn to_glib_full(&self) -> *mut ffi::GstStructure {
2018-04-01 08:29:15 +00:00
unsafe { ffi::gst_structure_copy(&self.0.as_ref().0) }
2017-07-07 13:04:54 +00:00
}
}
impl<'a> ToGlibPtrMut<'a, *mut ffi::GstStructure> for Structure {
type Storage = &'a mut Self;
fn to_glib_none_mut(&'a mut self) -> StashMut<*mut ffi::GstStructure, Self> {
2018-04-01 08:29:15 +00:00
unsafe { StashMut(&mut self.0.as_mut().0, self) }
2017-07-07 13:04:54 +00:00
}
}
impl FromGlibPtrNone<*const ffi::GstStructure> for Structure {
unsafe fn from_glib_none(ptr: *const ffi::GstStructure) -> Self {
2018-04-01 08:29:15 +00:00
assert!(!ptr.is_null());
let ptr = ffi::gst_structure_copy(ptr);
assert!(!ptr.is_null());
2017-07-07 13:04:54 +00:00
Structure(
2018-04-01 08:29:15 +00:00
ptr::NonNull::new_unchecked(ptr as *mut StructureRef),
2017-07-07 13:04:54 +00:00
PhantomData,
)
}
}
impl FromGlibPtrNone<*mut ffi::GstStructure> for Structure {
unsafe fn from_glib_none(ptr: *mut ffi::GstStructure) -> Self {
2018-04-01 08:29:15 +00:00
assert!(!ptr.is_null());
let ptr = ffi::gst_structure_copy(ptr);
assert!(!ptr.is_null());
2017-07-07 13:04:54 +00:00
Structure(
2018-04-01 08:29:15 +00:00
ptr::NonNull::new_unchecked(ptr as *mut StructureRef),
2017-07-07 13:04:54 +00:00
PhantomData,
)
}
}
impl FromGlibPtrFull<*const ffi::GstStructure> for Structure {
unsafe fn from_glib_full(ptr: *const ffi::GstStructure) -> Self {
2018-04-01 08:29:15 +00:00
assert!(!ptr.is_null());
Structure(
ptr::NonNull::new_unchecked(ptr as *mut StructureRef),
PhantomData,
)
2017-07-07 13:04:54 +00:00
}
}
impl FromGlibPtrFull<*mut ffi::GstStructure> for Structure {
unsafe fn from_glib_full(ptr: *mut ffi::GstStructure) -> Self {
2018-04-01 08:29:15 +00:00
assert!(!ptr.is_null());
Structure(
ptr::NonNull::new_unchecked(ptr as *mut StructureRef),
PhantomData,
)
2017-07-07 13:04:54 +00:00
}
}
2017-08-09 22:13:28 +00:00
impl<'a> glib::value::FromValueOptional<'a> for Structure {
unsafe fn from_value_optional(v: &'a glib::Value) -> Option<Self> {
let ptr = gobject_ffi::g_value_get_boxed(v.to_glib_none().0);
2018-04-01 08:29:15 +00:00
assert!(!ptr.is_null());
2017-08-09 22:13:28 +00:00
from_glib_none(ptr as *const ffi::GstStructure)
}
}
impl glib::value::SetValue for Structure {
unsafe fn set_value(v: &mut glib::Value, s: &Self) {
2018-04-01 08:29:15 +00:00
gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, s.0.as_ptr() as gpointer);
2017-08-09 22:13:28 +00:00
}
}
impl glib::value::SetValueOptional for Structure {
unsafe fn set_value_optional(v: &mut glib::Value, s: Option<&Self>) {
if let Some(s) = s {
gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, s.as_ptr() as gpointer);
} else {
gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, ptr::null_mut());
}
}
}
2017-08-09 22:13:28 +00:00
impl GlibPtrDefault for Structure {
type GlibType = *mut ffi::GstStructure;
}
2017-07-07 13:04:54 +00:00
#[repr(C)]
pub struct StructureRef(ffi::GstStructure);
impl StructureRef {
pub unsafe fn from_glib_borrow<'a>(ptr: *const ffi::GstStructure) -> &'a StructureRef {
assert!(!ptr.is_null());
&*(ptr as *mut StructureRef)
}
pub unsafe fn from_glib_borrow_mut<'a>(ptr: *mut ffi::GstStructure) -> &'a mut StructureRef {
assert!(!ptr.is_null());
&mut *(ptr as *mut StructureRef)
}
pub unsafe fn as_ptr(&self) -> *const ffi::GstStructure {
self as *const Self as *const ffi::GstStructure
}
pub unsafe fn as_mut_ptr(&self) -> *mut ffi::GstStructure {
self as *const Self as *mut ffi::GstStructure
}
2017-07-07 13:04:54 +00:00
pub fn to_string(&self) -> String {
2017-07-10 21:33:24 +00:00
unsafe { from_glib_full(ffi::gst_structure_to_string(&self.0)) }
2017-07-07 13:04:54 +00:00
}
pub fn get<'a, T: FromValueOptional<'a>>(&'a self, name: &str) -> Option<T> {
2017-07-07 13:04:54 +00:00
self.get_value(name).and_then(|v| v.get())
}
pub fn get_value<'a>(&'a self, name: &str) -> Option<&SendValue> {
2017-07-07 13:04:54 +00:00
unsafe {
2017-07-10 21:02:08 +00:00
let value = ffi::gst_structure_get_value(&self.0, name.to_glib_none().0);
2017-07-07 13:04:54 +00:00
if value.is_null() {
return None;
}
Some(&*(value as *const SendValue))
2017-07-07 13:04:54 +00:00
}
}
pub fn set<T: ToSendValue>(&mut self, name: &str, value: &T) {
let value = value.to_send_value();
2017-07-07 13:04:54 +00:00
self.set_value(name, value);
}
pub fn set_value(&mut self, name: &str, mut value: SendValue) {
2017-07-07 13:04:54 +00:00
unsafe {
2017-07-10 21:33:24 +00:00
ffi::gst_structure_take_value(
&mut self.0,
name.to_glib_none().0,
value.to_glib_none_mut().0,
);
2017-07-07 13:04:54 +00:00
mem::forget(value);
}
}
pub fn get_name(&self) -> &str {
unsafe {
2017-07-10 21:33:24 +00:00
CStr::from_ptr(ffi::gst_structure_get_name(&self.0))
.to_str()
.unwrap()
2017-07-07 13:04:54 +00:00
}
}
pub fn set_name(&mut self, name: &str) {
unsafe { ffi::gst_structure_set_name(&mut self.0, name.to_glib_none().0) }
}
2017-07-07 13:04:54 +00:00
pub fn has_field(&self, field: &str) -> bool {
unsafe {
2017-07-10 21:33:24 +00:00
from_glib(ffi::gst_structure_has_field(
&self.0,
field.to_glib_none().0,
))
2017-07-07 13:04:54 +00:00
}
}
pub fn has_field_with_type(&self, field: &str, type_: glib::Type) -> bool {
unsafe {
from_glib(ffi::gst_structure_has_field_typed(
&self.0,
field.to_glib_none().0,
type_.to_glib(),
))
}
}
2017-07-07 13:04:54 +00:00
pub fn remove_field(&mut self, field: &str) {
unsafe {
ffi::gst_structure_remove_field(&mut self.0, field.to_glib_none().0);
}
}
pub fn remove_fields(&mut self, fields: &[&str]) {
for f in fields {
self.remove_field(f)
}
}
2017-07-07 13:04:54 +00:00
pub fn remove_all_fields(&mut self) {
unsafe {
ffi::gst_structure_remove_all_fields(&mut self.0);
}
}
pub fn fields(&self) -> FieldIterator {
FieldIterator::new(self)
}
pub fn iter(&self) -> Iter {
Iter::new(self)
}
pub fn get_nth_field_name(&self, idx: u32) -> Option<&str> {
2017-07-07 13:04:54 +00:00
unsafe {
let field_name = ffi::gst_structure_nth_field_name(&self.0, idx);
if field_name.is_null() {
return None;
}
Some(CStr::from_ptr(field_name).to_str().unwrap())
}
}
pub fn n_fields(&self) -> u32 {
2017-07-07 13:04:54 +00:00
unsafe { ffi::gst_structure_n_fields(&self.0) as u32 }
}
pub fn can_intersect(&self, other: &StructureRef) -> bool {
unsafe { from_glib(ffi::gst_structure_can_intersect(&self.0, &other.0)) }
}
2017-12-17 09:52:52 +00:00
pub fn intersect(&self, other: &StructureRef) -> Option<Structure> {
unsafe { from_glib_full(ffi::gst_structure_intersect(&self.0, &other.0)) }
}
pub fn is_subset(&self, superset: &StructureRef) -> bool {
unsafe { from_glib(ffi::gst_structure_is_subset(&self.0, &superset.0)) }
}
pub fn fixate(&mut self) {
unsafe { ffi::gst_structure_fixate(&mut self.0) }
}
pub fn fixate_field(&mut self, name: &str) -> bool {
unsafe {
from_glib(ffi::gst_structure_fixate_field(
&mut self.0,
name.to_glib_none().0,
))
}
}
pub fn fixate_field_bool(&mut self, name: &str, target: bool) -> bool {
unsafe {
from_glib(ffi::gst_structure_fixate_field_boolean(
&mut self.0,
name.to_glib_none().0,
target.to_glib(),
))
}
}
pub fn fixate_field_str(&mut self, name: &str, target: &str) -> bool {
unsafe {
from_glib(ffi::gst_structure_fixate_field_string(
&mut self.0,
name.to_glib_none().0,
target.to_glib_none().0,
))
}
}
pub fn fixate_field_nearest_double(&mut self, name: &str, target: f64) -> bool {
unsafe {
from_glib(ffi::gst_structure_fixate_field_nearest_double(
&mut self.0,
name.to_glib_none().0,
target,
))
}
}
pub fn fixate_field_nearest_fraction<T: Into<Fraction>>(
&mut self,
name: &str,
target: T,
) -> bool {
2017-08-30 11:39:09 +00:00
skip_assert_initialized!();
let target = target.into();
unsafe {
from_glib(ffi::gst_structure_fixate_field_nearest_fraction(
&mut self.0,
name.to_glib_none().0,
*target.numer(),
*target.denom(),
))
}
}
pub fn fixate_field_nearest_int(&mut self, name: &str, target: i32) -> bool {
unsafe {
from_glib(ffi::gst_structure_fixate_field_nearest_int(
&mut self.0,
name.to_glib_none().0,
target,
))
}
}
2017-07-07 13:04:54 +00:00
}
impl fmt::Debug for StructureRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&StructureRef::to_string(self))
2017-07-07 13:04:54 +00:00
}
}
impl PartialEq for StructureRef {
fn eq(&self, other: &StructureRef) -> bool {
unsafe { from_glib(ffi::gst_structure_is_equal(&self.0, &other.0)) }
}
}
impl Eq for StructureRef {}
pub struct FieldIterator<'a> {
structure: &'a StructureRef,
idx: u32,
n_fields: u32,
}
impl<'a> FieldIterator<'a> {
2017-08-30 11:39:09 +00:00
fn new(structure: &'a StructureRef) -> FieldIterator<'a> {
skip_assert_initialized!();
2017-07-07 13:04:54 +00:00
let n_fields = structure.n_fields();
FieldIterator {
structure: structure,
idx: 0,
n_fields: n_fields,
}
}
}
impl<'a> Iterator for FieldIterator<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
if self.idx >= self.n_fields {
return None;
}
if let Some(field_name) = self.structure.get_nth_field_name(self.idx) {
self.idx += 1;
Some(field_name)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.idx == self.n_fields {
return (0, Some(0));
}
let remaining = (self.n_fields - self.idx) as usize;
(remaining, Some(remaining))
}
}
impl<'a> DoubleEndedIterator for FieldIterator<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.idx == self.n_fields {
return None;
}
self.n_fields -= 1;
if let Some(field_name) = self.structure.get_nth_field_name(self.n_fields) {
Some(field_name)
} else {
None
}
}
}
impl<'a> ExactSizeIterator for FieldIterator<'a> {}
pub struct Iter<'a> {
iter: FieldIterator<'a>,
}
impl<'a> Iter<'a> {
2017-08-30 11:39:09 +00:00
fn new(structure: &'a StructureRef) -> Iter<'a> {
skip_assert_initialized!();
Iter {
iter: FieldIterator::new(structure),
}
2017-07-07 13:04:54 +00:00
}
}
impl<'a> Iterator for Iter<'a> {
type Item = (&'a str, &'a SendValue);
2017-07-07 13:04:54 +00:00
fn next(&mut self) -> Option<(&'a str, &'a SendValue)> {
2017-07-07 13:04:54 +00:00
if let Some(f) = self.iter.next() {
let v = self.iter.structure.get_value(f);
Some((f, v.unwrap()))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if let Some(f) = self.iter.next_back() {
let v = self.iter.structure.get_value(f);
Some((f, v.unwrap()))
} else {
None
}
}
}
impl<'a> ExactSizeIterator for Iter<'a> {}
2017-11-11 12:14:49 +00:00
pub struct Builder {
s: Structure,
}
impl Builder {
fn new(name: &str) -> Self {
Builder {
s: Structure::new_empty(name),
}
}
pub fn field<V: ToSendValue>(mut self, name: &str, value: &V) -> Self {
2017-11-11 12:14:49 +00:00
self.s.set(name, value);
self
}
pub fn build(self) -> Structure {
self.s
}
}
2017-07-07 13:04:54 +00:00
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_set_get() {
::init().unwrap();
let mut s = Structure::new_empty("test");
assert_eq!(s.get_name(), "test");
s.set("f1", &"abc");
s.set("f2", &String::from("bcd"));
s.set("f3", &123i32);
2017-07-07 13:04:54 +00:00
assert_eq!(s.get::<&str>("f1").unwrap(), "abc");
assert_eq!(s.get::<&str>("f2").unwrap(), "bcd");
assert_eq!(s.get::<i32>("f3").unwrap(), 123i32);
assert_eq!(s.fields().collect::<Vec<_>>(), vec!["f1", "f2", "f3"]);
2017-07-12 07:38:34 +00:00
let v = s.iter().map(|(f, v)| (f, v.clone())).collect::<Vec<_>>();
assert_eq!(v.len(), 3);
assert_eq!(v[0].0, "f1");
assert_eq!(v[0].1.get::<&str>().unwrap(), "abc");
assert_eq!(v[1].0, "f2");
assert_eq!(v[1].1.get::<&str>().unwrap(), "bcd");
assert_eq!(v[2].0, "f3");
assert_eq!(v[2].1.get::<i32>().unwrap(), 123i32);
2017-07-07 13:04:54 +00:00
let s2 = Structure::new("test", &[("f1", &"abc"), ("f2", &"bcd"), ("f3", &123i32)]);
2017-07-07 13:04:54 +00:00
assert_eq!(s, s2);
}
2017-11-11 12:14:49 +00:00
#[test]
fn test_builder() {
::init().unwrap();
let s = Structure::builder("test")
.field("f1", &"abc")
.field("f2", &String::from("bcd"))
.field("f3", &123i32)
2017-11-11 12:14:49 +00:00
.build();
assert_eq!(s.get_name(), "test");
assert_eq!(s.get::<&str>("f1").unwrap(), "abc");
assert_eq!(s.get::<&str>("f2").unwrap(), "bcd");
assert_eq!(s.get::<i32>("f3").unwrap(), 123i32);
}
2018-04-06 10:01:17 +00:00
#[test]
fn test_string_conversion() {
let a = "Test, f1=(string)abc, f2=(uint)123;";
2018-04-06 10:01:17 +00:00
let s = Structure::from_string(&a).unwrap();
assert_eq!(s.get::<&str>("f1").unwrap(), "abc");
assert_eq!(s.get::<u32>("f2").unwrap(), 123);
assert_eq!(a, s.to_string());
}
2017-07-07 13:04:54 +00:00
}