[M120 Migration][VD] Enable direct rendering for TVPlus
[platform/framework/web/chromium-efl.git] / fuchsia_web / shell / cast_streaming_shell.cc
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <fuchsia/element/cpp/fidl.h>
6 #include <fuchsia/io/cpp/fidl.h>
7 #include <fuchsia/web/cpp/fidl.h>
8 #include <lib/sys/cpp/component_context.h>
9 #include <lib/sys/cpp/service_directory.h>
10
11 #include "base/base_paths.h"
12 #include "base/check.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/fuchsia/file_utils.h"
17 #include "base/fuchsia/fuchsia_logging.h"
18 #include "base/fuchsia/mem_buffer_util.h"
19 #include "base/fuchsia/process_context.h"
20 #include "base/logging.h"
21 #include "base/path_service.h"
22 #include "base/run_loop.h"
23 #include "base/task/single_thread_task_executor.h"
24 #include "base/test/test_future.h"
25 #include "base/time/time.h"
26 #include "components/cast/message_port/fuchsia/create_web_message.h"
27 #include "components/cast/message_port/platform_message_port.h"
28 #include "components/cast_streaming/test/cast_streaming_test_sender.h"
29 #include "components/fuchsia_component_support/annotations_manager.h"
30 #include "fuchsia_web/cast_streaming/cast_streaming.h"
31 #include "fuchsia_web/common/init_logging.h"
32 #include "fuchsia_web/common/test/fit_adapter.h"
33 #include "fuchsia_web/common/test/frame_test_util.h"
34 #include "fuchsia_web/shell/present_frame.h"
35 #include "fuchsia_web/shell/remote_debugging_port.h"
36 #include "fuchsia_web/shell/shell_relauncher.h"
37 #include "fuchsia_web/webengine/switches.h"
38 #include "fuchsia_web/webinstance_host/web_instance_host.h"
39 #include "media/base/media_util.h"
40 #include "media/gpu/test/video_test_helpers.h"
41 #include "third_party/abseil-cpp/absl/types/optional.h"
42
43 namespace {
44
45 // Identifier for JavaScript to be injected, only relevant if injecting multiple
46 // JavaScripts.
47 constexpr int kAddBeforeLoadJavaScriptID = 0;
48
49 void PrintUsage() {
50   std::cerr << "Usage: "
51             << base::CommandLine::ForCurrentProcess()->GetProgram().BaseName()
52             << " [--" << kRemoteDebuggingPortSwitch
53             << "=<port>] [-- [--{extra_flag1}] [--{extra_flag2}]]\n"
54             << "Setting " << kRemoteDebuggingPortSwitch << "=0"
55             << " will automatically choose an available port.\n"
56             << "Extra flags will be passed to WebEngine to be processed.\n";
57 }
58
59 media::VideoDecoderConfig GetDefaultVideoConfig() {
60   constexpr gfx::Size kVideoSize = {1280, 720};
61   constexpr gfx::Rect kVideoRect(kVideoSize);
62
63   return media::VideoDecoderConfig(
64       media::VideoCodec::kVP8, media::VideoCodecProfile::VP8PROFILE_MIN,
65       media::VideoDecoderConfig::AlphaMode::kIsOpaque, media::VideoColorSpace(),
66       media::VideoTransformation(), kVideoSize, kVideoRect, kVideoSize,
67       media::EmptyExtraData(), media::EncryptionScheme::kUnencrypted);
68 }
69
70 // Set up content directory and context params.
71 fuchsia::web::CreateContextParams GetCreateContextParams(
72     uint16_t remote_debugging_port) {
73   // Configure the fuchsia-dir://cast-streaming/ directory.
74   fuchsia::web::CreateContextParams create_context_params;
75   ApplyCastStreamingContextParams(&create_context_params);
76
77   // Enable other WebEngine features.
78   fuchsia::web::ContextFeatureFlags features =
79       fuchsia::web::ContextFeatureFlags::AUDIO |
80       fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER |
81       fuchsia::web::ContextFeatureFlags::NETWORK |
82       fuchsia::web::ContextFeatureFlags::VULKAN;
83   create_context_params.set_features(features);
84
85   create_context_params.set_remote_debugging_port(remote_debugging_port);
86
87   return create_context_params;
88 }
89
90 // Set autoplay, enable all logging, and present fullscreen view of `frame`.
91 absl::optional<fuchsia::element::GraphicalPresenterPtr> ConfigureFrame(
92     fuchsia::web::Frame* frame,
93     fidl::InterfaceHandle<fuchsia::element::AnnotationController>
94         annotation_controller) {
95   fuchsia::web::ContentAreaSettings settings;
96   settings.set_autoplay_policy(fuchsia::web::AutoplayPolicy::ALLOW);
97   frame->SetContentAreaSettings(std::move(settings));
98   frame->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::DEBUG);
99   return PresentFrame(frame, std::move(annotation_controller));
100 }
101
102 }  // namespace
103
104 int main(int argc, char** argv) {
105   base::CommandLine::Init(argc, argv);
106
107   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
108   CHECK(InitLoggingFromCommandLine(*command_line));
109
110   base::SingleThreadTaskExecutor executor(base::MessagePumpType::IO);
111
112   if (auto optional_exit_code = RelaunchForWebInstanceHostIfParent(
113           "#meta/cast_streaming_shell_for_web_instance_host.cm", *command_line);
114       optional_exit_code.has_value()) {
115     return optional_exit_code.value();
116   }
117
118   absl::optional<uint16_t> remote_debugging_port =
119       GetRemoteDebuggingPort(*command_line);
120   if (!remote_debugging_port) {
121     PrintUsage();
122     return 1;
123   }
124
125   // Instantiate Web Instance Host.
126   WebInstanceHostWithServicesFromThisComponent web_instance_host(
127       *base::ComponentContextForProcess()->outgoing(),
128       /*is_web_instance_component_in_same_package=*/false);
129   fidl::InterfaceRequest<fuchsia::io::Directory> services_request;
130   auto services = sys::ServiceDirectory::CreateWithRequest(&services_request);
131   base::CommandLine child_command_line =
132       base::CommandLine(command_line->GetArgs());
133   child_command_line.AppendSwitch(switches::kEnableCastStreamingReceiver);
134   zx_status_t result = web_instance_host.CreateInstanceForContextWithCopiedArgs(
135       GetCreateContextParams(remote_debugging_port.value()),
136       std::move(services_request), child_command_line);
137   if (result != ZX_OK) {
138     ZX_LOG(ERROR, result) << "CreateInstanceForContextWithCopiedArgs failed";
139     return 2;
140   }
141
142   base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo();
143
144   // Create the browser `context`.
145   fuchsia::web::ContextPtr context;
146   services->Connect(context.NewRequest());
147
148   base::RunLoop run_loop;
149
150   context.set_error_handler(
151       [quit_run_loop = run_loop.QuitClosure()](zx_status_t status) {
152         ZX_LOG(ERROR, status) << "Context connection lost:";
153         quit_run_loop.Run();
154       });
155
156   // Create the browser `frame`.
157   fuchsia::web::CreateFrameParams frame_params;
158   frame_params.set_enable_remote_debugging(true);
159
160   fuchsia::web::FramePtr frame;
161   context->CreateFrameWithParams(std::move(frame_params), frame.NewRequest());
162   frame.set_error_handler(
163       [quit_run_loop = run_loop.QuitClosure()](zx_status_t status) {
164         ZX_LOG(ERROR, status) << "Frame connection lost:";
165         quit_run_loop.Run();
166       });
167
168   // The underlying PresentView call expects an AnnotationController and will
169   // return PresentViewError.INVALID_ARGS without one. The AnnotationController
170   // should serve WatchAnnotations, but it doesn't need to do anything.
171   // TODO(b/264899156): Remove this when AnnotationController becomes
172   // optional.
173   auto annotations_manager =
174       std::make_unique<fuchsia_component_support::AnnotationsManager>();
175   fuchsia::element::AnnotationControllerPtr annotation_controller;
176   annotations_manager->Connect(annotation_controller.NewRequest());
177   auto presenter =
178       ConfigureFrame(frame.get(), std::move(annotation_controller));
179
180   // Register the MessagePort for the Cast Streaming Receiver.
181   std::unique_ptr<cast_api_bindings::MessagePort> sender_message_port;
182   std::unique_ptr<cast_api_bindings::MessagePort> receiver_message_port;
183   cast_api_bindings::CreatePlatformMessagePortPair(&sender_message_port,
184                                                    &receiver_message_port);
185
186   constexpr char kCastStreamingMessagePortOrigin[] = "cast-streaming:receiver";
187   base::test::TestFuture<fuchsia::web::Frame_PostMessage_Result>
188       post_message_result;
189   frame->PostMessage(kCastStreamingMessagePortOrigin,
190                      CreateWebMessage("", std::move(receiver_message_port)),
191                      CallbackToFitFunction(post_message_result.GetCallback()));
192   if (!post_message_result.Wait()) {
193     LOG(ERROR) << "PostMessage timed out.";
194     return 1;
195   }
196   if (post_message_result.Get().is_err()) {
197     LOG(ERROR) << "PostMessage failed.";
198     return 1;
199   }
200
201   // Inject JavaScript test harness into receiver.html.
202   base::test::TestFuture<fuchsia::web::Frame_AddBeforeLoadJavaScript_Result>
203       add_before_load_javascript_result;
204   base::FilePath pkg_path;
205   CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &pkg_path));
206   base::FilePath test_harness_js_path(pkg_path.AppendASCII(
207       "fuchsia_web/shell/cast_streaming_shell_data/injector.js"));
208   std::string test_harness_string;
209   CHECK(base::ReadFileToString(test_harness_js_path, &test_harness_string));
210   frame->AddBeforeLoadJavaScript(
211       kAddBeforeLoadJavaScriptID, {"*"},
212       base::MemBufferFromString(std::move(test_harness_string),
213                                 "test-harness-js"),
214       CallbackToFitFunction(add_before_load_javascript_result.GetCallback()));
215   if (!add_before_load_javascript_result.Wait()) {
216     LOG(ERROR) << "AddBeforeLoadJavaScript timed out.";
217     return 1;
218   }
219   if (add_before_load_javascript_result.Get().is_err()) {
220     LOG(ERROR) << "AddBeforeLoadJavaScript failed.";
221     return 1;
222   }
223
224   // Send `sender_message_port` to a Sender and start it.
225   cast_streaming::CastStreamingTestSender sender;
226   sender.Start(std::move(sender_message_port), net::IPAddress::IPv6Localhost(),
227                absl::nullopt, GetDefaultVideoConfig());
228
229   // Navigate `frame` to `receiver.html`.
230   fuchsia::web::LoadUrlParams load_params;
231   load_params.set_type(fuchsia::web::LoadUrlReason::TYPED);
232   load_params.set_was_user_activated(true);
233   fuchsia::web::NavigationControllerPtr nav_controller;
234   frame->GetNavigationController(nav_controller.NewRequest());
235   if (!LoadUrlAndExpectResponse(nav_controller, std::move(load_params),
236                                 kCastStreamingWebUrl)) {
237     LOG(ERROR) << "LoadUrl failed.";
238     return 1;
239   }
240
241   // Log the debugging port, if debugging is requested.
242   base::test::TestFuture<fuchsia::web::Context_GetRemoteDebuggingPort_Result>
243       get_remote_debugging_port_result;
244   context->GetRemoteDebuggingPort(
245       CallbackToFitFunction(get_remote_debugging_port_result.GetCallback()));
246   if (!get_remote_debugging_port_result.Wait()) {
247     LOG(ERROR) << "Remote debugging service timed out.";
248     return 1;
249   }
250   if (get_remote_debugging_port_result.Get().is_err()) {
251     LOG(ERROR) << "Remote debugging service was not opened.";
252     return 1;
253   }
254   LOG(INFO) << "Remote debugging port: "
255             << get_remote_debugging_port_result.Get().response().port;
256
257   if (!sender.RunUntilActive()) {
258     LOG(ERROR) << "RunUntilActive failed.";
259     return 1;
260   }
261
262   // Load video.
263   base::FilePath video_file(
264       pkg_path.AppendASCII("media/test/data/bear-1280x720.ivf"));
265   absl::optional<std::vector<uint8_t>> video_stream =
266       base::ReadFileToBytes(video_file);
267   CHECK(video_stream.has_value());
268   media::test::EncodedDataHelper video_helper(video_stream.value(),
269                                               media::VideoCodec::kVP8);
270
271   // Send first key frame.
272   scoped_refptr<media::DecoderBuffer> video_decoder_buffer =
273       video_helper.GetNextBuffer();
274   video_decoder_buffer->set_timestamp(base::TimeDelta());
275   video_decoder_buffer->set_is_key_frame(true);
276   sender.SendVideoBuffer(video_decoder_buffer);
277
278   run_loop.Run();
279
280   return 0;
281 }