#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <drmdecrypt/emeCDM/IEME.h>
+#endif
+
namespace cast_streaming {
namespace {
void SetWallClock(const std::string& wallclock_url,
SetWallClockCallback /*callback*/) override {}
void OnDemuxerFpsGot(int num, int den) override {}
+ void SetDecryptorHandle(uint64_t decryptor) override {}
+ void SetHasEncryptedListenerOrCdm(bool value) override{}
#endif
private:
}
#endif
+#if BUILDFLAG(IS_TIZEN_TV)
+void CdmContext::SetPlayerType(MediaPlayerType player_type) {
+ player_type_ = player_type;
+}
+
+CdmContext::MediaPlayerType CdmContext::GetPlayerType() const {
+ return player_type_;
+}
+
+void CdmContext::SetExternalError(ExternalError error_type) {
+ external_error_ = error_type;
+}
+
+CdmContext::ExternalError CdmContext::GetExternalError() const {
+ return external_error_;
+}
+#endif
+
} // namespace media
kHardwareContextReset,
};
+ // Types of Media Player
+ enum MediaPlayerType {
+ MEDIA_PLAYER_TYPE_NONE,
+ MEDIA_PLAYER_TYPE_URL,
+ MEDIA_PLAYER_TYPE_MEDIA_SOURCE,
+ MEDIA_PLAYER_TYPE_MEDIA_STREAM,
+#if defined(SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE)
+ MEDIA_PLAYER_TYPE_ELEMENTARY_MEDIA_STREAM_SOURCE,
+#endif // SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE
+#if defined(TIZEN_VIDEO_HOLE)
+ MEDIA_PLAYER_TYPE_URL_WITH_VIDEO_HOLE,
+ MEDIA_PLAYER_TYPE_WEBRTC_LOCAL_MEDIA_STREAM_WITH_VIDEO_HOLE,
+ MEDIA_PLAYER_TYPE_WEBRTC_MEDIA_STREAM_WITH_VIDEO_HOLE,
+ MEDIA_PLAYER_TYPE_MEDIA_SOURCE_WITH_VIDEO_HOLE,
+#if defined(SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE)
+ MEDIA_PLAYER_TYPE_ELEMENTARY_MEDIA_STREAM_SOURCE_WITH_VIDEO_HOLE,
+#endif // SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE
+#endif // TIZEN_VIDEO_HOLE
+ MEDIA_PLAYER_TYPE_UNDECIDED,
+ };
+
// Callback to notify the occurrence of an Event.
using EventCB = base::RepeatingCallback<void(Event)>;
virtual TizenCdmBridge* GetTizenCdmBridge();
#endif
+#if BUILDFLAG(IS_TIZEN_TV)
+ // Getter and setter for player_type_ used to select remote or local CDM mode
+ void SetPlayerType(MediaPlayerType player_type);
+ MediaPlayerType GetPlayerType() const;
+ enum class ExternalError { NONE, PLAYER_SRC_SET_BEFORE_MEDIA_KEYS };
+ void SetExternalError(ExternalError error_type);
+ ExternalError GetExternalError() const;
+#endif
+
protected:
CdmContext();
+
+ private:
+#if BUILDFLAG(IS_TIZEN_TV)
+ MediaPlayerType player_type_{MEDIA_PLAYER_TYPE_NONE};
+ ExternalError external_error_{ExternalError::NONE};
+#endif
};
// A reference holder to make sure the CdmContext is always valid as long as
namespace {
+const char kTVkeySystem[] = "com.tvkey.drm";
+
struct MimeTypeToCodecs {
const char* mime_type;
SupportedCodecs codecs;
if (key_system == kWidevineKeySystem)
return true;
+ if (key_system == kTVkeySystem) {
+ return true;
+ }
+
if (key_system == kClearKeyKeySystem) {
return true;
}
is_updating_ = false;
+#if !BUILDFLAG(IS_TIZEN_TV)
// Clear Key is always supported.
key_systems.emplace_back(std::make_unique<ClearKeyKeySystemInfo>());
+#endif
ProcessSupportedKeySystems(std::move(key_systems));
#include "ui/gfx/geometry/rect_f.h"
#endif
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <drmdecrypt/emeCDM/IEME.h>
+#endif
+
namespace media {
struct register_timeline_cb_info_s {
int sync) {}
virtual void OnMrsUrlChange(const std::string& url) {}
virtual void OnContentIdChange(const std::string& id) {}
+ virtual void OnInitData(const std::vector<unsigned char>& init_data) = 0;
#endif
// Executed whenever the presentation duration changes.
virtual void DestroyPlayerSync(base::OnceClosure cb) = 0;
virtual void SetVideoVisibility(bool visible) = 0;
virtual void OnDemuxerFpsGot(int num, int den) = 0;
+ virtual void SetDecryptorHandle(eme::eme_decryptor_t decryptor) = 0;
+ virtual void SetHasEncryptedListenerOrCdm(bool value) = 0;
#endif
};
#include "media/base/serial_runner.h"
#include "media/base/timestamp_constants.h"
#include "media/base/video_decoder_config.h"
+#include "absl/types/optional.h"
#if BUILDFLAG(IS_WIN)
#include "media/base/win/mf_feature_checks.h"
#include "media/base/logging_override_if_enabled.h"
#include "media/filters/decrypting_media_resource.h"
#include "tizen_src/chromium_impl/tizen/tizen_tv_platform.h"
+#include "tizen_src/ewk/efl_integration/common/application_type.h"
#endif
static const double kDefaultPlaybackRate = 0.0;
void DestroyPlayerSync(base::OnceClosure cb);
void SetVideoVisibility(bool visible);
void OnDemuxerFpsGot(int num, int den);
+ void SetDecryptorHandle(eme::eme_decryptor_t decryptor);
+ void SetHasEncryptedListenerOrCdm(bool value);
#endif
// |enabled_track_ids| contains track ids of enabled audio tracks.
void GetContentIdCB(const std::string& content_id);
void SyncTimelineCB(bool success);
void SetWallClockCB(bool success);
+ void OnInitData(const std::vector<unsigned char>& init_data);
#endif
void OnBufferingStateChange(BufferingState state,
BufferingStateChangeReason reason) final;
base::WaitableEvent::ResetPolicy::AUTOMATIC};
std::string mime_type_;
bool transparent_ = false;
+ bool has_encrypted_listener_or_cdm_ = false;
bool register_time_line_ = false;
bool unregister_time_line_ = false;
double speed_data_ = 0.0;
// If there's already a renderer, set the CDM on the renderer directly.
if (shared_state_.renderer) {
+#if BUILDFLAG(IS_TIZEN_TV)
+ if (content::IsHbbTV()) {
+ absl::optional<base::UnguessableToken> cdm_id = cdm_context->GetCdmId();
+ if (!cdm_id) {
+ // If there is no cdm_id, then the SetCdm on the renderer (mojo) is
+ // not valid, but for HbbTV Standalone EME we need to store the
+ // context and signal the CDM is attached.
+ LOG(INFO) << "HbbTV : store the context and run OnCdmAttached....";
+ shared_state_.renderer->SetHasEncryptedListenerOrCdm(true);
+ cdm_context_ = cdm_context;
+ std::move(cdm_attached_cb).Run(true);
+ return;
+ }
+
+ LOG(INFO) << "HbbTV with CDM ID, go via shared_state_.renderer->SetCdm()";
+ }
+#endif
+
shared_state_.renderer->SetCdm(
cdm_context, base::BindOnce(&RendererWrapper::OnCdmAttached,
weak_factory_.GetWeakPtr(),
shared_state_.renderer->OnDemuxerFpsGot(num, den);
}
}
+
+void PipelineImpl::RendererWrapper::SetDecryptorHandle(
+ eme::eme_decryptor_t decryptor) {
+ if (shared_state_.renderer) {
+ shared_state_.renderer->SetDecryptorHandle(decryptor);
+ }
+}
+
+void PipelineImpl::RendererWrapper::SetHasEncryptedListenerOrCdm(bool value) {
+ if (shared_state_.renderer) {
+ shared_state_.renderer->SetHasEncryptedListenerOrCdm(value);
+ } else {
+ // Store the value, as we need to set it before calling Initialize() on the
+ // renderer.
+ has_encrypted_listener_or_cdm_ = value;
+ }
+}
#endif
void PipelineImpl::RendererWrapper::CreateRendererInternal(
FROM_HERE,
base::BindOnce(&PipelineImpl::OnContentIdChange, weak_pipeline_, id));
}
+
+void PipelineImpl::RendererWrapper::OnInitData(
+ const std::vector<unsigned char>& init_data) {
+ DVLOG(2) << __func__;
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PipelineImpl::OnInitData, weak_pipeline_, init_data));
+}
#endif
void PipelineImpl::RendererWrapper::OnBufferingStateChange(
// case, and the player needs the mime type to configure itself correctly.
shared_state_.renderer->SetContentMimeType(mime_type_);
shared_state_.renderer->SetTransparent(transparent_);
+ LOG(INFO) << "Call SetHasEncryptedListenerOrCdm("
+ << has_encrypted_listener_or_cdm_ << ")";
+ shared_state_.renderer->SetHasEncryptedListenerOrCdm(
+ has_encrypted_listener_or_cdm_);
#endif
// Initialize Renderer and report timeout UMA.
// kSuspendAfterMetadata, latency is not important and the video may never
// play. In this case, not creating a default renderer to reduce memory usage.
std::unique_ptr<Renderer> default_renderer;
- if (start_type != StartType::kSuspendAfterMetadata)
- default_renderer = create_renderer_cb_.Run(std::nullopt);
+ if (start_type != StartType::kSuspendAfterMetadata) {
+ default_renderer = create_renderer_cb_.Run(absl::nullopt);
+ }
media_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&RendererWrapper::SetVideoVisibility,
base::Unretained(renderer_wrapper_.get()), visible));
}
+
+void PipelineImpl::SetDecryptorHandle(eme::eme_decryptor_t decryptor) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ renderer_wrapper_->SetDecryptorHandle(decryptor);
+}
+
+void PipelineImpl::SetHasEncryptedListenerOrCdm(bool value) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ renderer_wrapper_->SetHasEncryptedListenerOrCdm(value);
+}
#endif
#if defined(TIZEN_MULTIMEDIA)
DCHECK(thread_checker_.CalledOnValidThread());
return renderer_wrapper_->GetStartDate();
}
+
+void PipelineImpl::OnInitData(const std::vector<unsigned char>& init_data) {
+ DVLOG(2) << __func__;
+ DCHECK(client_);
+ client_->OnInitData(init_data);
+}
#endif
void PipelineImpl::OnDurationChange(base::TimeDelta duration) {
void DestroyPlayerSync(base::OnceClosure cb) override;
void SetVideoVisibility(bool visible) override;
void OnDemuxerFpsGot(int num, int den) override;
+ void SetDecryptorHandle(eme::eme_decryptor_t decryptor) override;
+ void OnInitData(const std::vector<unsigned char>& init_data);
+ void SetHasEncryptedListenerOrCdm(bool value) override;
#endif
void OnBufferingStateChange(BufferingState state,
BufferingStateChangeReason reason);
#include "ui/gfx/geometry/rect_f.h"
#endif
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <drmdecrypt/emeCDM/IEME.h>
+#endif
+
namespace media {
class CdmContext;
virtual void DestroyPlayerSync(base::OnceClosure cb) {}
virtual void SetVideoVisibility(bool visible) {}
virtual void OnDemuxerFpsGot(int num, int den) {}
+ virtual void SetDecryptorHandle(eme::eme_decryptor_t decryptor) {}
+ virtual void SetHasEncryptedListenerOrCdm(bool value) {}
#endif
// Starts rendering from |time|.
int sync) {}
virtual void OnMrsUrlChange(const std::string& url) {}
virtual void OnContentIdChange(const std::string& id) {}
+ virtual void OnInitData(const std::vector<unsigned char>& init_data) {}
#endif
// Executed when buffering state is changed. |reason| indicates the cause of
pipeline_->OnDemuxerFpsGot(num, den);
}
}
+
+void PipelineController::SetDecryptorHandle(eme::eme_decryptor_t decryptor) {
+ if (pipeline_) {
+ pipeline_->SetDecryptorHandle(decryptor);
+ }
+}
+
+void PipelineController::SetHasEncryptedListenerOrCdm(bool value) {
+ if (pipeline_) {
+ pipeline_->SetHasEncryptedListenerOrCdm(value);
+ }
+}
#endif
} // namespace media
void SetVideoVisibility(bool visible);
bool ShouldUseUpstreamArchitecture() const;
void OnDemuxerFpsGot(int num, int den);
+ void SetDecryptorHandle(eme::eme_decryptor_t decryptor);
+ void SetHasEncryptedListenerOrCdm(bool value);
#endif
private:
// Attempts to make progress from the current state to the target state.
remote_renderer_->OnDemuxerFpsGot(num, den);
}
+
+void MojoRenderer::SetDecryptorHandle(eme::eme_decryptor_t decryptor) {
+ DVLOG(2) << __func__;
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(remote_renderer_.is_bound());
+
+ remote_renderer_->SetDecryptorHandle(decryptor);
+}
+
+void MojoRenderer::OnInitData(const std::vector<unsigned char>& init_data) {
+ DVLOG(2) << __func__;
+ client_->OnInitData(init_data);
+}
+
+void MojoRenderer::SetHasEncryptedListenerOrCdm(bool value) {
+ DVLOG(2) << __func__;
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(remote_renderer_.is_bound());
+
+ remote_renderer_->SetHasEncryptedListenerOrCdm(value);
+}
#endif
void MojoRenderer::OnWaiting(WaitingReason reason) {
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <drmdecrypt/emeCDM/IEME.h>
+#endif
+
namespace media {
class MediaResource;
void DestroyPlayerSync(base::OnceClosure cb) override;
void SetVideoVisibility(bool visible) override;
void OnDemuxerFpsGot(int num, int den) override;
+ void SetDecryptorHandle(eme::eme_decryptor_t decryptor) override;
+ void OnInitData(const std::vector<unsigned char>& init_data) override;
+ void SetHasEncryptedListenerOrCdm(bool value) override;
#endif
private:
if (mojo_renderer_)
mojo_renderer_->OnDemuxerFpsGot(num, den);
}
+
+void MojoRendererWrapper::SetDecryptorHandle(eme::eme_decryptor_t decryptor) {
+ if (mojo_renderer_) {
+ mojo_renderer_->SetDecryptorHandle(decryptor);
+ }
+}
+
+void MojoRendererWrapper::SetHasEncryptedListenerOrCdm(bool value) {
+ if (mojo_renderer_) {
+ mojo_renderer_->SetHasEncryptedListenerOrCdm(value);
+ }
+}
#endif
} // namespace media
#include "media/base/renderer.h"
#include "media/mojo/clients/mojo_renderer.h"
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <drmdecrypt/emeCDM/IEME.h>
+#endif
+
namespace media {
// Simple wrapper around a MojoRenderer.
void DestroyPlayerSync(base::OnceClosure cb) override;
void SetVideoVisibility(bool visible) override;
void OnDemuxerFpsGot(int num, int den) override;
+ void SetDecryptorHandle(eme::eme_decryptor_t decryptor) override;
+ void SetHasEncryptedListenerOrCdm(bool value) override;
#endif
base::TimeDelta GetMediaTime() override;
[EnableIf=is_tizen_tv]
OnDemuxerFpsGot(int32 num, int32 den);
+
+ [EnableIf=is_tizen_tv]
+ SetDecryptorHandle(uint64 decryptor);
+
+ [EnableIf=is_tizen_tv]
+ SetHasEncryptedListenerOrCdm(bool value);
};
// A Mojo equivalent of media::RendererClient. See media/mojo/README.md
[EnableIf=is_tizen_tv]
OnPlayerStarted(bool play_started);
+
+ [EnableIf=is_tizen_tv]
+ OnInitData(array<uint8> init_data);
};
if (renderer_)
renderer_->OnDemuxerFpsGot(num, den);
}
+
+void MojoRendererService::SetDecryptorHandle(uint64_t decryptor) {
+ DVLOG(2) << __func__;
+
+ if (renderer_) {
+ renderer_->SetDecryptorHandle(decryptor);
+ }
+}
+
+void MojoRendererService::OnInitData(
+ const std::vector<unsigned char>& init_data) {
+ DVLOG(3) << __func__;
+ client_->OnInitData(init_data);
+}
+
+void MojoRendererService::SetHasEncryptedListenerOrCdm(bool value) {
+ DVLOG(2) << __func__;
+
+ if (renderer_) {
+ renderer_->SetHasEncryptedListenerOrCdm(value);
+ }
+}
#endif
void MojoRendererService::OnBufferingStateChange(
void DestroyPlayerSync(base::OnceClosure destroy_cb) final;
void SetVideoVisibility(bool visible) final;
void OnDemuxerFpsGot(int num, int den) final;
+ void SetDecryptorHandle(uint64_t decryptor) final;
+ void OnInitData(const std::vector<unsigned char>& init_data) final;
+ void SetHasEncryptedListenerOrCdm(bool value) final;
#endif
private:
media::register_timeline_cb_info_s info) = 0;
virtual void OnMrsUrlChange(const std::string& url) = 0;
virtual void OnContentIdChange(const std::string& id) = 0;
+ virtual bool HasEncryptedListener() const = 0;
#endif
// Returns the DOMNodeId of the DOM element hosting this media player.
virtual int GetElementId() = 0;
// potentiallyPlaying() uses it
bool was_potentially_playing = PotentiallyPlaying();
+ ReadyState old_max_state = ready_state_maximum_;
ReadyState old_state = ready_state_;
ReadyState new_state = state;
if (IsHTMLVideoElement())
ScheduleNamedEvent(event_type_names::kResize);
- ScheduleNamedEvent(event_type_names::kLoadedmetadata);
+
+ #if BUILDFLAG(IS_TIZEN_TV)
+ if ((!IsHbbTV()) ||
+ (ready_state_ > kHaveMetadata && old_max_state <= kHaveMetadata))
+ #endif
+ ScheduleNamedEvent(event_type_names::kLoadedmetadata);
bool jumped = false;
if (default_playback_start_position_ > 0) {
#endif // TIZEN_MULTIMEDIA
}
+#if BUILDFLAG(IS_TIZEN_TV)
+bool HTMLMediaElement::HasEncryptedListener() const {
+ return HasEventListeners(event_type_names::kEncrypted);
+}
+#endif
+
bool HTMLMediaElement::HasPendingActivity() const {
const auto result = HasPendingActivityInternal();
// TODO(dalecurtis): Replace c-style casts in followup patch.
media::register_timeline_cb_info_s info) override;
void OnMrsUrlChange(const std::string& url) override;
void OnContentIdChange(const std::string& id) override;
+ bool HasEncryptedListener() const override;
#endif
void SuspendMediaByHost(bool suspend) override;
// it's not interested in recording these events.
playback_events_recorder_.reset_on_disconnect();
+#if BUILDFLAG(IS_TIZEN_TV)
+ if (content::IsHbbTV()) {
+ SetDecryptorCb();
+ }
+#endif
+
#if defined(TIZEN_VIDEO_HOLE)
if (is_video_hole_) {
vfc_task_runner_->PostTask(
demuxer_manager_->SetLoadedUrl(GURL(url));
load_type_ = load_type;
+#if defined(TIZEN_VIDEO_HOLE)
+ switch (load_type_) {
+ case kLoadTypeMediaSource:
+ LOG(ERROR)
+ << "player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE_WITH_VIDEO_HOLE";
+ player_type_ =
+ media::CdmContext::MEDIA_PLAYER_TYPE_MEDIA_SOURCE_WITH_VIDEO_HOLE;
+ break;
+ case kLoadTypeURL:
+ LOG(ERROR) << "player_type_ = MEDIA_PLAYER_TYPE_URL_WITH_VIDEO_HOLE";
+ player_type_ = media::CdmContext::MEDIA_PLAYER_TYPE_URL_WITH_VIDEO_HOLE;
+ break;
+#if defined(SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE)
+ case kLoadTypeSamsungElementaryMediaStreamSource:
+ LOG(ERROR)
+ << "player_type_ = "
+ "MEDIA_PLAYER_TYPE_ELEMENTARY_MEDIA_STREAM_SOURCE_WITH_VIDEO_HOLE";
+ player_type_ = media::CdmContext::
+ MEDIA_PLAYER_TYPE_ELEMENTARY_MEDIA_STREAM_SOURCE_WITH_VIDEO_HOLE;
+ break;
+#endif
+ default:
+ LOG(ERROR) << "Unsupported load type...";
+ break;
+ }
+#else
+ switch (load_type_) {
+ case kLoadTypeMediaSource:
+ LOG(ERROR) << "player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE";
+ player_type_ = media::CdmContext::MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
+ break;
+ case kLoadTypeURL:
+ LOG(ERROR) << "player_type_ = MEDIA_PLAYER_TYPE_URL";
+ player_type_ = media::CdmContext::MEDIA_PLAYER_TYPE_URL;
+ break;
+#if defined(SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE)
+ case kLoadTypeSamsungElementaryMediaStreamSource:
+ LOG(ERROR)
+ << "player_type_ = MEDIA_PLAYER_TYPE_ELEMENTARY_MEDIA_STREAM_SOURCE";
+ player_type_ =
+ media::CdmContext::MEDIA_PLAYER_TYPE_ELEMENTARY_MEDIA_STREAM_SOURCE;
+ break;
+#endif
+ default:
+ LOG(ERROR) << "Unsupported load type...";
+ break;
+ }
+#endif
+
#if BUILDFLAG(IS_TIZEN_TV)
// |MediaUrlDemuxer| will be created if |IsHbbTV| function returns true. In
// this case, |demuxer_override_| is not null. (i.e, |demuxer_override_| is
: media::mojom::MediaURLScheme::kUnknown,
media::mojom::MediaStreamType::kNone);
+#if BUILDFLAG(IS_TIZEN_TV)
+ if (content::IsHbbTV()) {
+ SetCDMPlayerType();
+ if (client_->HasEncryptedListener()) {
+ had_encrypted_listener_ = true;
+ }
+ pipeline_controller_->SetHasEncryptedListenerOrCdm(
+ HasEncryptedListenerOrCdm());
+ }
+#endif
+
// If a demuxer override was specified or a Media Source pipeline will be
// used, the pipeline can start immediately.
if (demuxer_manager_->HasDemuxerOverride() ||
SetCdmInternal(cdm);
+ pending_cdm_context_ref_ =
+ blink::ToWebContentDecryptionModuleImpl(cdm)->GetCdmContextRef();
+
#if BUILDFLAG(IS_TIZEN_TV)
+ if (content::IsHbbTV()) {
+ SetDecryptorCb();
+ }
+
// Finish set media key directly
// Fix issue : AD insert case in EME stream, no cdm_id in this case and will
// return failed here. And then JS error occurred.
// Keep the reference to the CDM, as it shouldn't be destroyed until
// after the pipeline is done with the `cdm_context`.
pending_cdm_context_ref_ = std::move(cdm_context_ref);
+
+#if BUILDFLAG(IS_TIZEN_TV)
+ if (content::IsHbbTV()) {
+ SetCDMPlayerType();
+ }
+#endif
+
pipeline_controller_->SetCdm(
cdm_context,
base::BindOnce(&WebMediaPlayerImpl::OnCdmAttached, weak_this_));
if (success) {
// This will release the previously attached CDM (if any).
cdm_context_ref_ = std::move(pending_cdm_context_ref_);
+#if BUILDFLAG(IS_TIZEN_TV)
+ if (content::IsHbbTV()) {
+ SetCDMPlayerType();
+ }
+#endif
if (set_cdm_result_) {
set_cdm_result_->Complete();
set_cdm_result_.reset();
return pipeline_controller_->GetVideoId();
}
+bool WebMediaPlayerImpl::IsNotValidCDMPlayer() const {
+ if (!cdm_context_ref_ && !pending_cdm_context_ref_) {
+ return false;
+ }
+
+ auto cdm_context_type =
+ cdm_context_ref_
+ ? cdm_context_ref_->GetCdmContext()->GetPlayerType()
+ : pending_cdm_context_ref_->GetCdmContext()->GetPlayerType();
+
+ // If player type is set to none, change is valid
+ if (cdm_context_type == media::CdmContext::MEDIA_PLAYER_TYPE_NONE) {
+ return false;
+ }
+
+ // If default type is set in cmd and player is URL type,
+ // then change is not valid.
+ if (cdm_context_type == media::CdmContext::MEDIA_PLAYER_TYPE_UNDECIDED) {
+ bool is_url_player =
+#ifdef TIZEN_VIDEO_HOLE
+ (player_type_ ==
+ media::CdmContext::MEDIA_PLAYER_TYPE_URL_WITH_VIDEO_HOLE) ||
+#endif // TIZEN_VIDEO_HOLE
+ (player_type_ == media::CdmContext::MEDIA_PLAYER_TYPE_URL);
+ if (is_url_player) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // If other type than choosen, default or none was already
+ // set, then change is not valid.
+ if (cdm_context_type != player_type_) {
+ return true;
+ }
+
+ return false;
+}
+
void WebMediaPlayerImpl::SetParentalRatingResult(bool is_pass) {
pipeline_controller_->SetParentalRatingResult(is_pass);
}
return pipeline_controller_->GetStartDate();
}
+void WebMediaPlayerImpl::OnInitData(
+ const std::vector<unsigned char>& init_data) {
+ OnEncryptedMediaInitData(media::EmeInitDataType::CENC, init_data);
+}
+
void WebMediaPlayerImpl::SetVideoVisibility(bool visible) {
return pipeline_controller_->SetVideoVisibility(visible);
}
}
}
}
+
+bool WebMediaPlayerImpl::HasEncryptedListenerOrCdm() const {
+ return client_->HasEncryptedListener() || cdm_context_ref_ ||
+ pending_cdm_context_ref_;
+}
+
+void WebMediaPlayerImpl::SetCDMPlayerType() const {
+ if (IsNotValidCDMPlayer()) {
+ LOG(ERROR) << "player type mismatch";
+ pipeline_controller_->SetDecryptorHandle(0);
+ return;
+ }
+
+ if (cdm_context_ref_) {
+ cdm_context_ref_->GetCdmContext()->SetPlayerType(player_type_);
+ } else if (pending_cdm_context_ref_) {
+ pending_cdm_context_ref_->GetCdmContext()->SetPlayerType(player_type_);
+ }
+}
+
+void WebMediaPlayerImpl::SetDecryptorHandle() const {
+ if (cdm_context_ref_ && cdm_context_ref_->GetCdmContext()->GetDecryptor()) {
+ auto decryptor_handle =
+ cdm_context_ref_->GetCdmContext()->GetDecryptor()->GetDecryptorHandle();
+ LOG(INFO) << "Get decryptor handle ["
+ << reinterpret_cast<void*>(decryptor_handle)
+ << "] from cdm context";
+ pipeline_controller_->SetDecryptorHandle(decryptor_handle);
+ } else if (pending_cdm_context_ref_ &&
+ pending_cdm_context_ref_->GetCdmContext()->GetDecryptor()) {
+ auto decryptor_handle = pending_cdm_context_ref_->GetCdmContext()
+ ->GetDecryptor()
+ ->GetDecryptorHandle();
+ LOG(INFO) << "Get decryptor handle ["
+ << reinterpret_cast<void*>(decryptor_handle)
+ << "] from cdm context";
+ pipeline_controller_->SetDecryptorHandle(decryptor_handle);
+ }
+}
+
+void WebMediaPlayerImpl::OnCdmContextEvent(media::CdmContext::Event event) {
+ LOG(INFO) << "New key notified";
+
+ if (event != media::CdmContext::Event::kHasAdditionalUsableKey) {
+ return;
+ }
+
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WebMediaPlayerImpl::SetDecryptorHandle, weak_this_));
+}
+
+void WebMediaPlayerImpl::SetDecryptorCb() {
+ if (cdm_event_cb_registration_) {
+ LOG(INFO) << "Already registered - destruction should unregister";
+ }
+ if (cdm_context_ref_) {
+ LOG(INFO) << "Register on cdm_context_ref_";
+ cdm_event_cb_registration_ =
+ cdm_context_ref_->GetCdmContext()->RegisterEventCB(
+ base::BindRepeating(&WebMediaPlayerImpl::OnCdmContextEvent,
+ weak_factory_.GetWeakPtr()));
+ } else if (pending_cdm_context_ref_) {
+ LOG(INFO) << "Register on pending_cdm_context_ref_";
+ cdm_event_cb_registration_ =
+ pending_cdm_context_ref_->GetCdmContext()->RegisterEventCB(
+ base::BindRepeating(&WebMediaPlayerImpl::OnCdmContextEvent,
+ weak_factory_.GetWeakPtr()));
+ }
+}
#endif
+
} // namespace blink
#include "base/timer/elapsed_timer.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
+#include "media/base/callback_registry.h"
#include "media/base/cdm_config.h"
+#include "media/base/cdm_context.h"
#include "media/base/data_source.h"
#include "media/base/demuxer.h"
#include "media/base/eme_constants.h"
void OnRegisterTimelineCbInfo(media::register_timeline_cb_info_s info);
void OnMrsUrlChange(const std::string& url);
void OnContentIdChange(const std::string& id);
+ void OnInitData(const std::vector<unsigned char>& init_data) override;
#endif
// Shared between the WebMediaPlayer and DemuxerManager::Client interfaces.
void SetActiveAudioTrack(int index) override;
void SetActiveVideoTrack(int index) override;
void SetPreferTextLanguage(const std::string& lang) override;
+ bool HasEncryptedListenerOrCdm() const;
+ void SetCDMPlayerType() const;
+ void SetDecryptorHandle() const;
+ // Callback for the CDM to notify |this|.
+ void OnCdmContextEvent(media::CdmContext::Event event);
+ bool IsNotValidCDMPlayer() const;
+ void SetHasEncryptedListenerOrCdm(bool value);
#endif
void OnWaiting(media::WaitingReason reason) override;
void OnAudioConfigChange(const media::AudioDecoderConfig& config) override;
#if BUILDFLAG(IS_TIZEN_TV)
bool IsSyncDestroyPlayerNeeded();
+ void SetCdm(media::CdmContext* cdm_reference);
+ void SetDecryptorCb();
#endif
// Sets CdmContext from |cdm| on the pipeline and calls OnCdmAttached()
bool was_suspended_for_frame_closed_ = false;
+ media::CdmContext::MediaPlayerType player_type_;
+
#if defined(TIZEN_MULTIMEDIA)
bool was_suspended_by_player_ = false;
bool suspend_by_multi_reload_ = false;
bool is_live_stream_ = false;
base::TimeDelta min_seekable_time_;
base::TimeDelta max_seekable_time_;
+ bool had_encrypted_listener_{};
#endif
#if BUILDFLAG(IS_TIZEN_TV)
uint32_t session_id_ = 0;
#endif
+ // To keep the CdmContext event callback registered.
+ std::unique_ptr<media::CallbackRegistration> cdm_event_cb_registration_;
+
base::WeakPtr<WebMediaPlayerImpl> weak_this_;
base::WeakPtrFactory<WebMediaPlayerImpl> weak_factory_{this};
};
"TIZEN_MULTIMEDIA_WEBM_SUPPORT",
]
if (tizen_version >= 60) {
+ defines += [
+ "TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT",
+ ]
if (tizen_tv_riscv64) {
defines += [ "TIZEN_TV_RISCV64" ]
}
"//tizen_src/build:ecore-x",
"//tizen_src/build:libecore-x",
]
+
+ if (tizen_product_tv) {
+ external_content_browser_efl_configs += [ "//tizen_src/build:drmdecrypt" ]
+ }
}
if (tizen_web_speech_recognition) {
// Initialize and Prepare early when resource type is URL.
if (media_resource_->GetType() == media::MediaResource::Type::KUrl) {
SetPlayerInitialize();
+#if BUILDFLAG(IS_TIZEN_TV)
+ media_player_->SetHasEncryptedListenerOrCdm(has_encrypted_listener_or_cdm_);
+#endif
SetPlayerPrepare();
}
#if BUILDFLAG(IS_TIZEN_TV)
}
}
+#if BUILDFLAG(IS_TIZEN_TV)
+void TizenRendererImpl::SetDecryptorHandle(eme::eme_decryptor_t decryptor) {
+ if (media_player_) {
+ media_player_->SetDecryptorHandle(decryptor);
+ }
+}
+
+void TizenRendererImpl::SetHasEncryptedListenerOrCdm(bool value) {
+ has_encrypted_listener_or_cdm_ = value;
+ if (media_player_) {
+ media_player_->SetHasEncryptedListenerOrCdm(value);
+ }
+}
+#endif
+
bool TizenRendererImpl::PlaybackNotificationEnabled() {
content::WebContents* web_contents = GetWebContents();
if (!web_contents) {
num = fps_num_;
den = fps_den_;
}
-#endif
+
+void TizenRendererImpl::OnInitData(
+ const std::vector<unsigned char>& init_data) {
+ if (client_) {
+ client_->OnInitData(init_data);
+ }
+}
+#endif // BUILDFLAG(IS_TIZEN_TV)
void TizenRendererImpl::OnSeekableTimeChange(base::TimeDelta min_time,
base::TimeDelta max_time,
#include "tizen_src/chromium_impl/ui/gfx/tbm_buffer_handle_inter_process.h"
#endif
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <drmdecrypt/emeCDM/IEME.h>
+#endif
+
namespace base {
class SingleThreadTaskRunner;
}
void Show() override;
void Hide() override;
void GetFpsInfo(int& num, int& den) override;
+ void OnInitData(const std::vector<unsigned char>& init_data) override;
#endif
#if defined(TIZEN_TBM_SUPPORT)
void DestroyPlayerSync(base::OnceClosure destroy_cb) override;
void SetVideoVisibility(bool visible) override;
void OnDemuxerFpsGot(int num, int den) override;
+ void SetDecryptorHandle(eme::eme_decryptor_t decryptor) override;
+ void SetHasEncryptedListenerOrCdm(bool value) override;
#endif
private:
// initialize / prepare.
std::string mime_type_ = "";
bool is_player_can_start_ = true;
+ // Stores the has_encrypted_listener_or_cdm_ . Required for URL streams which
+ // are DASH content, so that we can set it to the media_player_ before calling
+ // initialize / prepare.
+ bool has_encrypted_listener_or_cdm_ = false;
bool transparent_ = false;
// one video + one transparent case, should disable mixer mode
// otherwise multi video will enter mixer mode and cause error
#if BUILDFLAG(IS_TIZEN_TV)
int64_t player_position = 0;
const int ret_val = player_get_play_position_ex(player, &player_position);
+ LOG(INFO) << "Current play position " << player_position;
#else
int player_position = 0;
const int ret_val = player_get_play_position(player, &player_position);
player_seek_completed_cb callback,
void* user_data) {
#if BUILDFLAG(IS_TIZEN_TV)
+ LOG(INFO) << "Set play position " << position.InMilliseconds();
return player_set_play_position_ex(player, position.InMilliseconds(),
accurate, callback, user_data);
#else
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
+#include "base/trace_event/trace_event.h"
#include "content/public/common/content_switches.h"
#include "media/base/decoder_buffer.h"
#include "tizen_src/platform_api_wrapper/platform_api_wrapper.h"
+#include "tizen_src/ewk/efl_integration/common/application_type.h"
#include "url/gurl.h"
namespace media {
privacy_mode_(privacy_mode),
server_certificate_registered_(false),
weak_factory_(this) {
- LOG(INFO) << "Constructing DRM for " << key_system_
- << ", privacy mode: " << privacy_mode
- << ", this:" << (void*)this;
- single_process_mode_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kSingleProcess);
- LOG(INFO) << "single process mode:" << std::boolalpha << single_process_mode_;
- // TODO(mdebski): what is privacy_mode?
-#if defined(PLATFORM_API_WRAPPER)
- cdm_.reset(
- platform_api_wrapper::createIEME(this, key_system_.c_str(), privacy_mode_));
-#else
- cdm_.reset(eme::IEME::create(this, key_system_, privacy_mode_));
-#endif
- CHECK(cdm_.get());
+ LOG(INFO) << "Constructing DRM bridge " << this << " for key system "
+ << key_system;
}
IEMEDrmBridge::~IEMEDrmBridge() {
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb,
bool privacy_mode) {
- LOG(INFO) << "Creating DRM bridge for key system: " << key_system;
+ LOG(INFO) << "Creating DRM bridge for key system: " << key_system;
ScopedIEMEDrmBridgePtr ieme_drm_bridge;
ieme_drm_bridge.reset(new IEMEDrmBridge(
key_system, session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb, privacy_mode));
- if (ieme_drm_bridge->cdm_ == nullptr)
- ieme_drm_bridge.reset();
-
return ieme_drm_bridge;
}
// static
ScopedIEMEDrmBridgePtr IEMEDrmBridge::CreateWithoutSessionSupport(
const std::string& key_system) {
+ LOG(INFO) << "CreateWithoutSessionSupport";
return IEMEDrmBridge::Create(key_system, SessionMessageCB(),
SessionClosedCB(), SessionKeysChangeCB(),
SessionExpirationUpdateCB(), false);
}
+bool IEMEDrmBridge::VerifyStandaloneEmePreconditions(CdmPromise& promise,
+ const char* method_name) {
+ auto RejectPromise = [&promise, method_name](const char* message) {
+ std::string error_str("Unable to call ");
+ error_str += method_name;
+ error_str += message;
+ LOG(WARNING) << error_str;
+ promise.reject(CdmPromise::Exception::INVALID_STATE_ERROR, 0, error_str);
+ };
+
+ if (GetExternalError() ==
+ CdmContext::ExternalError::PLAYER_SRC_SET_BEFORE_MEDIA_KEYS) {
+ RejectPromise(" as MediaKeys was set after assigning media element source");
+ return false;
+ } else if (GetPlayerType() == MEDIA_PLAYER_TYPE_NONE) {
+ // We can't reject here for HbbTV + EME standalone case, because the
+ // MSE + EME case can request the keys before the player type can be set.
+ SetPlayerType(MEDIA_PLAYER_TYPE_UNDECIDED);
+ return true;
+ }
+
+ return true;
+}
+
+void IEMEDrmBridge::CreateIEME() {
+ // Up to now IEME instance was created in IEMEDrmBridge's constructor.
+ // This is now delayed to additionally support EME + DASH scenario in HbbTV.
+ // Previously IEMEDrmBridge was used only for EME + MSE case. It requires
+ // IEME instanced in the default model (local) as the decryption calls are
+ // invoked in Chromium.
+ // EME + DASH requires IEME in the remote model as the decryption is performed
+ // in CAPI Player process.
+ // However HbbTV still can use EME + MSE. Delaying IEME creation gives more
+ // wiggle room to detect which EME use case HbbTV application attempts.
+ // This is currently based on the source type the related media element was
+ // set with. If MediaSource was set we need default model. If URL was set we
+ // need remote model.
+ // Without the delay we would need to know the source type when MediaKeys
+ // is created (impossible as to know the source type MediaKeys needs to
+ // be already assigned to media element). With the delay we require it much
+ // later - during MediaKeySession's generateRequest() call.
+
+ if (cdm_) {
+ return;
+ }
+
+ single_process_mode_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSingleProcess);
+
+ // remote model for standalone EME in HbbTV
+ bool is_url_player =
+#ifdef TIZEN_VIDEO_HOLE
+ (GetPlayerType() == MEDIA_PLAYER_TYPE_URL_WITH_VIDEO_HOLE) ||
+#endif
+ (GetPlayerType() == MEDIA_PLAYER_TYPE_URL);
+
+ auto ieme_model = (content::IsHbbTV() && is_url_player)
+ ? eme::CDM_MODEL::E_CDM_MODEL_REMOTE
+ : eme::CDM_MODEL::E_CDM_MODEL_DEFAULT;
+
+ LOG(INFO) << "Creating IEME, key system: " << key_system_
+ << ", player type: " << GetPlayerType()
+ << ", IEME model: " << ieme_model
+ << ", single process mode:" << std::boolalpha
+ << single_process_mode_;
+
+ cdm_.reset(eme::IEME::create(this, key_system_, false, ieme_model));
+ CHECK(cdm_.get());
+}
+
void IEMEDrmBridge::SetServerCertificate(
const std::vector<uint8_t>& certificate,
std::unique_ptr<media::SimpleCdmPromise> promise) {
LOG(INFO) << "Setting server certificate";
CHECK(!certificate.empty());
+ if (content::IsHbbTV() && !VerifyStandaloneEmePreconditions(
+ *promise.get(), "setServerCeritficate")) {
+ return;
+ }
+
+ CreateIEME();
const auto result_status =
cdm_->setServerCertificate(CreateStringFromBinaryData(certificate));
init_data_type)
<< ", server_certificate_registered_ "
<< server_certificate_registered_;
+ if (content::IsHbbTV() &&
+ !VerifyStandaloneEmePreconditions(*promise.get(), "generateRequest")) {
+ return;
+ }
+
+ CreateIEME();
// In privacy mode server certificate request has to be generated before
// creating session and generating request. All requests have to be queued
CHECK(session_type != CdmSessionType::kTemporary);
+ if (content::IsHbbTV() &&
+ !VerifyStandaloneEmePreconditions(*promise.get(), "load")) {
+ return;
+ }
+ CreateIEME();
+
const auto load_result_status = cdm_->session_load(ieme_session_id);
if (eme::kSuccess != load_result_status) {
LOG(ERROR) << "Loading session failed with status: " << load_result_status;
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb,
bool privacy_mode) {
+ LOG(INFO) << "CreateIEMEDrmBridge";
return IEMEDrmBridge::Create(key_system, session_message_cb,
session_closed_cb, session_keys_change_cb,
session_expiration_update_cb, privacy_mode)
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb,
bool privacy_mode);
+ bool VerifyStandaloneEmePreconditions(CdmPromise& promise,
+ const char* method_name);
+ void CreateIEME();
void ResolvePromise(uint32_t promise_id);
void ChangeKeyStatuses(const std::string& ieme_session_id);
#if defined(TIZEN_VIDEO_HOLE)
#include <Ecore_Wl2.h>
#include "tizen_src/chromium_impl/media/base/tizen/video_plane_controller_capi.h"
+#endif
#if BUILDFLAG(IS_TIZEN_TV)
+#include <player_product.h>
#include <player_product_platform_interface.h>
-#endif
+#include <player_product_module_interface.h>
#endif
#if defined(ENABLE_WRT_JS)
// Update duration every 100ms.
const int kDurationUpdateInterval = 100;
+#if BUILDFLAG(IS_TIZEN_TV)
+char kCAPath[] = "/opt/data/cert/user/";
+#endif
// TODO(max): Remove these proxy free functions.
// Called by player_prepare_async()
OnHandlePlayerError(ret, FROM_HERE);
return false;
}
+#if BUILDFLAG(IS_TIZEN_TV)
+ ret = player_set_certificate_path(player_, kCAPath);
+ if (ret != PLAYER_ERROR_NONE) {
+ LOG_ID(ERROR, GetPlayerId())
+ << "player_set_certificate_path failed " << ret;
+ }
+#endif // BUILDFLAG(IS_TIZEN_TV)
+
return true;
}
#else
player_set_uri(player_, url_.spec().c_str());
#endif
+
#if !defined(EWK_BRINGUP)
player_set_sound_type(player_, SOUND_TYPE_MEDIA);
#endif
}
bool MediaPlayerBridgeCapi::SetPlayerPrepareAsync() {
+ if (is_preparing_) {
+ LOG_ID(INFO, player_id_) << "Already performing player_prepare_async";
+ return true;
+ }
+ LOG_ID(INFO, player_id_) << "Call player_prepare_async";
+
int ret = player_prepare_async(player_, PlayerPreparedCb, this);
if (ret != PLAYER_ERROR_NONE) {
is_preparing_ = false;
int err = PlayerSetPlayPosition(player_, time, true, SeekCompletedCb, this);
if (err != PLAYER_ERROR_NONE) {
LOG_ID(ERROR, player_id_) << "|player_set_play_position| failed";
+ UpdateSeekState(false);
OnTimeUpdate(player_id_, GetCurrentTime());
OnTimeChanged(player_id_);
if (seek_cb_)
}
void MediaPlayerBridgeCapi::UpdateSeekState(bool state) {
+ LOG_ID(INFO, player_id_) << "UpdateSeekState " << state;
is_seeking_ = state;
}
switch (delayed_player_state_) {
case PLAYER_STATE_DELAYED_PLAY:
delayed_player_state_ = PLAYER_STATE_DELAYED_NULL;
- Play();
+ {
+ bool ret = Play();
+ LOG_ID(INFO, player_id_)
+ << "Play returned " << ret << " delayed_player_state_ "
+ << delayed_player_state_;
+ }
break;
case PLAYER_STATE_DELAYED_PAUSE:
delayed_player_state_ = PLAYER_STATE_DELAYED_NULL;
}
return;
}
+
+ if (waiting_for_drm_init_complete_) {
+ LOG_ID(INFO, player_id_) << "Waiting for DRM init, don't seek";
+ return;
+ }
+
LOG_ID(INFO, player_id_)
<< "(" << static_cast<void*>(this) << ") " << __func__
<< " applying pending seek - " << pending_seek_duration_.InSecondsF();
return;
}
+ if (waiting_for_drm_init_complete_) {
+ LOG_ID(INFO, player_id_) << "Waiting for DRM init, don't seek";
+ return;
+ }
LOG_ID(INFO, player_id_)
<< "(" << static_cast<void*>(this) << ") " << __func__
<< " applying pending seek, then play - "
GURL url_;
bool is_live_stream_{false};
int player_id_ = 0;
- int delayed_player_state_;
+ MediaPlayerBridgeCapi::PlayerState delayed_player_state_;
player_h player_ = nullptr;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
bool is_preparing_{false};
bool is_play_pending_{false};
bool suspended_{false};
+ bool waiting_for_drm_init_complete_ = false;
bool pending_set_volume_{false};
double volume_{0.0};
namespace {
const int kSeekableTimeUpdateInterval = 500;
-}
+const int64_t kDrmTimeoutSeconds = 10;
+} // namespace
static void OnSubtitleDataCallback(unsigned long long time_stamp,
unsigned index,
MediaPlayerBridgeCapiTV::~MediaPlayerBridgeCapiTV() {
LOG_ID(INFO, player_id_) << "(" << static_cast<void*>(this) << ") "
<< __func__;
+ drm_condition_.Signal();
weak_factory_.InvalidateWeakPtrs();
if (GetProductType() == "LFD") {
return true;
}
+bool MediaPlayerBridgeCapiTV::SetURIToPlayer(const std::string& fs_path) {
+#if BUILDFLAG(IS_TIZEN_TV)
+ if (blink::IsHbbTV()) {
+ if (IsStandaloneEme()) {
+ int ret = PreparePlayerForStandaloneEme();
+ if (PLAYER_ERROR_NONE != ret) {
+ OnHandlePlayerError(ret, FROM_HERE);
+ return false;
+ }
+
+ if (!is_preloaded_) {
+ player_set_uri(player_,
+ (hbbtv_url_ + "|DRM_TYPE=DRM_TYPE_CENC").c_str());
+ }
+ } else if (!is_preloaded_) {
+ player_set_uri(player_, hbbtv_url_.c_str());
+ }
+
+#ifdef TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT
+ if (!IsStandaloneEme() && IsEncryptedContent()) {
+ if (PreparePlayerDrmManager() == MM_PLAYER_ERROR) {
+ return false;
+ }
+ }
+#endif
+ }
+#endif
+
+ return true;
+}
+
void MediaPlayerBridgeCapiTV::Prepare() {
int ret = PLAYER_ERROR_NONE;
bool media_resource_acquired = false;
std::string translated_url;
- std::string drm_info;
if (IsPlayerSuspended() && !is_resuming_) {
LOG(INFO) << "Player is suspended, and !is_resuming_ - call Resume()";
if (GetMediaPlayerClient())
GetMediaPlayerClient()->NotifyPlaybackState(
kPlaybackLoad, player_id_, url_.spec(), mime_type_,
- &media_resource_acquired, &translated_url, &drm_info);
+ &media_resource_acquired, &translated_url, &drm_info_);
if (!SetAppInfo()) {
LOG(ERROR) << "SetAppInfo failed";
LOG_ID(INFO, GetPlayerId())
<< "media_resource_acquired: " << media_resource_acquired
- << ",translated_url:" << translated_url << ",drm_info: " << drm_info;
+ << ",translated_url:" << translated_url << ",drm_info: " << drm_info_;
if (mime_type_.find("application/vnd.oipf.contentaccessstreaming+xml") !=
std::string::npos) {
GetMediaPlayerClient()->PlaybackNotificationEnabled() &&
blink::IsHbbTV()) {
#if !defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
- if (!SetDrmInfo(drm_info)) {
+ if (!SetDrmInfo(drm_info_)) {
LOG_ID(ERROR, GetPlayerId()) << "SetDrmInfo failed";
return;
}
if (blink::IsHbbTV()) {
hbbtv_url_ = url_.spec();
content_id_ = url_.spec();
+ LOG_ID(INFO, player_id_) << "content_id_ set " << content_id_;
clean_url_ = url_.spec();
RemoveUrlSuffixForCleanUrl();
GetUserPreferAudioLanguage();
}
if (blink::IsHbbTV() && CheckHighBitRate() && stream_type_ == DASH_STREAM)
AppendUrlHighBitRate(url_.spec());
- player_prepared_ = false;
- // TODO(k.brzuszczak): To remove when EME ported
- if (!is_preloaded_) {
- player_set_uri(player_, hbbtv_url_.c_str());
+ if (!SetURIToPlayer(url_.spec())) {
+ LOG_ID(ERROR, GetPlayerId()) << "set uri failed";
+ return;
}
+ player_prepared_ = false;
+ player_released_ = false;
parental_rating_pass_ = true;
MediaPlayerBridgeCapi::Prepare();
if (!is_preloading_ && GetMediaPlayerClient()) {
void MediaPlayerBridgeCapiTV::Release() {
is_preloaded_ = false;
player_prepared_ = false;
+ if (player_released_) {
+ LOG_ID(INFO, GetPlayerId()) << "player is already released";
+ return;
+ }
+ player_released_ = true;
+ is_preloaded_ = false;
+ drm_condition_.Signal();
+
StopSeekableTimeUpdateTimer();
if (IsMultiVideo() && is_player_mixer_opened_) {
int ret = player_video_mixer_close(player_, DEPRECATED_PARAM);
}
SetDecoderReleased(content::kAudioDecoder);
}
- if (GetMediaPlayerClient())
- GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
- NotifySubtitleState(blink::WebMediaPlayer::kSubtitleStop);
+
+ // If the encrypted_listener_or_cdm_has_changed_ is true, then we're
+ // restarting due to this, so don't send the NotifyPlaybackState.
+ if (encrypted_listener_or_cdm_has_changed_ == false) {
+ if (GetMediaPlayerClient()) {
+ GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
+ }
+ NotifySubtitleState(blink::WebMediaPlayer::kSubtitleStop);
+ }
+
pending_active_text_track_id_ = active_text_track_id_;
pending_active_audio_track_id_ = active_audio_track_id_;
pending_active_video_track_id_ = active_video_track_id_;
if (!MediaPlayerBridgeCapi::Play())
return false;
+ drm_condition_.Signal();
+
if (blink::IsHbbTV()) {
if (!ActivateAudioStreamIfNeeded()) {
return false;
void MediaPlayerBridgeCapiTV::Seek(base::TimeDelta time,
base::OnceClosure seek_cb) {
LOG_ID(INFO, player_id_) << "(" << static_cast<void*>(this) << ") "
- << __func__ << " time: " << time.InSecondsF();
+ << __func__ << " time: " << time.InSecondsF()
+ << ", playback_time_: " << playback_time_
+ << ", waiting_for_drm_init_complete_: "
+ << waiting_for_drm_init_complete_;
// Ignore seek in release state. Will catch-up later.
if (GetPlayerState() == PLAYER_STATE_NONE) {
LOG_ID(INFO, player_id_) << __func__ << " Ignore seek in release state.";
}
}
seek_cb_ = std::move(seek_cb);
- if (IsPlayerSuspended() && playback_time_ != time) {
+ bool is_suspend_seek = IsPlayerSuspended() && playback_time_ != time;
+ if (is_seeking_ || is_suspend_seek || waiting_for_drm_init_complete_ ||
+ is_preparing_ || is_preloading_) {
+ LOG_ID(INFO, player_id_)
+ << "(" << static_cast<void*>(this) << ") "
+ << " stashing pending seek - " << time.InSecondsF() << "s";
+
playback_time_ = time;
- delayed_player_state_ = PLAYER_STATE_DELAYED_SEEK;
+ if (delayed_player_state_ == PLAYER_STATE_DELAYED_PLAY ||
+ delayed_player_state_ == PLAYER_STATE_DELAYED_SEEK_THEN_PLAY) {
+ delayed_player_state_ = PLAYER_STATE_DELAYED_SEEK_THEN_PLAY;
+ } else {
+ delayed_player_state_ = PLAYER_STATE_DELAYED_SEEK;
+ }
pending_seek_duration_ = playback_time_;
OnTimeUpdate(player_id_, playback_time_);
OnTimeChanged(player_id_);
return;
}
+ LOG_ID(INFO, player_id_) << "SeekInternal";
MediaPlayerBridgeCapi::SeekInternal(time);
+
+ LOG_ID(INFO, player_id_) << "NotifySubtitleState seek start";
NotifySubtitleState(blink::WebMediaPlayer::kSubtitleSeekStart,
time.InSecondsF());
void MediaPlayerBridgeCapiTV::PlayerPrepared() {
player_prepared_ = true;
+
+ // If the encrypted_listener_or_cdm_has_changed_ flag is true, then the
+ // CDM has changed or been set since we started to preload, so we'll
+ // restart the preload/prepare to use the new CDM.
+ if (encrypted_listener_or_cdm_has_changed_) {
+ Release();
+ encrypted_listener_or_cdm_has_changed_ = false;
+ Prepare();
+ return;
+ }
+ is_resuming_ = false;
MediaPlayerBridgeCapi::PlayerPrepared();
int canSeek = 0;
<< " is_live_stream_ " << is_live_stream_
<< " player_prepared_ " << player_prepared_;
if (is_paused_) {
+ LOG_ID(INFO, player_id_) << "Paused, return";
return;
}
}
if (GetPlayerState() != PLAYER_STATE_PLAYING)
return;
+
+ LOG_ID(INFO, player_id_) << "Pause due to buffering " << percent;
if (player_pause(player_) != PLAYER_ERROR_NONE) {
LOG(ERROR) << "|player_pause| failed";
return;
LOG_ID(INFO, GetPlayerId()) << "PlayerPreloaded,this: " << this
<< ",player_prepared_:" << player_prepared_;
+ // If the encrypted_listener_or_cdm_has_changed_ flag is true, then the
+ // CDM has changed or been set since we started to preload, so we'll
+ // restart the preload/prepare to use the new CDM.
+ if (encrypted_listener_or_cdm_has_changed_) {
+ Release();
+ encrypted_listener_or_cdm_has_changed_ = false;
+ Prepare();
+ return;
+ }
+
is_preloaded_ = true;
is_preloading_ = false;
is_live_stream_ = CheckLiveStreaming();
int ret = PLAYER_ERROR_INVALID_OPERATION;
const void* type_data_ptr = type_data.c_str();
const void* value_data_ptr = value_data.c_str();
+ uses_hbbtv_drm_ = true;
player_drm_type_e drm_type = PLAYER_DRM_TYPE_PLAYREADY;
ret = player_set_drm_info(player_, type_data_ptr, type_data.length(),
value_data_ptr, value_data.length(), drm_type);
base::BindOnce(&MediaPlayerBridgeCapiTV::OnDrmError,
weak_factory_.GetWeakPtr(), err_code, err_str));
}
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+ content::WebContentsDelegate* web_contents_delegate =
+ GetMediaPlayerClient()->GetWebContentsDelegate();
+ if (web_contents_delegate) {
+ web_contents_delegate->NotifyDRMError(err_code, {});
+ }
+#endif
MediaPlayerBridgeCapi::OnHandlePlayerError(err_code, FROM_HERE);
}
}
std::string MediaPlayerBridgeCapiTV::GetContentId() {
+ LOG_ID(INFO, player_id_) << content_id_;
return content_id_;
}
return -1;
}
+#if BUILDFLAG(IS_TIZEN_TV)
+void MediaPlayerBridgeCapiTV::SetHasEncryptedListenerOrCdm(bool value) {
+ LOG_ID(INFO, GetPlayerId()) << "Has encrypted listener or CDM: " << value;
+
+ // If the CDM has changed, and we're still preloading or preparing, then
+ // set the encrypted_listener_or_cdm_has_changed_ flag - when the current
+ // preloading/preparing completed, we'll re-set and restart the
+ // preload/prepare with the correct CDM settings.
+ if ((has_encrypted_listener_or_cdm_ != value) &&
+ (is_preparing_ || is_preloading_)) {
+ encrypted_listener_or_cdm_has_changed_ = true;
+ }
+
+ has_encrypted_listener_or_cdm_ = value;
+}
+
+void MediaPlayerBridgeCapiTV::SetDecryptorHandle(
+ eme::eme_decryptor_t decryptor) {
+ LOG_ID(INFO, GetPlayerId())
+ << "Received decryptor " << reinterpret_cast<void*>(decryptor);
+
+ base::AutoLock auto_lock(drm_lock_);
+ decryptor_ = decryptor;
+
+ if (decryptor == 0) {
+ OnHandlePlayerError(PLAYER_ERROR_DRM_EXPIRED, FROM_HERE);
+ }
+
+ drm_condition_.Signal();
+}
+
+bool MediaPlayerBridgeCapiTV::IsEncryptedContent() const {
+ bool is_encrypted = false;
+ std::vector<std::string> drm_info_pairs = base::SplitStringUsingSubstr(
+ drm_info_, ", ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ for (auto it = drm_info_pairs.begin(); it != drm_info_pairs.end(); ++it) {
+ std::vector<std::string> drm_info_pair = base::SplitStringUsingSubstr(
+ *it, ": ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ if (drm_info_pair.size() != 2) {
+ continue;
+ }
+
+ std::string trim_key = drm_info_pair.at(0);
+ std::string trim_value = drm_info_pair.at(1);
+ if (trim_key == "\"detected_drm\"" && trim_value != "\"0\"") {
+ is_encrypted = true;
+ break;
+ }
+ }
+ return is_encrypted;
+}
+
+bool MediaPlayerBridgeCapiTV::IsStandaloneEme() {
+ // assumes HbbTV DRM is inactive
+ bool is_hbbtv_drm_inactive = true;
+
+ content::WebContentsDelegate* contents_delegate =
+ GetMediaPlayerClient()->GetWebContentsDelegate();
+ if (contents_delegate != nullptr) {
+ auto& active_drm = contents_delegate->GetActiveDRM();
+
+ LOG_ID(INFO, GetPlayerId()) << "active DRM: '" << active_drm << "'";
+
+ // for HbbTV 2.0.2 assumes inactive DRM unless drmAgent.setActiveDRM() was
+ // called with something else than "urn:hbbtv:oipfdrm:inactive"
+
+ is_hbbtv_drm_inactive =
+ active_drm.empty() || (active_drm == "urn:hbbtv:oipfdrm:inactive");
+ }
+
+ bool ret = blink::IsHbbTV() && (stream_type_ == DASH_STREAM) &&
+ is_hbbtv_drm_inactive && !uses_hbbtv_drm_ &&
+ has_encrypted_listener_or_cdm_;
+
+ LOG_ID(INFO, GetPlayerId())
+ << "is HbbTV: " << blink::IsHbbTV()
+ << ", HbbTV DRM inactive: " << is_hbbtv_drm_inactive
+ << ", uses HbbTV DRM: " << uses_hbbtv_drm_
+ << ", stream type: " << stream_type_
+ << ", has encrypted listener or CDM: " << has_encrypted_listener_or_cdm_
+ << ", standalone EME: " << ret;
+
+ return ret;
+}
+
+int MediaPlayerBridgeCapiTV::PreparePlayerForDrmEme(
+ set_drm_init_data_cb init_data_cb,
+ security_init_complete_cb init_complete_cb) {
+ waiting_for_drm_init_complete_ = false;
+ auto player_res = player_set_drm_handle(player_, PLAYER_DRM_TYPE_EME, 0);
+ if (PLAYER_ERROR_NONE != player_res) {
+ LOG_ID(ERROR, GetPlayerId())
+ << "Failed to set drm handle with error [" << player_res << "]";
+ return player_res;
+ }
+
+ player_res = player_set_drm_init_data_cb(player_, init_data_cb, this);
+ if (PLAYER_ERROR_NONE != player_res) {
+ LOG_ID(ERROR, GetPlayerId())
+ << "Failed to set drm init data cb with error [" << player_res << "]";
+ return player_res;
+ }
+
+ player_res = player_set_drm_init_complete_cb(player_, init_complete_cb, this);
+ if (PLAYER_ERROR_NONE != player_res) {
+ LOG_ID(ERROR, GetPlayerId())
+ << "Failed to set drm init complete cb with error [" << player_res
+ << "]";
+ return player_res;
+ }
+
+ return player_res;
+}
+
+void MediaPlayerBridgeCapiTV::WaitingDRMInitComplete() {
+ base::AutoLock auto_lock(drm_lock_);
+ if (decryptor_ != 0) {
+ LOG_ID(INFO, player_id_) << "Already got decryptor!";
+ waiting_for_drm_init_complete_ = false;
+ return;
+ }
+
+ LOG_ID(INFO, player_id_) << "Got Init Data, waiting for init complete";
+ waiting_for_drm_init_complete_ = true;
+}
+
+int MediaPlayerBridgeCapiTV::PreparePlayerForStandaloneEme() {
+ LOG_ID(INFO, GetPlayerId()) << "Preparing player for standalone DRM EME";
+
+ const auto init_data_cb = [](drm_init_data_type init_type, void* data,
+ int data_length, void* user_data) -> int {
+ LOG(INFO) << "Received init data, size: " << data_length
+ << ", type: " << init_type;
+ if (init_type != CENC) {
+ LOG(WARNING) << "Not CENC init data type";
+ return NOT_CENC_DATA;
+ }
+ if (!user_data) {
+ LOG(ERROR) << "No user data";
+ return NO_USER_DATA;
+ }
+
+ auto data_bytes = static_cast<unsigned char*>(data);
+ std::vector<unsigned char> init_data(data_bytes, data_bytes + data_length);
+
+ auto thiz = static_cast<MediaPlayerBridgeCapiTV*>(user_data);
+ if (thiz->GetMediaPlayerClient()) {
+ thiz->WaitingDRMInitComplete();
+ thiz->GetMediaPlayerClient()->OnInitData(init_data);
+ }
+
+ return ERROR_NONE;
+ };
+
+ const auto init_complete_cb = [](int* drmhandle, unsigned int length,
+ unsigned char* psshdata,
+ void* user_data) -> bool {
+ if (!user_data) {
+ LOG(ERROR) << "No user data";
+ return false;
+ }
+
+ auto thiz = static_cast<MediaPlayerBridgeCapiTV*>(user_data);
+ return thiz->CompleteDrmInit(*drmhandle);
+ };
+
+ return PreparePlayerForDrmEme(init_data_cb, init_complete_cb);
+}
+
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+InitDataError MediaPlayerBridgeCapiTV::PreparePlayerDrmManager() {
+ LOG_ID(INFO, GetPlayerId()) << "Preparing player for DRM EME";
+
+ const auto init_data_cb = [](drm_init_data_type init_type, void* data,
+ int data_length, void* user_data) -> int {
+ if (!user_data) {
+ return NULL_PTR_ERROR;
+ }
+
+ const auto data_bytes = static_cast<unsigned char*>(data);
+ std::vector<unsigned char> init_data(data_bytes, data_bytes + data_length);
+
+ auto thiz = static_cast<MediaPlayerBridgeCapiTV*>(user_data);
+ thiz->WaitingDRMInitComplete();
+ thiz->task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MediaPlayerBridgeCapiTV::NotifyDrmInitData,
+ thiz->weak_factory_.GetWeakPtr(), init_type, init_data));
+ return ERROR_NONE;
+ };
+
+ const auto init_complete_cb = [](int* drmhandle, unsigned int length,
+ unsigned char* psshdata,
+ void* user_data) -> bool {
+ if (!user_data) {
+ return NULL_PTR_ERROR;
+ }
+ auto thiz = static_cast<MediaPlayerBridgeCapiTV*>(user_data);
+ LOG_ID(INFO, thiz->GetPlayerId())
+ << "Post to task to run NotifyDrmInitComplete";
+ thiz->task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MediaPlayerBridgeCapiTV::NotifyDrmInitComplete,
+ thiz->weak_factory_.GetWeakPtr(), length, psshdata));
+ LOG_ID(INFO, thiz->GetPlayerId()) << "Wait for result....";
+ thiz->CompleteDrmInit(*drmhandle);
+ return *drmhandle != 0;
+ };
+
+ int ret = PreparePlayerForDrmEme(init_data_cb, init_complete_cb);
+ if (ret != PLAYER_ERROR_NONE) {
+ return MM_PLAYER_ERROR;
+ }
+ return ERROR_NONE;
+}
+
+void MediaPlayerBridgeCapiTV::NotifyDrmInitData(
+ int init_type,
+ const std::vector<unsigned char>& data) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ content::WebContentsDelegate* web_contents_delegate =
+ GetMediaPlayerClient()->GetWebContentsDelegate();
+ if (!web_contents_delegate) {
+ return;
+ }
+
+ web_contents_delegate->NotifyDRMInitData(init_type, data);
+}
+
+void MediaPlayerBridgeCapiTV::NotifyDrmInitComplete(unsigned int length,
+ unsigned char* psshdata) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ base::AutoLock auto_lock(drm_lock_);
+
+ content::WebContentsDelegate* web_contents_delegate =
+ GetMediaPlayerClient()->GetWebContentsDelegate();
+ if (!web_contents_delegate) {
+ return;
+ }
+
+ if (decryptor_ == 0) {
+ decryptor_ = web_contents_delegate->NotifyDRMInitComplete(length, psshdata);
+ LOG(INFO) << "decryptor_ = " << decryptor_;
+ }
+ drm_condition_.Signal();
+}
+#endif // TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT
+
+bool MediaPlayerBridgeCapiTV::CompleteDrmInit(int& drmhandle) {
+ base::AutoLock auto_lock(drm_lock_);
+
+ if (player_released_) {
+ LOG_ID(INFO, GetPlayerId())
+ << "Not waiting for decryptor as player is releasing";
+ drmhandle = 0;
+ return false;
+ }
+
+ if (decryptor_ == 0) {
+ // OnPlayerDrmInitComplete callback will be invoked by CAPI Player when
+ // drmeme GStreamer element will attempt decrypting first frame.
+ // drmeme then waits 20 seconds during which it expects to receive
+ // decryptor handle. If this time-outs player error is reported.
+ // If the decryptor_ is not yet known below we need to wait for it.
+ // It will arrive when Media Keys Session is updated with license data
+ // for the first time.
+ // If the application misbehaves the wait here would be indefinite blocking
+ // other callbacks registered in CAPI Player.
+ // The timed wait here is a safety net for cases when the
+ // OnPlayerDrmInitComplete callback was invoked but we the decryptor handle
+ // doesn't arrive on time.
+ // 10 seconds was picked here, because it should be enough for a roundtrip
+ // to the license server, and HbbTV requires unavailable keys to timeout in
+ // < 20 seconds.
+ LOG_ID(INFO, GetPlayerId()) << "Waiting for decryptor...";
+
+ if (IsStandaloneEme()) {
+ if (GetMediaPlayerClient()) {
+ GetMediaPlayerClient()->OnWaiting(
+ media::WaitingReason::kNoDecryptionKey);
+ }
+ }
+
+ base::TimeTicks start = base::TimeTicks::Now();
+ while (decryptor_ == 0) {
+ base::TimeTicks curr = base::TimeTicks::Now();
+ if ((curr - start).InSeconds() >= kDrmTimeoutSeconds) {
+ break;
+ }
+ LOG_ID(WARNING, GetPlayerId())
+ << "Wait for " << (kDrmTimeoutSeconds - (curr - start).InSeconds());
+ drm_condition_.TimedWait(base::TimeDelta::FromSeconds(
+ kDrmTimeoutSeconds - (curr - start).InSeconds()));
+ }
+ if (decryptor_ == 0) {
+ LOG_ID(ERROR, GetPlayerId())
+ << "No decryptor received before drmeme timeout!";
+ OnMediaError(MEDIA_ERROR_DECODE);
+ } else {
+ base::TimeTicks curr = base::TimeTicks::Now();
+ LOG_ID(INFO, GetPlayerId()) << "Waited " << (curr - start).InSecondsF();
+ }
+ } else {
+ LOG_ID(INFO, GetPlayerId()) << "Decryptor already set";
+ }
+ LOG_ID(INFO, GetPlayerId())
+ << "Decryptor: " << reinterpret_cast<void*>(decryptor_);
+
+ drmhandle = decryptor_;
+ waiting_for_drm_init_complete_ = false;
+ return (decryptor_ != 0);
+}
+#endif
} // namespace media
#ifndef MEDIA_FILTERS_MEDIA_PLAYER_BRIDGE_CAPI_TV_H_
#define MEDIA_FILTERS_MEDIA_PLAYER_BRIDGE_CAPI_TV_H_
+#include <emeCDM/IEME.h>
#include <player_product.h>
+#include "base/synchronization/condition_variable.h"
#include "media/filters/hardware_resource_helper.h"
#include "media/filters/media_player_bridge_capi.h"
#include "tizen_src/chromium_impl/build/tizen_version.h"
SCALER_SUB3,
};
+enum InitDataError {
+ ERROR_NONE = 0,
+ NOT_CENC_DATA,
+ NO_USER_DATA,
+ NULL_PTR_ERROR,
+ MM_PLAYER_ERROR,
+};
+
#if defined(TIZEN_VD_NGA)
struct AudioPreselectionInfos {
std::string id;
double GetStartDate() override;
// LFD TV mixer mode
- bool IsMuted(void);
+ bool IsMuted();
void SetMultiVideoState();
void ActivateCurrentStream(player_stream_type_e stream_type);
bool IsMultiVideo() const override;
void SetMultiVideo(bool is_multi_video) override;
bool Is4thVideo() override { return is_4th_video_; }
+ void SetHasEncryptedListenerOrCdm(bool value) override;
+ void SetDecryptorHandle(eme::eme_decryptor_t decryptor) override;
+ bool SetURIToPlayer(const std::string&);
protected:
void PlayerPrepared() override;
bool is_player_seek_available_{true};
bool is_notification_playback_ready_delayed_{false};
bool leave_not_visible_{false};
+ std::string drm_info_{""};
void NotifySubtitlePlay(int id,
const std::string& url,
void SetVideoVisibilityPrivate(bool);
#endif
+ bool IsStandaloneEme();
+ bool IsEncryptedContent() const;
+ int PreparePlayerForDrmEme(set_drm_init_data_cb init_data_cb,
+ security_init_complete_cb init_complete_cb);
+ int PreparePlayerForStandaloneEme();
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+ InitDataError PreparePlayerDrmManager();
+ void NotifyDrmInitData(int init_type, const std::vector<unsigned char>& data);
+ void NotifyDrmInitComplete(unsigned int length, unsigned char* psshdata);
+#endif
+ bool CompleteDrmInit(int& drmhandle);
+
StreamType stream_type_{OTHER_STREAM};
std::string hbbtv_url_{""}; // url_ + HIGHBITRATE(if mpd)
std::string mime_type_ = "";
base::TimeDelta min_seekable_time_{base::TimeDelta()};
base::TimeDelta max_seekable_time_{base::TimeDelta()};
base::RepeatingTimer seekable_time_update_timer_;
+ void WaitingDRMInitComplete();
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MediaPlayerBridgeCapiTV> weak_factory_;
+
+ bool has_encrypted_listener_or_cdm_{false};
+ // Flag indicating that has_encrypted_listener_or_cdm_ has been changed.
+ bool encrypted_listener_or_cdm_has_changed_{false};
+ bool uses_hbbtv_drm_{false};
+ eme::eme_decryptor_t decryptor_{0};
+ base::Lock drm_lock_;
+ base::ConditionVariable drm_condition_{&drm_lock_};
+ std::atomic<bool> player_released_;
};
} // namespace media
#include "ui/gfx/geometry/rect_f.h"
#endif
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <drmdecrypt/emeCDM/IEME.h>
+#endif
+
namespace media {
class DemuxerStream;
#else
#include <ivideo-dp-control.hpp>
#endif // TIZEN_VERSION_AT_LEAST(9, 0, 0) && !BUILDFLAG(IS_RISCV64_TV)
+
+#include <drmdecrypt/emeCDM/IEME.h>
#endif // BUILDFLAG(IS_TIZEN_TV)
namespace media {
virtual bool IsMultiVideo() const { return false; }
virtual void SetMultiVideo(bool is_multi_video) {}
virtual void SetVideoVisibility(bool visible) {}
+#if BUILDFLAG(IS_TIZEN_TV)
+ virtual void SetDecryptorHandle(eme::eme_decryptor_t decryptor) {}
+ virtual void SetHasEncryptedListenerOrCdm(bool value) {}
+#endif
};
#if BUILDFLAG(IS_TIZEN_TV)
virtual content::WebContentsDelegate* GetWebContentsDelegate() const = 0;
virtual void OnPlayerStarted(bool play_started) = 0;
virtual void GetFpsInfo(int& num, int& den) = 0;
+ virtual void OnInitData(const std::vector<unsigned char>& init_data) = 0;
#endif
};
// found in the LICENSE file.
// Multiply-included file, no traditional include guard.
+#include <vector>
#include "base/values.h"
#include "common/hit_test_params.h"
std::string /* scheme */)
IPC_MESSAGE_CONTROL1(EwkViewMsg_SetTimeOffset, double /* time offset */)
+
+IPC_MESSAGE_ROUTED1(EwkFrameMsg_SetKeySystemWhitelist,
+ std::vector<std::string> /* key system list */)
+
// Sent when the renderer was prevented from displaying insecure content in
// a secure page by a security policy. The page may appear incomplete.
IPC_MESSAGE_ROUTED0(EwkHostMsg_DidBlockInsecureContent)
RenderViewHost* render_view_host = web_contents_->GetRenderViewHost();
+#if BUILDFLAG(IS_TIZEN_TV)
+ SendKeySystemWhitelist();
+#endif
+
SendDelayedMessages(render_view_host);
if (render_view_host) {
ewkMediaSubtitleDataDelete(subtitle_data);
}
+bool EWebView::SetKeySystemWhitelist(const char* list[], unsigned list_size) {
+ key_system_whitelist_.clear();
+ for (unsigned n = 0; n < list_size; ++n) {
+ key_system_whitelist_.emplace_back(list[n]);
+ LOG(INFO) << "whitelisted key system: " << key_system_whitelist_.back();
+ }
+
+ return SendKeySystemWhitelist();
+}
+
+void EWebView::SetActiveDRM(const char* drm_system_id) {
+ active_drm_ = drm_system_id ? drm_system_id : "";
+ LOG(INFO) << "active DRM: \"" << active_drm_ << "\"";
+}
+
+const std::string& EWebView::GetActiveDRM() const {
+ return active_drm_;
+}
+
+bool EWebView::SendKeySystemWhitelist() const {
+ RenderFrameHost* render_frame_host = web_contents_->GetPrimaryMainFrame();
+
+ if (render_frame_host) {
+ LOG(INFO) << "sending key system whitelist"
+ << ", size: " << key_system_whitelist_.size();
+ const bool send_result =
+ render_frame_host->Send(new EwkFrameMsg_SetKeySystemWhitelist(
+ render_frame_host->GetRoutingID(), key_system_whitelist_));
+ if (!send_result) {
+ LOG(WARNING) << "failed sending key system whitelist";
+ }
+ return send_result;
+ } else {
+ // will try sending when render frame host is available (RenderViewCreated)
+ return true;
+ }
+}
+
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+void EWebView::NotifyDRMInitData(int init_type,
+ const std::vector<unsigned char>& data) {
+ Ewk_DRM_Init_Data_Info info;
+ info.init_type = init_type;
+ info.data = static_cast<void*>(const_cast<unsigned char*>(data.data()));
+ info.data_length = data.size();
+
+ SmartCallback<EWebViewCallbacks::DRMInitData>().call(
+ static_cast<void*>(&info));
+}
+
+int EWebView::NotifyDRMInitComplete(unsigned int pssh_data_length,
+ unsigned char* pssh_data) {
+ Ewk_DRM_Init_Complete_Info info;
+ info.pssh_data_length = pssh_data_length;
+ info.pssh_data = pssh_data;
+ info.decryptor = 0;
+
+ SmartCallback<EWebViewCallbacks::DRMInitComplete>().call(
+ static_cast<void*>(&info));
+
+ return info.decryptor;
+}
+
+void EWebView::NotifyDRMError(int error_code, const std::string& error_string) {
+ Ewk_DRM_Error_Info info{error_code, error_string.c_str()};
+ SmartCallback<EWebViewCallbacks::DRMError>().call(static_cast<void*>(&info));
+}
+#endif
+
void EWebView::UpdateCurrentTime(double current_time) {
current_time_ = current_time;
}
unsigned int len,
int media_position);
void SetPreferSubtitleLang(const char* lang_list);
+ bool SetKeySystemWhitelist(const char* list[], unsigned list_size);
+ void SetActiveDRM(const char* drm_system_id);
+ const std::string& GetActiveDRM() const;
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+ void NotifyDRMInitData(int init_type, const std::vector<unsigned char>& data);
+ int NotifyDRMInitComplete(unsigned int pssh_data_length,
+ unsigned char* pssh_data);
+ void NotifyDRMError(int error_code, const std::string& error_string);
+#endif
void NotifySubtitleState(int state, double time_stamp);
void NotifySubtitlePlay(int active_track_id,
const char* url,
WebViewAsyncRequestHitTestDataCallback* cb);
#if BUILDFLAG(IS_TIZEN_TV)
+ bool SendKeySystemWhitelist() const;
void InitInspectorServer();
void RunPendingSetFocus(Eina_Bool focus);
double current_time_ = 0.0;
bool use_sw_audio_decoder_ = false;
Ewk_Hardware_Decoders broadcast_decoder_ = EWK_HARDWARE_DECODERS_NONE;
+ std::vector<std::string> key_system_whitelist_;
+ std::string active_drm_;
#endif
std::unique_ptr<_Ewk_Back_Forward_List> back_forward_list_;
FirstTimestamp,
PESData,
EVENTData,
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+ DRMInitData,
+ DRMInitComplete,
+ DRMError,
+#endif
#endif
OverscrolledLeft,
OverscrolledRight,
DECLARE_EWK_VIEW_CALLBACK(FirstTimestamp, "on,dvb,subtitle,timestamp", void*);
DECLARE_EWK_VIEW_CALLBACK(PESData, "on,dvb,subtitle,data", void*);
DECLARE_EWK_VIEW_CALLBACK(EVENTData, "update,event,data", void*);
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+DECLARE_EWK_VIEW_CALLBACK(DRMInitData, "drm,init,data", void*);
+DECLARE_EWK_VIEW_CALLBACK(DRMInitComplete, "drm,init,complete", void*);
+DECLARE_EWK_VIEW_CALLBACK(DRMError, "drm,error", void*);
+#endif
#endif
DECLARE_EWK_VIEW_CALLBACK(OverscrolledLeft, "overscrolled,left", void);
DECLARE_EWK_VIEW_CALLBACK(OverscrolledRight, "overscrolled,right", void);
return EINA_FALSE;
}
-Eina_Bool ewk_view_key_system_whitelist_set(Evas_Object* ewkView, const char** list, unsigned list_size)
-{
- LOG_EWK_API_MOCKUP();
+Eina_Bool ewk_view_key_system_whitelist_set(Evas_Object* ewkView,
+ const char** list,
+ unsigned list_size) {
+#if BUILDFLAG(IS_TIZEN_TV)
+ EWK_VIEW_IMPL_GET_OR_RETURN(ewkView, impl, EINA_FALSE);
+ const bool result = impl->SetKeySystemWhitelist(list, list_size);
+ return (result ? EINA_TRUE : EINA_FALSE);
+#else
+ LOG_EWK_API_MOCKUP("Only for Tizen TV.");
return EINA_FALSE;
+#endif
}
Eina_Bool ewk_view_is_video_playing(Evas_Object* o, Ewk_Is_Video_Playing_Callback callback, void* user_data)
Eina_Bool ewk_view_active_drm_set(Evas_Object* view, const char* drm_system_id)
{
- LOG_EWK_API_MOCKUP();
+#if BUILDFLAG(IS_TIZEN_TV)
+ EWK_VIEW_IMPL_GET_OR_RETURN(view, impl, EINA_FALSE);
+ impl->SetActiveDRM(drm_system_id);
+ return EINA_TRUE;
+#else
+ LOG_EWK_API_MOCKUP("Only for Tizen TV.");
return EINA_FALSE;
+#endif
}
void ewk_view_broadcast_decoder_set(
#ifndef CONTENT_RENDERER_CLIENT_EFL_H
#define CONTENT_RENDERER_CLIENT_EFL_H
+#include <string>
+#include <vector>
+
#include "content/public/renderer/content_renderer_client.h"
#include "content/public/renderer/render_frame_observer.h"
#include "renderer/render_thread_observer_efl.h"
int element_id) override;
#endif // defined(TIZEN_TV_UPSTREAM_MULTIMEDIA)
+#if BUILDFLAG(IS_TIZEN_TV)
+ void SetKeySystemWhitelist(const std::vector<std::string>& key_systems) {
+ key_system_whitelist_ = key_systems;
+ }
+#endif
+
private:
static void ApplyCustomMobileSettings(blink::WebView*);
IPC_MESSAGE_HANDLER(EwkFrameMsg_LoadNotFoundErrorPage, OnLoadNotFoundErrorPage)
IPC_MESSAGE_HANDLER(EwkFrameMsg_MoveToNextOrPreviousSelectElement, OnMoveToNextOrPreviousSelectElement)
IPC_MESSAGE_HANDLER(EwkFrameMsg_RequestSelectCollectionInformation, OnRequestSelectCollectionInformation);
+#if BUILDFLAG(IS_TIZEN_TV)
+ IPC_MESSAGE_HANDLER(EwkFrameMsg_SetKeySystemWhitelist,
+ OnSetKeySystemWhitelist)
+#endif
IPC_MESSAGE_HANDLER(EwkFrameMsg_GetPlainText, OnGetPlainText);
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
#endif
}
+#if BUILDFLAG(IS_TIZEN_TV)
+void RenderFrameObserverEfl::OnSetKeySystemWhitelist(
+ const std::vector<std::string>& list) {
+ auto client = static_cast<ContentRendererClientEfl*>(
+ GetContentClientExport()->renderer());
+ LOG(INFO) << "received key system whitelist, size: " << list.size();
+ if (client) {
+ client->SetKeySystemWhitelist(list);
+ }
+}
+#endif
+
void RenderFrameObserverEfl::DidChangeScrollOffset() {
if (render_frame()->GetMainRenderFrame() != render_frame()) {
return;
#ifndef RENDER_FRAME_OBSERVER_EFL_H_
#define RENDER_FRAME_OBSERVER_EFL_H_
+#include <string>
#include <vector>
#include "content/public/renderer/render_frame_observer.h"
void OnMoveToNextOrPreviousSelectElement(bool direction);
void OnRequestSelectCollectionInformation();
+#if BUILDFLAG(IS_TIZEN_TV)
+ void OnSetKeySystemWhitelist(const std::vector<std::string>& list);
+#endif
void OnGetPlainText(int callback_id);
gfx::Size max_scroll_offset_;
JAVASCRIPT_DIALOG_TYPE_ALERT, false, std::move(dialog_closed_callback));
}
}
+
+const std::string& WebContentsDelegateEfl::GetActiveDRM() {
+ return web_view_->GetActiveDRM();
+}
+
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+void WebContentsDelegateEfl::NotifyDRMInitData(
+ int init_type,
+ const std::vector<unsigned char>& data) {
+ web_view_->NotifyDRMInitData(init_type, data);
+}
+
+int WebContentsDelegateEfl::NotifyDRMInitComplete(unsigned int pssh_data_length,
+ unsigned char* pssh_data) {
+ return web_view_->NotifyDRMInitComplete(pssh_data_length, pssh_data);
+}
+
+void WebContentsDelegateEfl::NotifyDRMError(int error_code,
+ const std::string& error_string) {
+ return web_view_->NotifyDRMError(error_code, error_string);
+}
+#endif
#endif
#if BUILDFLAG(IS_TIZEN_TV)
int framerate) override;
void NotifyMediaDeviceConnectionChanged(int device_type) override;
int GetBroadcastDecoder() override;
+ const std::string& GetActiveDRM() override;
+#if defined(TIZEN_MULTIMEDIA_DRMMANAGER_SUPPORT)
+ void NotifyDRMInitData(int init_type,
+ const std::vector<unsigned char>& data) override;
+ int NotifyDRMInitComplete(unsigned int pssh_data_length,
+ unsigned char* pssh_data) override;
+ void NotifyDRMError(int error_code, const std::string& error_string) override;
+#endif
#endif
#if defined(TIZEN_AUTOFILL)