blob: d04069b50f11463fc541662c144ec07ed366d67d [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.
#ifndef GARNET_LIB_MEDIA_TEST_INCLUDE_LIB_MEDIA_TEST_FRAME_SINK_H_
#define GARNET_LIB_MEDIA_TEST_INCLUDE_LIB_MEDIA_TEST_FRAME_SINK_H_
#include <fuchsia/mediacodec/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/component/cpp/startup_context.h>
#include <lib/fit/function.h>
#include <src/lib/fxl/macros.h>
#include <lib/ui/base_view/cpp/view_provider_component.h>
#include <lib/zx/vmo.h>
#include <stdint.h>
#include <memory>
#include <set>
class FrameSinkView;
// FrameSink will deliver frames to Scenic via ImagePipe, with a presentation
// timestamp set to when PutFrame() was called. This should visually show decode
// throughput, for now. A .h264 file doesn't have real timestamps, but we could
// potentially add a flag to clock the frames out at a particular frame rate.
//
// This class is designed such that frames, views, and stream signal methods
// should be called on the main_loop_. WaitForView must be called on a separate
// thread while main_loop_ runs to allow Scenic time to connect before we send
// frames.
class FrameSink {
public:
// Only designed to be managed via unique_ptr<>, so this static factory method
// is how we create these.
//
// |startup_context| must out-last the FrameSink
// |main_loop| must out-last the FrameSink
// |frames_per_second| if not exactly 0.0, the frames per second, else use the
// built-in default frames per second.
static std::unique_ptr<FrameSink> Create(
component::StartupContext* startup_context, async::Loop* main_loop,
double frames_per_second,
fit::function<void(FrameSink*)> view_connected_callback);
~FrameSink();
// The on_done will get called on main_loop_'s thread. If the callee
// wants/needs to, the callee can post to a different thread.
void PutFrame(uint32_t image_id, const zx::vmo& vmo, uint64_t vmo_offset,
std::shared_ptr<const fuchsia::media::StreamOutputFormat>
output_format,
fit::closure on_done);
// (Quickly) cause all frames to eventually be returned (assuming the rest of
// the system is cooperating), without forcing discard of previously queued
// frames, and asynchronously call on_frames_returned when all frames are done
// returning.
//
// The on_frames_returned gets called on main_loop_'s thread. If the callee
// wants/needs to, the callee can post to a different thread.
void PutEndOfStreamThenWaitForFramesReturnedAsync(
fit::closure on_frames_returned);
void AddFrameSinkView(FrameSinkView* view);
void RemoveFrameSinkView(FrameSinkView* view);
// This is not used for any calls to PutFrame(), rather only internally
// within calls to PutEndOfStreamThenWaitForFramesReturnedAsync(). This is
// public only so that FrameSinkView can see it.
constexpr static uint32_t kBlankFrameImageId =
std::numeric_limits<uint32_t>::max();
private:
FrameSink(component::StartupContext* startup_context, async::Loop* main_loop,
double frames_per_second,
fit::function<void(FrameSink*)> view_connected_callback);
void CheckIfAllFramesReturned();
component::StartupContext* startup_context_ = nullptr;
async::Loop* main_loop_ = nullptr;
const double frames_per_second_ = 0.0;
// The actual views are owned by the view_provider_app_ with no
// super-straightforward way for PutFrame() to find them, so we instead have
// our views register themselves with the FrameSink as they're
// created/destroyed, and we make sure to only interact with them on main_loop
// so we don't need a lock for the set of views. These pointers are not owning
// pointers. Any given pointer in here is removed from the set in the view's
// destructor, so that avoids this set pointing to a view that doesn't exist.
// Typically there will be only one view in steady-state in typical usage of
// FrameSink, but this should still allow for more than one.
std::set<FrameSinkView*> views_;
// During destruction of view_provider_app_, some views_.erase() happens. For
// this reason, and because we want to be able to assert in ~FrameSink that
// there are zero views, we use a unique_ptr<> here so we can delete
// view_provider_app_ early during ~FrameSink.
std::unique_ptr<scenic::ViewProviderComponent> view_provider_component_;
uint32_t frames_outstanding_ = 0;
fit::closure on_frames_returned_;
fit::function<void(FrameSink*)> view_connected_callback_;
zx_time_t last_requested_present_time_ = ZX_TIME_INFINITE_PAST;
FXL_DISALLOW_COPY_AND_ASSIGN(FrameSink);
};
#endif // GARNET_LIB_MEDIA_TEST_INCLUDE_LIB_MEDIA_TEST_FRAME_SINK_H_