Shaka Player Embedded
player.cc
Go to the documentation of this file.
1 // Copyright 2016 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "shaka/player.h"
16 
17 #include <functional>
18 #include <list>
19 
20 #include "shaka/version.h"
23 #include "src/debug/mutex.h"
24 #include "src/js/dom/document.h"
25 #include "src/js/manifest.h"
27 #include "src/js/net.h"
28 #include "src/js/player_externs.h"
29 #include "src/js/stats.h"
30 #include "src/js/track.h"
32 #include "src/mapping/convert_js.h"
33 #include "src/mapping/js_engine.h"
34 #include "src/mapping/js_utils.h"
36 #include "src/mapping/promise.h"
37 #include "src/util/macros.h"
38 #include "src/util/utils.h"
39 
40 // Declared in version.h by generated code in //shaka/tools/version.py.
41 extern "C" const char* GetShakaEmbeddedVersion(void) {
42  return SHAKA_VERSION_STR;
43 }
44 
45 namespace shaka {
46 
47 namespace {
48 
55 class LoadHelper : public GenericConverter {
56  public:
57  explicit LoadHelper(double value) : value_(value) {}
58 
59  bool TryConvert(Handle<JsValue> /* value */) override {
60  LOG(FATAL) << "Not reached";
61  }
62 
63  ReturnVal<JsValue> ToJsValue() const override {
64  return !isnan(value_) ? ::shaka::ToJsValue(value_) : JsUndefined();
65  }
66 
67  private:
68  const double value_;
69 };
70 
71 } // namespace
72 
73 template <>
74 struct impl::ConvertHelper<Player::LogLevel> {
75  static bool FromJsValue(Handle<JsValue> source, Player::LogLevel* level) {
76  int value;
77  if (!shaka::FromJsValue(source, &value))
78  return false;
79  *level = static_cast<Player::LogLevel>(value);
80  return true;
81  }
82 };
83 
84 
85 class Player::Impl : public JsObjectWrapper {
86  public:
87  explicit Impl(JsManager* engine) : filters_mutex_("Player::Impl") {
88  CHECK(engine) << "Must pass a JsManager instance";
89  }
90  ~Impl() {
91  if (object_)
92  CallMethod<void>("destroy").wait();
93  if (video_)
94  video_->Detach();
95  }
96 
98 
100  media::MediaPlayer* player) {
101  // This function can be called immediately after the JsManager
102  // constructor. Since the Environment might not be setup yet, run this in
103  // an internal task so we know it is ready.
104  DCHECK(!JsManagerImpl::Instance()->MainThread()->BelongsToCurrentThread());
105  const auto callback = [=]() -> Converter<void>::variant_type {
106  LocalVar<JsValue> player_ctor = GetDescendant(
107  JsEngine::Instance()->global_handle(), {"shaka", "Player"});
108  if (GetValueType(player_ctor) != proto::ValueType::Function) {
109  LOG(ERROR) << "Cannot get 'shaka.Player' object; is "
110  "shaka-player.compiled.js corrupted?";
111  return Error("The constructor 'shaka.Player' is not found.");
112  }
113  LocalVar<JsFunction> player_ctor_func =
114  UnsafeJsCast<JsFunction>(player_ctor);
115 
116  LocalVar<JsValue> result_or_except;
117  std::vector<LocalVar<JsValue>> args;
118  if (player) {
119  video_ = new js::mse::HTMLVideoElement(
121  args.emplace_back(video_->JsThis());
122  }
123  if (!InvokeConstructor(player_ctor_func, args.size(), args.data(),
124  &result_or_except)) {
125  return ConvertError(result_or_except);
126  }
127 
128  Init(UnsafeJsCast<JsObject>(result_or_except));
129  return AttachListeners(client);
130  };
131  return JsManagerImpl::Instance()
132  ->MainThread()
133  ->AddInternalTask(TaskPriority::Internal, "Player ctor",
134  std::move(callback))
135  ->future();
136  }
137 
141  auto future = CallMethod<void>("attach", new_elem);
142  return SetVideoWhenResolved(future, new_elem);
143  }
144 
146  auto future = CallMethod<void>("detach");
147  return SetVideoWhenResolved(future, nullptr);
148  }
149 
150  template <typename T>
152  const std::string& name_path) {
153  auto callback = std::bind(&Impl::GetConfigValueRaw<T>, this, name_path);
155  std::move(callback));
156  }
157 
159  std::unique_lock<Mutex> lock(filters_mutex_);
160  filters_.emplace_back(filters);
161  }
162 
164  std::unique_lock<Mutex> lock(filters_mutex_);
165  for (NetworkFilters*& elem : filters_) {
166  // Don't remove from the list so we don't invalidate the iterator while we
167  // are processing a request.
168  if (elem == filters)
169  elem = nullptr;
170  }
171  }
172 
173  void* GetRawJsValue() {
174  return &object_;
175  }
176 
177  private:
178  Converter<void>::future_type SetVideoWhenResolved(
181  auto then = [=]() -> Converter<void>::variant_type {
182  auto results = future.get();
183  if (!holds_alternative<Error>(results)) {
184  if (video_)
185  video_->Detach();
186  video_ = video;
187  }
188  return results;
189  };
190  // Create a std::future that, when get() is called, will call the given
191  // function.
192  return std::async(std::launch::deferred, std::move(then));
193  }
194 
195  template <typename T>
196  using filter_member_t =
197  std::future<optional<Error>> (NetworkFilters::*)(RequestType, T*);
198 
199  template <typename T>
200  typename Converter<T>::variant_type GetConfigValueRaw(
201  const std::string& name_path) {
202  DCHECK(JsManagerImpl::Instance()->MainThread()->BelongsToCurrentThread());
203  LocalVar<JsValue> configuration;
204  auto error = CallMemberFunction(object_, "getConfiguration", 0, nullptr,
205  &configuration);
206  if (holds_alternative<Error>(error))
207  return get<Error>(error);
208 
209  // Split the name path on periods.
210  std::vector<std::string> components = util::StringSplit(name_path, '.');
211 
212  // Navigate through the path.
213  auto result =
214  GetDescendant(UnsafeJsCast<JsObject>(configuration), components);
215 
216  return Converter<T>::Convert(name_path, result);
217  }
218 
219  Converter<void>::variant_type AttachListeners(Client* client) {
220 #define ATTACH(name, call) \
221  do { \
222  const auto ret = AttachEventListener( \
223  name, std::bind(&Client::OnError, client, std::placeholders::_1), \
224  call); \
225  if (holds_alternative<Error>(ret)) \
226  return get<Error>(ret); \
227  } while (false)
228 
229  const auto on_error = [=](Handle<JsObject> event) {
230  LocalVar<JsValue> detail = GetMemberRaw(event, "detail");
231  client->OnError(ConvertError(detail));
232  };
233  ATTACH("error", on_error);
234 
235  const auto on_buffering = [=](Handle<JsObject> event) {
236  LocalVar<JsValue> is_buffering = GetMemberRaw(event, "buffering");
237  bool is_buffering_bool;
238  if (FromJsValue(is_buffering, &is_buffering_bool)) {
239  client->OnBuffering(is_buffering_bool);
240  } else {
241  client->OnError(Error("Bad 'buffering' event from JavaScript Player"));
242  }
243  };
244  ATTACH("buffering", on_buffering);
245 #undef ATTACH
246 
247  auto results = CallMethod<Handle<JsObject>>("getNetworkingEngine").get();
248  if (holds_alternative<Error>(results))
249  return get<Error>(results);
250  JsObjectWrapper net_engine;
251  net_engine.Init(get<Handle<JsObject>>(results));
252 
253  auto req_filter = [this](RequestType type, js::Request request) {
255  std::shared_ptr<Request> pub_request(new Request(std::move(request)));
256  StepNetworkFilter(type, pub_request, filters_.begin(),
258  return ret;
259  };
260  auto results2 =
261  net_engine.CallMethod<void>("registerRequestFilter", req_filter).get();
262  if (holds_alternative<Error>(results2))
263  return get<Error>(results2);
264 
265  auto resp_filter = [this](RequestType type, js::Response response) {
267  std::shared_ptr<Response> pub_response(new Response(std::move(response)));
268  StepNetworkFilter(type, pub_response, filters_.begin(),
270  return ret;
271  };
272  results2 =
273  net_engine.CallMethod<void>("registerResponseFilter", resp_filter)
274  .get();
275  if (holds_alternative<Error>(results2))
276  return get<Error>(results2);
277 
278  return {};
279  }
280 
281  template <typename T>
282  void StepNetworkFilter(RequestType type, std::shared_ptr<T> obj,
283  std::list<NetworkFilters*>::iterator it,
284  filter_member_t<T> on_filter, Promise results) {
285  {
286  std::unique_lock<Mutex> lock(filters_mutex_);
287  for (; it != filters_.end(); it++) {
288  if (!*it)
289  continue;
290 
291  auto future = ((*it)->*on_filter)(type, obj.get());
292  HandleNetworkFuture(results, std::move(future), [=]() {
293  StepNetworkFilter(type, obj, std::next(it), on_filter, results);
294  });
295  return;
296  }
297  }
298 
299  // Don't call these with the lock held since they can cause Promises to get
300  // handled and can call back into this method.
301  obj->Finalize();
302  results.ResolveWith(JsUndefined(), /* run_events= */ false);
303  }
304 
305  private:
307  Mutex filters_mutex_;
308  std::list<NetworkFilters*> filters_;
309 };
310 
311 // \cond Doxygen_Skip
313 Player::Client::~Client() {}
314 // \endcond Doxygen_Skip
315 
316 void Player::Client::OnError(const Error& /* error */) {}
317 void Player::Client::OnBuffering(bool /* is_buffering */) {}
318 
319 
320 Player::Player(JsManager* engine) : impl_(new Impl(engine)) {}
321 
322 Player::Player(Player&&) = default;
323 
325 
326 Player& Player::operator=(Player&&) = default;
327 
329  DCHECK(engine);
330  return Impl::CallGlobalMethod<void>({"shaka", "log", "setLevel"},
331  static_cast<int>(level));
332 }
333 
335  DCHECK(engine);
336  return Impl::GetGlobalField<Player::LogLevel>(
337  {"shaka", "log", "currentLevel"});
338 }
339 
341  DCHECK(engine);
342  return Impl::GetGlobalField<std::string>({"shaka", "Player", "version"});
343 }
344 
345 
347  media::MediaPlayer* player) {
348  return impl_->Initialize(client, player);
349 }
350 
352  return impl_->CallMethod<void>("destroy");
353 }
354 
355 
357  return impl_->CallMethod<bool>("isAudioOnly");
358 }
359 
361  return impl_->CallMethod<bool>("isBuffering");
362 }
363 
365  return impl_->CallMethod<bool>("isInProgress");
366 }
367 
369  return impl_->CallMethod<bool>("isLive");
370 }
371 
373  return impl_->CallMethod<bool>("isTextTrackVisible");
374 }
375 
377  return impl_->CallMethod<bool>("usingEmbeddedTextTrack");
378 }
379 
380 
382  return impl_->CallMethod<optional<std::string>>("assetUri");
383 }
384 
386  return impl_->CallMethod<optional<shaka::DrmInfo>>("drmInfo");
387 }
388 
390  const {
391  return impl_->CallMethod<std::vector<LanguageRole>>(
392  "getAudioLanguagesAndRoles");
393 }
394 
396  return impl_->CallMethod<BufferedInfo>("getBufferedInfo");
397 }
398 
400  return impl_->CallMethod<double>("getExpiration");
401 }
402 
404  return impl_->CallMethod<Stats>("getStats");
405 }
406 
408  return impl_->CallMethod<std::vector<Track>>("getTextTracks");
409 }
410 
412  return impl_->CallMethod<std::vector<Track>>("getVariantTracks");
413 }
414 
416  const {
417  return impl_->CallMethod<std::vector<LanguageRole>>(
418  "getTextLanguagesAndRoles");
419 }
420 
422  return impl_->CallMethod<std::string>("keySystem");
423 }
424 
426  return impl_->CallMethod<BufferedRange>("seekRange");
427 }
428 
429 
430 AsyncResults<void> Player::Load(const std::string& manifest_uri,
431  double start_time,
432  const std::string& mime_type) {
433  return impl_->CallMethod<void>("load", manifest_uri, LoadHelper(start_time),
434  mime_type);
435 }
436 
438  return impl_->CallMethod<void>("unload");
439 }
440 
441 AsyncResults<bool> Player::Configure(const std::string& name_path,
442  DefaultValueType /* value */) {
443  return impl_->CallMethod<bool>("configure", name_path, Any()); // undefined
444 }
445 
446 AsyncResults<bool> Player::Configure(const std::string& name_path, bool value) {
447  return impl_->CallMethod<bool>("configure", name_path, value);
448 }
449 
450 AsyncResults<bool> Player::Configure(const std::string& name_path,
451  double value) {
452  return impl_->CallMethod<bool>("configure", name_path, value);
453 }
454 
455 AsyncResults<bool> Player::Configure(const std::string& name_path,
456  const std::string& value) {
457  return impl_->CallMethod<bool>("configure", name_path, value);
458 }
459 
460 AsyncResults<bool> Player::GetConfigurationBool(const std::string& name_path) {
461  return impl_->GetConfigValue<bool>(name_path);
462 }
463 
465  const std::string& name_path) {
466  return impl_->GetConfigValue<double>(name_path);
467 }
468 
470  const std::string& name_path) {
471  return impl_->GetConfigValue<std::string>(name_path);
472 }
473 
474 
476  return impl_->CallMethod<void>("resetConfiguration");
477 }
478 
480  return impl_->CallMethod<void>("retryStreaming");
481 }
482 
484  optional<std::string> role) {
485  return impl_->CallMethod<void>("selectAudioLanguage", language, role);
486 }
487 
489  return impl_->CallMethod<void>("selectEmbeddedTextTrack");
490 }
491 
492 AsyncResults<void> Player::SelectTextLanguage(const std::string& language,
493  optional<std::string> role) {
494  return impl_->CallMethod<void>("selectTextLanguage", language, role);
495 }
496 
498  return impl_->CallMethod<void>("selectTextTrack", track.GetInternal());
499 }
500 
502  bool clear_buffer) {
503  return impl_->CallMethod<void>("selectVariantTrack", track.GetInternal(),
504  clear_buffer);
505 }
506 
508  return impl_->CallMethod<void>("setTextTrackVisibility", visibility);
509 }
510 
512  const std::string& language,
513  const std::string& kind,
514  const std::string& mime,
515  const std::string& codec,
516  const std::string& label) {
517  return impl_->CallMethod<Track>("addTextTrack", uri, language, kind, mime,
518  codec, label);
519 }
520 
522  return impl_->Attach(player);
523 }
524 
526  return impl_->Detach();
527 }
528 
530  impl_->AddNetworkFilters(filters);
531 }
532 
534  impl_->RemoveNetworkFilters(filters);
535 }
536 
537 AsyncResults<bool> Player::Configure(const std::string& name_path,
538  const uint8_t* data, size_t data_size) {
539  return impl_->CallMethod<bool>("configure", name_path,
540  ByteBuffer(data, data_size));
541 }
542 
543 void* Player::GetRawJsValue() {
544  return impl_->GetRawJsValue();
545 }
546 
547 } // namespace shaka
AsyncResults< void > Detach()
Definition: player.cc:525
Definition: any.h:31
Converter< void >::future_type Initialize(Client *client, media::MediaPlayer *player)
Definition: player.cc:99
AsyncResults< std::string > GetConfigurationString(const std::string &name_path)
Definition: player.cc:469
AsyncResults< void > SelectTextLanguage(const std::string &language, optional< std::string > role=nullopt)
Definition: player.cc:492
ReturnVal< JsValue > JsUndefined()
Definition: js_wrappers.cc:284
void RemoveNetworkFilters(NetworkFilters *filters)
Definition: player.cc:163
bool FromJsValue(Handle< JsValue > source, T *dest)
Definition: convert_js.h:370
AsyncResults< std::vector< LanguageRole > > GetAudioLanguagesAndRoles() const
Definition: player.cc:389
ReturnVal< JsValue > GetDescendant(Handle< JsObject > root, const std::vector< std::string > &names)
Definition: js_utils.cc:52
AsyncResults< BufferedInfo > GetBufferedInfo() const
Definition: player.cc:395
AsyncResults< bool > IsInProgress() const
Definition: player.cc:364
AsyncResults< bool > IsBuffering() const
Definition: player.cc:360
static AsyncResults< LogLevel > GetLogLevel(JsManager *engine)
Definition: player.cc:334
static AsyncResults< void > SetLogLevel(JsManager *engine, LogLevel level)
Definition: player.cc:328
ReturnVal< JsValue > ToJsValue(T &&source)
Definition: convert_js.h:381
AsyncResults< void > SelectEmbeddedTextTrack()
Definition: player.cc:488
virtual void OnBuffering(bool is_buffering)
Definition: player.cc:317
#define SHAKA_NON_COPYABLE_OR_MOVABLE_TYPE(Type)
Definition: macros.h:51
AsyncResults< double > GetConfigurationDouble(const std::string &name_path)
Definition: player.cc:464
void HandleNetworkFuture(Promise promise, std::future< optional< Error >> future, std::function< void()> on_done)
Definition: js_utils.cc:68
const char * source
Definition: media_utils.cc:30
AsyncResults< bool > GetConfigurationBool(const std::string &name_path)
Definition: player.cc:460
AsyncResults< void > RetryStreaming()
Definition: player.cc:479
virtual std::future< optional< Error > > OnRequestFilter(RequestType type, Request *request)
Definition: net_public.cc:126
void RemoveNetworkFilters(NetworkFilters *filters)
Definition: player.cc:533
AsyncResults< std::string > KeySystem() const
Definition: player.cc:421
void ResolveWith(Handle< JsValue > value, bool run_events=true)
Definition: promise.cc:101
ExceptionCode type
std::shared_future< impl::RetOf< Func > > InvokeOrSchedule(Func &&callback)
Definition: task_runner.h:192
AsyncResults< bool > UsingEmbeddedTextTrack() const
Definition: player.cc:376
AsyncResults< bool > IsTextTrackVisible() const
Definition: player.cc:372
AsyncResults< void > Initialize(Client *client, media::MediaPlayer *player=nullptr)
Definition: player.cc:346
AsyncResults< void > Load(const std::string &manifest_uri, double start_time=NAN, const std::string &mime_type="")
Definition: player.cc:430
AsyncResults< Track > AddTextTrack(const std::string &uri, const std::string &language, const std::string &kind, const std::string &mime, const std::string &codec="", const std::string &label="")
Definition: player.cc:511
Converter< T >::future_type GetConfigValue(const std::string &name_path)
Definition: player.cc:151
virtual void OnError(const Error &error)
Definition: player.cc:316
proto::ValueType GetValueType(Handle< JsValue > value)
Definition: js_wrappers.cc:329
js::Track GetInternal() const
Definition: track.cc:166
Player(JsManager *engine)
Definition: player.cc:320
void * GetRawJsValue()
Definition: player.cc:173
AsyncResults< void > Attach(media::MediaPlayer *player)
Definition: player.cc:521
Converter< void >::future_type Detach()
Definition: player.cc:145
AsyncResults< optional< std::string > > AssetUri() const
Definition: player.cc:381
const char * GetShakaEmbeddedVersion(void)
Definition: player.cc:41
AsyncResults< void > Destroy()
Definition: player.cc:351
Impl(JsManager *engine)
Definition: player.cc:87
Converter< void >::future_type Attach(media::MediaPlayer *player)
Definition: player.cc:138
AsyncResults< std::vector< LanguageRole > > GetTextLanguagesAndRoles() const
Definition: player.cc:415
static RefPtr< Document > EnsureGlobalDocument()
Definition: document.cc:51
#define ATTACH(name, call)
AsyncResults< std::vector< Track > > GetTextTracks() const
Definition: player.cc:407
AsyncResults< void > Unload()
Definition: player.cc:437
AsyncResults< void > SetTextTrackVisibility(bool visibility)
Definition: player.cc:507
std::shared_ptr< ThreadEvent< impl::RetOf< Func > > > AddInternalTask(TaskPriority priority, const std::string &name, Func &&callback)
Definition: task_runner.h:219
static Promise PendingPromise()
Definition: promise.h:54
RequestType
Definition: net.h:38
AsyncResults< void > SelectTextTrack(const Track &track)
Definition: player.cc:497
static bool FromJsValue(Handle< JsValue > source, Player::LogLevel *level)
Definition: player.cc:75
AsyncResults< bool > Configure(const std::string &name_path, DefaultValueType)
Definition: player.cc:441
AsyncResults< Stats > GetStats() const
Definition: player.cc:403
AsyncResults< bool > IsAudioOnly() const
Definition: player.cc:356
AsyncResults< bool > IsLive() const
Definition: player.cc:368
std::vector< std::string > StringSplit(const std::string &source, char split_on)
Definition: utils.cc:64
Player & operator=(Player &&)
AsyncResults< BufferedRange > SeekRange() const
Definition: player.cc:425
AsyncResults< double > GetExpiration() const
Definition: player.cc:399
void AddNetworkFilters(NetworkFilters *filters)
Definition: player.cc:529
AsyncResults< void > ResetConfiguration()
Definition: player.cc:475
AsyncResults< std::vector< Track > > GetVariantTracks() const
Definition: player.cc:411
bool InvokeConstructor(Handle< JsFunction > ctor, int argc, LocalVar< JsValue > *argv, LocalVar< JsValue > *result_or_except)
Definition: js_wrappers.cc:165
AsyncResults< void > SelectAudioLanguage(const std::string &language, optional< std::string > role=nullopt)
Definition: player.cc:483
void Init(Handle< JsObject > object)
ReturnVal< JsValue > GetMemberRaw(Handle< JsObject > object, const std::string &name, LocalVar< JsValue > *exception=nullptr)
Definition: js_wrappers.cc:136
static AsyncResults< std::string > GetPlayerVersion(JsManager *engine)
Definition: player.cc:340
void AddNetworkFilters(NetworkFilters *filters)
Definition: player.cc:158
AsyncResults< void > SelectVariantTrack(const Track &track, bool clear_buffer=false)
Definition: player.cc:501
#define SHAKA_VERSION_STR
Definition: version.h:24
AsyncResults< optional< DrmInfo > > DrmInfo() const
Definition: player.cc:385
virtual std::future< optional< Error > > OnResponseFilter(RequestType type, Response *response)
Definition: net_public.cc:131
TaskRunner * MainThread()