blob: c7e30a9eb3fae66af6cf581d8409f288ad06b100 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
bitfield::bitfield,
failure::{bail, format_err, Error, ResultExt},
fidl::endpoints::ClientEnd,
fidl_fuchsia_media::{
AudioSampleFormat, AudioStreamType, MediumSpecificStreamType, SimpleStreamSinkProxy,
StreamPacket, StreamType, AUDIO_ENCODING_SBC,
},
fidl_fuchsia_mediaplayer::{
PlayerEvent, PlayerEventStream, PlayerMarker, PlayerProxy, SourceMarker,
},
fuchsia_zircon as zx,
futures::{stream, StreamExt},
};
const DEFAULT_BUFFER_LEN: usize = 65536;
/// Players are configured and accept media frames, which are sent to the
/// media subsystem.
pub struct Player {
buffer: zx::Vmo,
buffer_len: usize,
codec: String,
current_offset: usize,
stream_source: SimpleStreamSinkProxy,
player: PlayerProxy,
events: PlayerEventStream,
playing: bool,
}
bitfield! {
pub struct SbcHeader(u32);
impl Debug;
u8;
syncword, _: 7, 0;
sampling_frequency, _: 9, 8;
blocks, _: 11, 10;
channel_mode, _: 13, 12;
allocation_method, _: 14;
subbands, _: 15;
bitpool, _: 23, 16;
crccheck, _: 31, 24;
}
impl Player {
/// Attempt to make a new player that decodes and plays frames encoded in the
/// `codec`
// TODO(jamuraa): add encoding parameters for this (SetConfiguration)
pub async fn new(codec: String) -> Result<Player, Error> {
let player = fuchsia_app::client::connect_to_service::<PlayerMarker>()
.context("Failed to connect to media player")?;
let (source_client, source) = fidl::endpoints::create_endpoints()?;
let source_proxy = source_client.into_proxy()?;
player.create_stream_source(0, false, false, None, source)?;
let audio_stream_type = AudioStreamType {
sample_format: AudioSampleFormat::Signed16,
channels: 2, // Stereo
frames_per_second: 44100, // 44.1kHz
};
let mut stream_type = StreamType {
medium_specific: MediumSpecificStreamType::Audio(audio_stream_type),
encoding: codec.clone(),
encoding_parameters: None,
};
let (stream_source, stream_source_sink) = fidl::endpoints::create_endpoints()?;
let stream_source = stream_source.into_proxy()?;
source_proxy.add_stream(&mut stream_type, 44100, 1, stream_source_sink)?;
// TODO: vmar map this for faster access.
let buffer =
zx::Vmo::create_with_opts(zx::VmoOptions::NON_RESIZABLE, DEFAULT_BUFFER_LEN as u64)?;
stream_source.add_payload_buffer(0, buffer.clone(0, DEFAULT_BUFFER_LEN as u64)?)?;
let mut player_event_stream = player.take_event_stream();
let source_client_channel = source_proxy.into_channel().unwrap().into_zx_channel();
let upcasted_source = ClientEnd::<SourceMarker>::new(source_client_channel);
player.set_source(Some(upcasted_source))?;
// We should be able to wait until either
// (1) audio is connected or
// (2) there is a Problem.
loop {
let x = await!(player_event_stream.next());
if x.is_none() {
// The player closed the event stream, something is wrong.
return Err(format_err!("MediaPlayer closed"));
}
let evt = x.unwrap();
if evt.is_err() {
return Err(evt.unwrap_err().into());
}
let PlayerEvent::OnStatusChanged { player_status } = evt.unwrap();
if let Some(problem) = player_status.problem {
return Err(format_err!(
"Problem setting up: {} - {:?}",
problem.type_,
problem.details
));
}
if player_status.audio_connected {
break;
}
}
Ok(Player {
buffer,
buffer_len: DEFAULT_BUFFER_LEN,
codec,
stream_source,
player,
events: player_event_stream,
current_offset: 0,
playing: false,
})
}
/// Interpret the first four octets of the slice in `bytes` as a little-endian u32
/// Panics if the slice is not at least four octets.
fn as_u32_le(bytes: &[u8]) -> u32 {
((bytes[3] as u32) << 24)
+ ((bytes[2] as u32) << 16)
+ ((bytes[1] as u32) << 8)
+ ((bytes[0] as u32) << 0)
}
/// Given a buffer with an SBC frame at the start, find the length of the
/// SBC frame.
fn find_sbc_frame_len(buf: &[u8]) -> Result<usize, Error> {
const SBC_SYNCWORD: u8 = 0x9c;
if buf[0] != SBC_SYNCWORD {
return Err(format_err!("SBC frame syncword not found"));
}
if buf.len() < 4 {
return Err(format_err!("Buffer too short for header"));
}
let head = SbcHeader(Player::as_u32_le(&buf[0..4]));
let subbands: usize = match head.subbands() {
false => 4,
true => 8,
};
let channels: usize = match head.channel_mode() {
0 => 1,
_ => 2,
};
let blocks: usize = (head.blocks() as usize + 1) * 4;
let bitpool: usize = head.bitpool() as usize;
let joint = 1; // should be 0 if stereo instead
let stereo_frame_length: usize = 4
+ (4 * subbands * channels as usize) / 8 as usize
+ ((joint * subbands + blocks * bitpool) as f64 / 8.0).ceil() as usize;
Ok(stereo_frame_length)
}
/// Accepts a payload which may contain multiple frames and breaks it into
/// frames and sends it to media.
pub fn push_payload(&mut self, payload: &[u8]) -> Result<(), Error> {
let mut offset = 13;
while offset < payload.len() {
if self.codec == AUDIO_ENCODING_SBC {
let len = Player::find_sbc_frame_len(&payload[offset..])?;
if offset + len > payload.len() {
return Err(format_err!("Ran out of buffer for SBC frame"));
}
self.send_frame(&payload[offset..offset + len])?;
offset += len;
} else {
return Err(format_err!("Unrecognized codec!"));
}
}
Ok(())
}
/// Push an encoded media frame into the buffer and signal that it's there to media.
pub fn send_frame(&mut self, frame: &[u8]) -> Result<(), Error> {
if frame.len() > self.buffer_len {
self.stream_source.end_of_stream()?;
bail!("frame is too large for buffer");
}
if self.current_offset + frame.len() > self.buffer_len {
self.current_offset = 0;
}
self.buffer.write(frame, self.current_offset as u64)?;
let mut packet = StreamPacket {
pts: self.current_offset as i64,
payload_buffer_id: 0,
payload_offset: self.current_offset as u64,
payload_size: frame.len() as u64,
buffer_config: 0,
flags: 0,
stream_id: 0,
};
self.stream_source.send_packet_no_reply(&mut packet)?;
self.current_offset += frame.len();
Ok(())
}
pub fn playing(&self) -> bool {
self.playing
}
pub fn play(&mut self) -> Result<(), Error> {
self.player.play()?;
self.playing = true;
Ok(())
}
pub fn pause(&mut self) -> Result<(), Error> {
self.player.pause()?;
self.playing = false;
Ok(())
}
pub fn next_event<'a>(&'a mut self) -> stream::Next<PlayerEventStream> {
self.events.next()
}
}
impl Drop for Player {
fn drop(&mut self) {
self.pause()
.unwrap_or_else(|e| println!("Error in drop: {:}", e));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frame_length() {
assert_eq!(
119,
Player::find_sbc_frame_len(&[156, 189, 53, 102]).unwrap()
);
}
#[test]
#[should_panic(expected = "should have paniced on not enough data")]
fn test_as_u32_le_len() {
let _ = Player::as_u32_le(&[0, 1, 2]);
}
#[test]
fn test_as_u32_le() {
assert_eq!(1, Player::as_u32_le(&[1, 0, 0, 0]));
assert_eq!(0xff00ff00, Player::as_u32_le(&[0, 0xff, 0, 0xff]));
assert_eq!(0xffffffff, Player::as_u32_le(&[0xff, 0xff, 0xff, 0xff]));
}
}