blob: 5864348174e7071887c5231df507c200945d4e75 [file] [log] [blame]
// Copyright 2019 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.
//! Handles negotiating buffer sets with the codec server and sysmem.
use crate::{buffer_collection_constraints::*, Result};
use failure::{self, Fail, ResultExt};
use fidl::endpoints::{create_endpoints, ClientEnd};
use fidl_fuchsia_media::*;
use fidl_fuchsia_sysmem::*;
use fidl_table_validation::{ValidFidlTable, Validate};
use fuchsia_component::client;
use fuchsia_zircon as zx;
use std::{
convert::TryFrom,
fmt,
iter::{IntoIterator, StepBy},
ops::RangeFrom,
};
#[derive(Debug)]
pub enum Error {
ReclaimClientTokenChannel,
ServerOmittedBufferVmo,
PacketReferencesInvalidBuffer,
VmoReadFail(zx::Status),
}
impl fmt::Display for Error {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self, w)
}
}
impl Fail for Error {}
#[allow(unused)]
#[derive(ValidFidlTable, Copy, Clone, Debug, PartialEq)]
#[fidl_table_src(StreamBufferSettings)]
pub struct ValidStreamBufferSettings {
buffer_lifetime_ordinal: u64,
buffer_constraints_version_ordinal: u64,
packet_count_for_server: u32,
packet_count_for_client: u32,
per_packet_buffer_bytes: u32,
#[fidl_field_type(default = false)]
single_buffer_mode: bool,
}
#[allow(unused)]
#[derive(ValidFidlTable)]
#[fidl_table_src(StreamBufferConstraints)]
#[fidl_table_validator(StreamBufferConstraintsValidator)]
pub struct ValidStreamBufferConstraints {
buffer_constraints_version_ordinal: u64,
default_settings: ValidStreamBufferSettings,
per_packet_buffer_bytes_min: u32,
per_packet_buffer_bytes_recommended: u32,
per_packet_buffer_bytes_max: u32,
packet_count_for_server_min: u32,
packet_count_for_server_recommended: u32,
packet_count_for_server_recommended_max: u32,
packet_count_for_server_max: u32,
packet_count_for_client_min: u32,
packet_count_for_client_max: u32,
single_buffer_mode_allowed: bool,
#[fidl_field_type(default = false)]
is_physically_contiguous_required: bool,
#[fidl_field_type(optional)]
very_temp_kludge_bti_handle: Option<zx::Handle>,
}
#[derive(ValidFidlTable)]
#[fidl_table_src(StreamOutputConstraints)]
pub struct ValidStreamOutputConstraints {
pub stream_lifetime_ordinal: u64,
pub buffer_constraints_action_required: bool,
pub buffer_constraints: ValidStreamBufferConstraints,
}
pub struct StreamBufferConstraintsValidator;
#[derive(Debug)]
pub enum StreamBufferConstraintsError {
VersionOrdinalZero,
SingleBufferMode,
ConstraintsNoBtiHandleForPhysicalBuffers,
}
impl Validate<ValidStreamBufferConstraints> for StreamBufferConstraintsValidator {
type Error = StreamBufferConstraintsError;
fn validate(candidate: &ValidStreamBufferConstraints) -> std::result::Result<(), Self::Error> {
if candidate.buffer_constraints_version_ordinal == 0 {
// An ordinal of 0 in StreamBufferConstraints is not allowed.
return Err(StreamBufferConstraintsError::VersionOrdinalZero);
}
if candidate.default_settings.single_buffer_mode {
// StreamBufferConstraints should never suggest single buffer mode.
return Err(StreamBufferConstraintsError::SingleBufferMode);
}
if candidate.is_physically_contiguous_required
&& candidate
.very_temp_kludge_bti_handle
.as_ref()
.map(|h| h.is_invalid())
.unwrap_or(true)
{
// The bti handle must be provided if the buffers need to be physically contiguous.
return Err(StreamBufferConstraintsError::ConstraintsNoBtiHandleForPhysicalBuffers);
}
Ok(())
}
}
/// The pattern to use when advancing ordinals.
#[derive(Debug, Clone, Copy)]
pub enum OrdinalPattern {
/// Odd ordinal pattern starts at 1 and moves in increments of 2: [1,3,5..]
Odd,
/// All ordinal pattern starts at 1 and moves in increments of 1: [1,2,3..]
All,
}
impl IntoIterator for OrdinalPattern {
type Item = u64;
type IntoIter = StepBy<RangeFrom<Self::Item>>;
fn into_iter(self) -> Self::IntoIter {
let (start, step) = match self {
OrdinalPattern::Odd => (1, 2),
OrdinalPattern::All => (1, 1),
};
(start..).step_by(step)
}
}
pub fn get_ordinal(pattern: &mut <OrdinalPattern as IntoIterator>::IntoIter) -> u64 {
pattern.next().expect("Getting next item in infinite pattern")
}
pub enum BufferSetType {
Input,
Output,
}
pub struct BufferSetFactory;
impl BufferSetFactory {
pub async fn buffer_set(
buffer_lifetime_ordinal: u64,
constraints: ValidStreamBufferConstraints,
codec: &mut StreamProcessorProxy,
buffer_set_type: BufferSetType,
buffer_collection_constraints: Option<BufferCollectionConstraints>,
) -> Result<BufferSet> {
let (collection_client, settings) = await!(Self::settings(
buffer_lifetime_ordinal,
constraints,
buffer_collection_constraints
))?;
vlog!(2, "Got settings; waiting for buffers.");
match buffer_set_type {
BufferSetType::Input => codec
.set_input_buffer_partial_settings(settings)
.context("Sending input partial settings to codec")?,
BufferSetType::Output => codec
.set_output_buffer_partial_settings(settings)
.context("Sending output partial settings to codec")?,
};
let (status, collection_info) = await!(collection_client.wait_for_buffers_allocated())
.context("Waiting for buffers")?;
vlog!(2, "Sysmem responded: {:?}", status);
let collection_info = zx::Status::ok(status).map(|_| collection_info)?;
if let BufferSetType::Output = buffer_set_type {
vlog!(2, "Completing settings for output.");
codec.complete_output_buffer_partial_settings(buffer_lifetime_ordinal)?;
}
//collection_client.close()?;
vlog!(
2,
"Got {} buffers of size {:?}",
collection_info.buffer_count,
collection_info.settings.buffer_settings.size_bytes
);
vlog!(3, "Buffer collection is: {:#?}", collection_info.settings);
for (i, buffer) in collection_info.buffers.iter().enumerate() {
// We enumerate beyond collection_info.buffer_count just for debugging
// purposes at this log level.
vlog!(3, "Buffer {} is : {:#?}", i, buffer);
}
Ok(BufferSet::try_from(BufferSetSpec {
proxy: collection_client,
buffer_lifetime_ordinal,
collection_info,
})?)
}
async fn settings(
buffer_lifetime_ordinal: u64,
constraints: ValidStreamBufferConstraints,
buffer_collection_constraints: Option<BufferCollectionConstraints>,
) -> Result<(BufferCollectionProxy, StreamBufferPartialSettings)> {
let (client_token, client_token_request) =
create_endpoints::<BufferCollectionTokenMarker>()?;
let (codec_token, codec_token_request) = create_endpoints::<BufferCollectionTokenMarker>()?;
let client_token = client_token.into_proxy()?;
let sysmem_client =
client::connect_to_service::<AllocatorMarker>().context("Connecting to sysmem")?;
sysmem_client
.allocate_shared_collection(client_token_request)
.context("Allocating shared collection")?;
client_token.duplicate(std::u32::MAX, codec_token_request)?;
let (collection_client, collection_request) = create_endpoints::<BufferCollectionMarker>()?;
sysmem_client.bind_shared_collection(
ClientEnd::new(
client_token
.into_channel()
.map_err(|_| Error::ReclaimClientTokenChannel)?
.into_zx_channel(),
),
collection_request,
)?;
let collection_client = collection_client.into_proxy()?;
await!(collection_client.sync()).context("Syncing codec_token_request with sysmem")?;
let mut collection_constraints =
buffer_collection_constraints.unwrap_or(BUFFER_COLLECTION_CONSTRAINTS_DEFAULT);
assert_eq!(
collection_constraints.min_buffer_count_for_camping, 0,
"Codecs assert that buffer_count == packet count, so we can't change this yet."
);
collection_constraints.min_buffer_count_for_camping =
constraints.default_settings.packet_count_for_client;
vlog!(3, "Our buffer collection constraints are: {:#?}", collection_constraints);
// By design we must say true even if all our fields are left at
// default, or sysmem will not give us buffer handles.
let has_constraints = true;
collection_client
.set_constraints(has_constraints, &mut collection_constraints)
.context("Sending buffer constraints to sysmem")?;
Ok((
collection_client,
StreamBufferPartialSettings {
buffer_lifetime_ordinal: Some(buffer_lifetime_ordinal),
buffer_constraints_version_ordinal: Some(
constraints.buffer_constraints_version_ordinal,
),
single_buffer_mode: Some(constraints.default_settings.single_buffer_mode),
packet_count_for_server: Some(constraints.default_settings.packet_count_for_server),
packet_count_for_client: Some(constraints.default_settings.packet_count_for_client),
sysmem_token: Some(codec_token),
},
))
}
}
#[derive(ValidFidlTable, Clone, Copy, Debug, PartialEq)]
#[fidl_table_src(PacketHeader)]
pub struct ValidPacketHeader {
pub buffer_lifetime_ordinal: u64,
pub packet_index: u32,
}
#[derive(ValidFidlTable, Clone, Copy, Debug, PartialEq)]
#[fidl_table_src(Packet)]
pub struct ValidPacket {
pub header: ValidPacketHeader,
pub buffer_index: u32,
pub stream_lifetime_ordinal: u64,
pub start_offset: u32,
pub valid_length_bytes: u32,
#[fidl_field_type(optional)]
pub timestamp_ish: Option<u64>,
#[fidl_field_type(default = false)]
pub start_access_unit: bool,
#[fidl_field_type(default = false)]
pub known_end_access_unit: bool,
}
struct BufferSetSpec {
proxy: BufferCollectionProxy,
buffer_lifetime_ordinal: u64,
collection_info: BufferCollectionInfo2,
}
#[derive(Debug, PartialEq)]
pub struct Buffer {
pub data: zx::Vmo,
pub start: u64,
pub size: u64,
}
#[derive(Debug)]
pub struct BufferSet {
pub proxy: BufferCollectionProxy,
pub buffers: Vec<Buffer>,
pub buffer_lifetime_ordinal: u64,
}
impl TryFrom<BufferSetSpec> for BufferSet {
type Error = failure::Error;
fn try_from(mut src: BufferSetSpec) -> std::result::Result<Self, Self::Error> {
let mut buffers = vec![];
for (i, buffer) in src.collection_info.buffers
[0..(src.collection_info.buffer_count as usize)]
.iter_mut()
.enumerate()
{
buffers.push(Buffer {
data: buffer.vmo.take().ok_or(Error::ServerOmittedBufferVmo).context(format!(
"Trying to ingest {}th buffer of {}: {:#?}",
i, src.collection_info.buffer_count, buffer
))?,
start: buffer.vmo_usable_start,
size: src.collection_info.settings.buffer_settings.size_bytes as u64,
});
}
Ok(Self { proxy: src.proxy, buffers, buffer_lifetime_ordinal: src.buffer_lifetime_ordinal })
}
}
impl BufferSet {
pub fn read_packet(&self, packet: &ValidPacket) -> Result<Vec<u8>> {
let buffer = self
.buffers
.get(packet.buffer_index as usize)
.ok_or(Error::PacketReferencesInvalidBuffer)?;
let mut dest = vec![0; packet.valid_length_bytes as usize];
buffer.data.read(&mut dest, packet.start_offset as u64).map_err(Error::VmoReadFail)?;
Ok(dest)
}
}