<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Implementation of a virtio video decoder backed by a device.

use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryInto;

use backend::*;
use base::{error, IntoRawDescriptor, WaitContext};

use crate::virtio::resource_bridge::{
    self, BufferInfo, ResourceInfo, ResourceRequest, ResourceRequestSocket,
};
use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
use crate::virtio::video::command::{QueueType, VideoCmd};
use crate::virtio::video::control::{CtrlType, CtrlVal, QueryCtrlResponse, QueryCtrlType};
use crate::virtio::video::device::*;
use crate::virtio::video::error::*;
use crate::virtio::video::event::*;
use crate::virtio::video::format::*;
use crate::virtio::video::params::Params;
use crate::virtio::video::protocol;
use crate::virtio::video::response::CmdResponse;

mod backend;
mod capability;

use capability::*;

type StreamId = u32;
type ResourceId = u32;

// ResourceId given by the driver
type InputResourceId = u32;
type OutputResourceId = u32;

// Id for a frame buffer passed to Chrome.
// We cannot use OutputResourceId as is because this ID must be between 0 and ((# of buffers) - 1).
//
// TODO(b/1518105): Once we decide to generate resource_id in the device side,
// we don't need this value and can pass OutputResourceId to Chrome directly.
type FrameBufferId = i32;

type ResourceHandle = u32;
type Timestamp = u64;

// The result of OutputResources.queue_resource().
enum QueueOutputResourceResult {
    UsingAsEos,                // The resource is kept as EOS buffer.
    Reused(FrameBufferId),     // The resource has been registered before.
    Registered(FrameBufferId), // The resource is queued first time.
}

#[derive(Default)]
struct InputResources {
    // Timestamp -> InputResourceId
    timestamp_to_res_id: BTreeMap<Timestamp, InputResourceId>,

    // InputResourceId -> ResourceHandle
    res_id_to_res_handle: BTreeMap<InputResourceId, ResourceHandle>,
}

#[derive(Default)]
struct OutputResources {
    // OutputResourceId <-> FrameBufferId
    res_id_to_frame_buf_id: BTreeMap<OutputResourceId, FrameBufferId>,
    frame_buf_id_to_res_id: BTreeMap<FrameBufferId, OutputResourceId>,

    // Store the resource id of the queued output buffers.
    queued_res_ids: BTreeSet<OutputResourceId>,

    // Reserves output resource ID that will be used to notify EOS.
    // If a guest enqueues a resource with this ID, the resource must not be sent to the host.
    // Once the value is set, it won't be changed until resolution is changed or a stream is
    // destroyed.
    eos_resource_id: Option<OutputResourceId>,

    // This is a flag that shows whether the device's set_output_buffer_count is called.
    // This will be set to true when ResourceCreate for OutputBuffer is called for the first time.
    //
    // TODO(b/1518105): This field is added as a hack because the current virtio-video v3 spec
    // doesn't have a way to send a number of frame buffers the guest provides.
    // Once we have the way in the virtio-video protocol, we should remove this flag.
    is_output_buffer_count_set: bool,

    // OutputResourceId -> ResourceHandle
    res_id_to_res_handle: BTreeMap<OutputResourceId, ResourceHandle>,
}

impl OutputResources {
    fn queue_resource(
        &mut self,
        resource_id: OutputResourceId,
    ) -> VideoResult<QueueOutputResourceResult> {
        if !self.queued_res_ids.insert(resource_id) {
            error!("resource_id {} is already queued", resource_id);
            return Err(VideoError::InvalidParameter);
        }

        // Stores an output buffer to notify EOS.
        // This is necessary because libvda is unable to indicate EOS along with returned buffers.
        // For now, when a `Flush()` completes, this saved resource will be returned as a zero-sized
        // buffer with the EOS flag.
        // TODO(b/149725148): Remove this when libvda supports buffer flags.
        if *self.eos_resource_id.get_or_insert(resource_id) == resource_id {
            return Ok(QueueOutputResourceResult::UsingAsEos);
        }

        Ok(match self.res_id_to_frame_buf_id.entry(resource_id) {
            Entry::Occupied(e) => QueueOutputResourceResult::Reused(*e.get()),
            Entry::Vacant(_) => {
                let buffer_id = self.res_id_to_frame_buf_id.len() as FrameBufferId;
                self.res_id_to_frame_buf_id.insert(resource_id, buffer_id);
                self.frame_buf_id_to_res_id.insert(buffer_id, resource_id);
                QueueOutputResourceResult::Registered(buffer_id)
            }
        })
    }

    fn dequeue_frame_buffer(
        &mut self,
        buffer_id: FrameBufferId,
        stream_id: StreamId,
    ) -> Option<ResourceId> {
        let resource_id = match self.frame_buf_id_to_res_id.get(&buffer_id) {
            Some(id) => *id,
            None => {
                error!(
                    "unknown frame buffer id {} for stream {}",
                    buffer_id, stream_id
                );
                return None;
            }
        };

        self.queued_res_ids.take(&resource_id).or_else(|| {
            error!(
                "resource_id {} is not enqueued for stream {}",
                resource_id, stream_id
            );
            None
        })
    }

    fn dequeue_eos_resource_id(&mut self) -> Option<OutputResourceId> {
        self.queued_res_ids.take(&self.eos_resource_id?)
    }

    fn set_output_buffer_count(&mut self) -> bool {
        if !self.is_output_buffer_count_set {
            self.is_output_buffer_count_set = true;
            return true;
        }
        false
    }
}

// Context is associated with one `DecoderSession`, which corresponds to one stream from the
// virtio-video's point of view.
#[derive(Default)]
struct Context<S: DecoderSession> {
    stream_id: StreamId,

    in_params: Params,
    out_params: Params,

    in_res: InputResources,
    out_res: OutputResources,

    // Set the flag if we need to clear output resource when the output queue is cleared next time.
    is_clear_out_res_needed: bool,

    session: Option<S>,
}

impl<S: DecoderSession> Context<S> {
    fn new(stream_id: StreamId, format: Format) -> Self {
        Context {
            stream_id,
            in_params: Params {
                format: Some(format),
                min_buffers: 2,
                max_buffers: 32,
                plane_formats: vec![Default::default()],
                ..Default::default()
            },<commit_msg>virtio: video: change minimum input buffer of decoder to 1<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Implementation of a virtio video decoder backed by a device.

use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryInto;

use backend::*;
use base::{error, IntoRawDescriptor, WaitContext};

use crate::virtio::resource_bridge::{
    self, BufferInfo, ResourceInfo, ResourceRequest, ResourceRequestSocket,
};
use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
use crate::virtio::video::command::{QueueType, VideoCmd};
use crate::virtio::video::control::{CtrlType, CtrlVal, QueryCtrlResponse, QueryCtrlType};
use crate::virtio::video::device::*;
use crate::virtio::video::error::*;
use crate::virtio::video::event::*;
use crate::virtio::video::format::*;
use crate::virtio::video::params::Params;
use crate::virtio::video::protocol;
use crate::virtio::video::response::CmdResponse;

mod backend;
mod capability;

use capability::*;

type StreamId = u32;
type ResourceId = u32;

// ResourceId given by the driver
type InputResourceId = u32;
type OutputResourceId = u32;

// Id for a frame buffer passed to Chrome.
// We cannot use OutputResourceId as is because this ID must be between 0 and ((# of buffers) - 1).
//
// TODO(b/1518105): Once we decide to generate resource_id in the device side,
// we don't need this value and can pass OutputResourceId to Chrome directly.
type FrameBufferId = i32;

type ResourceHandle = u32;
type Timestamp = u64;

// The result of OutputResources.queue_resource().
enum QueueOutputResourceResult {
    UsingAsEos,                // The resource is kept as EOS buffer.
    Reused(FrameBufferId),     // The resource has been registered before.
    Registered(FrameBufferId), // The resource is queued first time.
}

#[derive(Default)]
struct InputResources {
    // Timestamp -> InputResourceId
    timestamp_to_res_id: BTreeMap<Timestamp, InputResourceId>,

    // InputResourceId -> ResourceHandle
    res_id_to_res_handle: BTreeMap<InputResourceId, ResourceHandle>,
}

#[derive(Default)]
struct OutputResources {
    // OutputResourceId <-> FrameBufferId
    res_id_to_frame_buf_id: BTreeMap<OutputResourceId, FrameBufferId>,
    frame_buf_id_to_res_id: BTreeMap<FrameBufferId, OutputResourceId>,

    // Store the resource id of the queued output buffers.
    queued_res_ids: BTreeSet<OutputResourceId>,

    // Reserves output resource ID that will be used to notify EOS.
    // If a guest enqueues a resource with this ID, the resource must not be sent to the host.
    // Once the value is set, it won't be changed until resolution is changed or a stream is
    // destroyed.
    eos_resource_id: Option<OutputResourceId>,

    // This is a flag that shows whether the device's set_output_buffer_count is called.
    // This will be set to true when ResourceCreate for OutputBuffer is called for the first time.
    //
    // TODO(b/1518105): This field is added as a hack because the current virtio-video v3 spec
    // doesn't have a way to send a number of frame buffers the guest provides.
    // Once we have the way in the virtio-video protocol, we should remove this flag.
    is_output_buffer_count_set: bool,

    // OutputResourceId -> ResourceHandle
    res_id_to_res_handle: BTreeMap<OutputResourceId, ResourceHandle>,
}

impl OutputResources {
    fn queue_resource(
        &mut self,
        resource_id: OutputResourceId,
    ) -> VideoResult<QueueOutputResourceResult> {
        if !self.queued_res_ids.insert(resource_id) {
            error!("resource_id {} is already queued", resource_id);
            return Err(VideoError::InvalidParameter);
        }

        // Stores an output buffer to notify EOS.
        // This is necessary because libvda is unable to indicate EOS along with returned buffers.
        // For now, when a `Flush()` completes, this saved resource will be returned as a zero-sized
        // buffer with the EOS flag.
        // TODO(b/149725148): Remove this when libvda supports buffer flags.
        if *self.eos_resource_id.get_or_insert(resource_id) == resource_id {
            return Ok(QueueOutputResourceResult::UsingAsEos);
        }

        Ok(match self.res_id_to_frame_buf_id.entry(resource_id) {
            Entry::Occupied(e) => QueueOutputResourceResult::Reused(*e.get()),
            Entry::Vacant(_) => {
                let buffer_id = self.res_id_to_frame_buf_id.len() as FrameBufferId;
                self.res_id_to_frame_buf_id.insert(resource_id, buffer_id);
                self.frame_buf_id_to_res_id.insert(buffer_id, resource_id);
                QueueOutputResourceResult::Registered(buffer_id)
            }
        })
    }

    fn dequeue_frame_buffer(
        &mut self,
        buffer_id: FrameBufferId,
        stream_id: StreamId,
    ) -> Option<ResourceId> {
        let resource_id = match self.frame_buf_id_to_res_id.get(&buffer_id) {
            Some(id) => *id,
            None => {
                error!(
                    "unknown frame buffer id {} for stream {}",
                    buffer_id, stream_id
                );
                return None;
            }
        };

        self.queued_res_ids.take(&resource_id).or_else(|| {
            error!(
                "resource_id {} is not enqueued for stream {}",
                resource_id, stream_id
            );
            None
        })
    }

    fn dequeue_eos_resource_id(&mut self) -> Option<OutputResourceId> {
        self.queued_res_ids.take(&self.eos_resource_id?)
    }

    fn set_output_buffer_count(&mut self) -> bool {
        if !self.is_output_buffer_count_set {
            self.is_output_buffer_count_set = true;
            return true;
        }
        false
    }
}

// Context is associated with one `DecoderSession`, which corresponds to one stream from the
// virtio-video's point of view.
#[derive(Default)]
struct Context<S: DecoderSession> {
    stream_id: StreamId,

    in_params: Params,
    out_params: Params,

    in_res: InputResources,
    out_res: OutputResources,

    // Set the flag if we need to clear output resource when the output queue is cleared next time.
    is_clear_out_res_needed: bool,

    session: Option<S>,
}

impl<S: DecoderSession> Context<S> {
    fn new(stream_id: StreamId, format: Format) -> Self {
        Context {
            stream_id,
            in_params: Params {
                format: Some(format),
                min_buffers: 1,
                max_buffers: 32,
                plane_formats: vec![Default::default()],
                ..Default::default()
            },
{'fraction_non_alphanumeric': 0.09682928640380188, 'fraction_numerical': 0.007054280834632806, 'mean_word_length': 2.896990740740741, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2874741', 'n_tokens_mistral': 3991, 'n_tokens_neox': 3854, 'n_words': 1147}