[MM] Handle buffering and playback 99/285999/4
authorVenugopal S M <sm.venugopal@samsung.com>
Fri, 23 Dec 2022 13:21:58 +0000 (18:51 +0530)
committerVenugopal Muruganathan <sm.venugopal@samsung.com>
Tue, 3 Jan 2023 07:23:36 +0000 (07:23 +0000)
- Add queue for handling the decoder buffers
- Handle EOS frame arriving before ESPP buffer gets full
- Handle pending play, buffer level update
- Release ESPP resources on EOS and error
- Pass "kNeedBuffer" from chunkdumxer to ESPP
- Use timebased buffering for playback on TV

Reference:
https://review.tizen.org/gerrit/274860
https://review.tizen.org/gerrit/275104
https://review.tizen.org/gerrit/274698 (partial)
https://review.tizen.org/gerrit/275420
https://review.tizen.org/gerrit/276113
https://review.tizen.org/gerrit/276374
https://review.tizen.org/gerrit/276419
https://review.tizen.org/gerrit/276487
https://review.tizen.org/gerrit/280222

Change-Id: I079096c9a82f502bf72803f9563c618375e21a3e
Signed-off-by: Venugopal S M <sm.venugopal@samsung.com>
15 files changed:
media/base/demuxer_stream.h
media/filters/chunk_demuxer.cc
media/mojo/clients/mojo_demuxer_stream_impl.cc
media/mojo/services/mojo_demuxer_stream_adapter.cc
third_party/blink/renderer/platform/media/web_media_player_impl.cc
tizen_src/chromium_impl/base/tizen/static_map.h [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc
tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h
tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h
tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.cc
tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.h
tizen_src/chromium_impl/media/filters/esplusplayer_util.cc
tizen_src/chromium_impl/media/filters/esplusplayer_util.h
tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc
tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h

index 2a5bda0..8f7e95a 100644 (file)
@@ -60,6 +60,9 @@ class MEDIA_EXPORT DemuxerStream {
     kOk,
     kAborted,
     kConfigChanged,
+#if defined(TIZEN_MULTIMEDIA)
+    kNeedBuffer,
+#endif
     kError,
     kStatusMax = kError,
   };
index 0bf2bf7..eaca4da 100644 (file)
@@ -404,7 +404,13 @@ void ChunkDemuxerStream::CompletePendingReadIfPossible_Locked() {
           // Return early without calling |read_cb_| since we don't have
           // any data to return yet.
           DVLOG(2) << __func__ << ": returning kNeedBuffer, type " << type_;
+#if defined(TIZEN_MULTIMEDIA)
+          status = DemuxerStream::kNeedBuffer;
+          buffer = nullptr;
+          break;
+#else
           return;
+#endif
         case SourceBufferStreamStatus::kEndOfStream:
           status = DemuxerStream::kOk;
           buffer = StreamParserBuffer::CreateEOSBuffer();
index 0bafd4f..d2b82fb 100644 (file)
@@ -85,6 +85,15 @@ void MojoDemuxerStreamImpl::OnBufferReady(ReadCallback callback,
     return;
   }
 
+#if defined(TIZEN_MULTIMEDIA)
+  if (status == Status::kNeedBuffer) {
+    LOG(INFO) << __func__ << ": kNeedBuffer!";
+    std::move(callback).Run(Status::kNeedBuffer, mojom::DecoderBufferPtr(),
+                            audio_config, video_config);
+    return;
+  }
+#endif
+
   if (status == Status::kAborted) {
     std::move(callback).Run(Status::kAborted, mojom::DecoderBufferPtr(),
                             audio_config, video_config);
index c5de682..dcd0d90 100644 (file)
@@ -106,6 +106,13 @@ void MojoDemuxerStreamAdapter::OnBufferReady(
     return;
   }
 
+#if defined(TIZEN_MULTIMEDIA)
+  if (status == kNeedBuffer) {
+    std::move(read_cb_).Run(kNeedBuffer, nullptr);
+    return;
+  }
+#endif
+
   DCHECK_EQ(status, kOk);
   mojo_decoder_buffer_reader_->ReadDecoderBuffer(
       std::move(buffer), base::BindOnce(&MojoDemuxerStreamAdapter::OnBufferRead,
index 9961397..e136260 100644 (file)
@@ -1354,7 +1354,11 @@ double WebMediaPlayerImpl::CurrentTime() const {
 
 bool WebMediaPlayerImpl::IsEnded() const {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
+#if defined(TIZEN_MULTIMEDIA)
+  return (ended_ || (CurrentTime() >= Duration()));
+#else
   return ended_;
+#endif
 }
 
 WebMediaPlayer::NetworkState WebMediaPlayerImpl::GetNetworkState() const {
diff --git a/tizen_src/chromium_impl/base/tizen/static_map.h b/tizen_src/chromium_impl/base/tizen/static_map.h
new file mode 100644 (file)
index 0000000..2985409
--- /dev/null
@@ -0,0 +1,286 @@
+// Copyright (c) 2022 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STATIC_MAP_H
+#define BASE_STATIC_MAP_H
+
+#include <type_traits>
+#include <unordered_map>
+
+// |StaticMap| is a map which resemebles std::unordered_map (ie. has no element
+// order) but can inline values for given keys for fast access. Because
+// everything is calculated at compile time this has a drawback of using only
+// types which match non-type template parameter for a key type.
+// This can be especially useful for enumeration types where you know you will
+// need to frequently access values for some predefined keys but need some sort
+// of fallback in other cases.
+//
+// Template of declaration of |StaticMap| should contain |KeyType| and
+// |ValueType| (duh) then a |StaticKeys| with template containing KeyType and
+// then values of keys storage you want to inline, then optionally a
+// |DynamicMapType| which will be used when key is not present in static part of
+// the map, this defaults to std::unordered_map. For |DynamicMapType| you can
+// also use |UseDefaultValue| which will return the same value (by const&) for
+// any key. Example declarations:
+//
+// StaticMap<SomeEnumT, size_t, StaticKeys<SomeEnumT, SomeEnumT::Important,
+//           SomeEnumT::EquallyImportant>>
+// - this defines a map from SomeEnumT to size_t, with SomeEnumT::Important and
+// SomeEnumT::EquallyImportant inlined. Both map values are default initialized
+// and as a fallback std::unordered_map<SomeEnumT, size_t> is used.
+// StaticMap<OtherEnumT, Clazz*, StaticKeys<OtherEnumT, OtherEnumT::Quick>,
+//           UseDefaultValue<Clazz*>>
+// - this map will map OtherEnumT to Clazz*, but only allows modification of
+// OtherEnumT::Quick key. Other return nullptr.
+//
+// You can access elements of the map by using operator[], which will behave
+// like normal operator[] when using std::unordered_map fallback; this means no
+// const version. To access inlined elements in const context you can use
+// get<key>(), which will check if key is indeed stored inlined.
+// The fun thing is, with |UseDefaultValue| you get const version of operator[]
+// so you can use it anywhere.
+// To check whether element is present use count().
+//
+// TODO: iterators and other map interfaces.
+
+namespace base {
+
+// Scroll down for |StaticMap| implementation here are utilities
+
+// |DoesTemplateContain| is a type trait (i.e. it defines static boolean member
+// |value|), checking, if the first value template argument is also among the
+// rest of the template parameter pack. This is used as a helper in
+// |IsTemplateSet| type trait to guarantee the static map keys are unique.
+template <class T, T... Tail>
+struct DoesTemplateContain;
+
+template <class T, T Value>
+struct DoesTemplateContain<T, Value> : std::false_type {};
+
+template <class T, T Value, T Head>
+struct DoesTemplateContain<T, Value, Head>
+    : std::integral_constant<bool, Value == Head> {};
+
+template <class T, T Value, T Head, T... Tail>
+struct DoesTemplateContain<T, Value, Head, Tail...>
+    : std::integral_constant<
+          bool,
+          DoesTemplateContain<T, Value, Head>::value ||
+              DoesTemplateContain<T, Value, Tail...>::value> {};
+
+static_assert(!DoesTemplateContain<int, 1>::value, "Test failed");
+static_assert(DoesTemplateContain<int, 1, 1>::value, "Test failed");
+static_assert(!DoesTemplateContain<int, 2, 1>::value, "Test failed");
+static_assert(DoesTemplateContain<int, 1, 1, 1, 1, 1, 1>::value, "Test failed");
+static_assert(DoesTemplateContain<int, 1, 2, 2, 2, 2, 1>::value, "Test failed");
+
+// |IsTemplateSet| is a type trait checking, if the value parameter pack set
+// consists of unique values, that is, it can be used as a set of unique keys in
+// static map. As such, it is used in |StaticKeys| class.
+template <class T, T... Tail>
+struct IsTemplateSet;
+
+template <class T>
+struct IsTemplateSet<T> : std::true_type {};
+
+template <class T, T Head, T... Tail>
+struct IsTemplateSet<T, Head, Tail...>
+    : std::integral_constant<bool,
+                             !DoesTemplateContain<T, Head, Tail...>::value &&
+                                 IsTemplateSet<T, Tail...>::value> {};
+
+static_assert(IsTemplateSet<int>::value, "Test failed");
+static_assert(IsTemplateSet<int, 2>::value, "Test failed");
+static_assert(IsTemplateSet<int, 2, 1>::value, "Test failed");
+static_assert(!IsTemplateSet<int, 1, 1>::value, "Test failed");
+static_assert(!IsTemplateSet<int, 1, 1, 1, 1, 1, 1>::value, "Test failed");
+static_assert(IsTemplateSet<int, 1, 2, 3, 4, 5, 6>::value, "Test failed");
+static_assert(!IsTemplateSet<int, 1, 2, 3, 4, 5, 1>::value, "Test failed");
+
+// Here is |StaticMap| implementation
+
+// |StaticKeys| are used for defining key values which storage should be
+// inlined. |StaticKeys| checks whether provided keys are not duplicated.
+template <class T, T... Values>
+struct StaticKeys {
+  static_assert(IsTemplateSet<T, Values...>::value, "Keys are duplicated!");
+  using KeyType = T;
+};
+
+template <class T>
+struct StaticKeys<T> {
+  // TODO: Make this fail, why use static map without static values...
+  //       Right now it will fail in DynamicMap specialization.
+  //   static_assert(!std::is_same<T,T>::value, "Static keys are empty");
+};
+
+template <class T>
+struct UseDefaultValue {
+  constexpr UseDefaultValue() : value() {}
+
+  template <class... Args>
+  explicit UseDefaultValue(Args&&... args)
+      : value(std::forward<Args>(args)...) {}
+
+  template <class KeyType>
+  const T& operator[](KeyType) const {
+    return value;
+  }
+
+ private:
+  const T value;
+};
+
+template <class K, class V>
+using DefaultDynamicMap = std::unordered_map<K, V>;
+
+// This is only to fail if StaticKeys were not used
+template <class KeyType,
+          class ValueType,
+          class StaticKeysT,
+          class DynamicMapType = DefaultDynamicMap<KeyType, ValueType>,
+          KeyType... keys>
+class StaticMap {
+  static_assert(std::is_same<StaticKeysT, StaticKeys<KeyType, keys...>>::value,
+                "You didn't use StaticKeys to pass compile time keys or the "
+                "type of it's values does not match KeyType");
+};
+
+// |DynamicMapType| version which uses this type for a fallback in case when key
+// is not inlined
+template <class KeyType, class ValueType, class DynamicMapType>
+class StaticMap<KeyType, ValueType, StaticKeys<KeyType>, DynamicMapType> {
+ public:
+  constexpr StaticMap() = default;
+  StaticMap(const StaticMap&) = default;
+  StaticMap(StaticMap&&) = default;
+  ~StaticMap() = default;
+
+  StaticMap& operator=(const StaticMap&) = default;
+  StaticMap& operator=(StaticMap&&) = default;
+
+  template <class... Args>
+  explicit StaticMap(Args&&... rest) : map_(std::forward<Args>(rest)...) {}
+
+  // This will should always fail, because DynamicMap won't provide keys at
+  // compile time
+  template <KeyType V>
+  ValueType& get() {
+    static_assert(!std::is_same<KeyType, decltype(V)>::value,
+                  "There is no such compile time Key!");
+  }
+
+  template <KeyType V>
+  constexpr const ValueType& get() const {
+    static_assert(!std::is_same<KeyType, decltype(V)>::value,
+                  "There is no such compile time Key!");
+  }
+
+  // Below functions will only exist if map_ defines matching operator[]
+  // This ugly monster is used to disable it for const DynamicMapType when this
+  // is not const
+  template <class U = DynamicMapType>
+  auto operator[](KeyType key) -> typename std::enable_if<
+      std::is_same<decltype(std::declval<U>()[key]), ValueType&>::value,
+      ValueType&>::type {
+    return map_[key];
+  }
+
+  template <class U = DynamicMapType>
+  auto operator[](KeyType key) const -> decltype(std::declval<const U>()[key]) {
+    static_assert(std::is_same<decltype(std::declval<const U>()[key]),
+                               const ValueType&>::value,
+                  "DynamicMapType::operator[] must return ValueType& or const "
+                  "ValueType&");
+    return map_[key];
+  }
+
+ private:
+  DynamicMapType map_;
+};
+
+// Contains storage for given inlined key value and otherwise delegates to map_,
+// which could be again this or |DynamicMapType| version.
+template <class KeyType,
+          class ValueType,
+          class DynamicMapType,
+          KeyType key_value_,
+          KeyType... rest_of_keys_>
+class StaticMap<KeyType,
+                ValueType,
+                StaticKeys<KeyType, key_value_, rest_of_keys_...>,
+                DynamicMapType> {
+  // Make sure keys set is ok
+  static const StaticKeys<KeyType, key_value_, rest_of_keys_...> static_keys;
+
+  using InnerMapType = StaticMap<KeyType,
+                                 ValueType,
+                                 StaticKeys<KeyType, rest_of_keys_...>,
+                                 DynamicMapType>;
+
+ public:
+  using size_type = size_t;
+
+  constexpr StaticMap() : value_() {}
+  StaticMap(const StaticMap&) = default;
+  StaticMap(StaticMap&&) = default;
+  ~StaticMap() = default;
+
+  StaticMap& operator=(const StaticMap&) = default;
+  StaticMap& operator=(StaticMap&&) = default;
+
+  template <class T, class... Args>
+  explicit StaticMap(T&& arg, Args&&... rest)
+      : value_(std::forward<T>(arg)), map_(std::forward<Args>(rest)...) {}
+
+  // Compile time conditional on key value
+  template <KeyType V>
+  auto get() -> typename std::enable_if<V == key_value_, ValueType&>::type {
+    return value_;
+  }
+
+  template <KeyType V>
+  auto get() -> typename std::enable_if<V != key_value_, ValueType&>::type {
+    return map_.template get<V>();
+  }
+
+  template <KeyType V>
+  constexpr auto get() const ->
+      typename std::enable_if<V == key_value_, const ValueType&>::type {
+    return value_;
+  }
+
+  template <KeyType V>
+  constexpr auto get() const ->
+      typename std::enable_if<V != key_value_, const ValueType&>::type {
+    return map_.template get<V>();
+  }
+
+  // Below functions will only exist if map_ defines matching operator[]
+  template <class U = InnerMapType>
+  auto operator[](KeyType key) -> decltype(std::declval<U>()[key]) {
+    if (key == key_value_)
+      return value_;
+    return map_[key];
+  }
+
+  template <class U = InnerMapType>
+  auto operator[](KeyType key) const -> decltype(std::declval<const U>()[key]) {
+    if (key == key_value_)
+      return value_;
+    return map_[key];
+  }
+
+  // This is to mimic std::unordered_map and std::map interfaces
+  size_type count(KeyType key) const {
+    return key == key_value_ ? 1 : map_.count();
+  }
+
+ private:
+  ValueType value_;
+  InnerMapType map_;
+};
+
+}  // namespace base
+#endif  // BASE_STATIC_MAP_H
index 5ff233d..8888c69 100644 (file)
@@ -26,9 +26,6 @@
 #include "tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h"
 
 namespace content {
-namespace {
-const float kDefaultVolume = 1.0;
-}
 
 class TizenRendererImpl::RendererClientInternal final
     : public media::RendererClient {
@@ -128,9 +125,7 @@ TizenRendererImpl::TizenRendererImpl(
     : state_(STATE_UNINITIALIZED),
       task_runner_(task_runner),
       sink_(sink),
-      volume_(1.0),
-      renderer_extension_receiver_(this),
-      playback_rate_(0.0) {
+      renderer_extension_receiver_(this) {
   // TODO(dalecurtis): Remove once experiments for http://crbug.com/470940 are
   // complete.
   int threshold_ms = 0;
@@ -147,11 +142,7 @@ TizenRendererImpl::~TizenRendererImpl() {
 
   weak_factory_.InvalidateWeakPtrs();
 
-  // TODO
-  if (init_cb_)
-    ;
-  else if (flush_cb_)
-    ;
+  media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Release();
   if (web_contents_observer_)
     web_contents_observer_->RemoveMediaPlayerRenderer(this);
 }
@@ -169,18 +160,16 @@ void TizenRendererImpl::Initialize(media::MediaResource* media_resource,
   init_cb_ = std::move(init_cb);
   media_resource_ = media_resource;
 
-  if (!media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Initialize(sink_)) {
+  if (!media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()
+           ->CreatePlayer()) {
     std::move(init_cb_).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
     return;
   }
 
-  std::move(init_cb_).Run(media::PIPELINE_OK);
-
   // TODO: return unsupported error for CDM.
-  state_ = STATE_INITIALIZING;
-  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetTaskRunner(
+  std::move(init_cb_).Run(media::PIPELINE_OK);
+  media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->SetTaskRunner(
       task_runner_.get());
-  SetStreamInfo();
 }
 
 void TizenRendererImpl::SetStreamInfo() {
@@ -188,7 +177,7 @@ void TizenRendererImpl::SetStreamInfo() {
   if (audio_stream_) {
     audio_renderer_client_ = std::make_unique<RendererClientInternal>(
         media::DemuxerStream::AUDIO, this);
-    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetStreamInfo(
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->SetStreamInfo(
         media::DemuxerStream::AUDIO, audio_stream_,
         audio_renderer_client_.get());
   }
@@ -197,17 +186,18 @@ void TizenRendererImpl::SetStreamInfo() {
   if (video_stream_) {
     video_renderer_client_ = std::make_unique<RendererClientInternal>(
         media::DemuxerStream::VIDEO, this);
-    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetStreamInfo(
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->SetStreamInfo(
         media::DemuxerStream::VIDEO, video_stream_,
         video_renderer_client_.get());
 #if defined(TIZEN_TBM_SUPPORT)
-    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetFrameAvailableCallback(
-        base::BindRepeating(&TizenRendererImpl::OnNewTbmFrameAvailable,
-                            base::Unretained(this)));
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()
+        ->SetFrameAvailableCallback(
+            base::BindRepeating(&TizenRendererImpl::OnNewTbmFrameAvailable,
+                                base::Unretained(this)));
 #else
-    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetFrameAvailableCallback(
-        base::BindRepeating(&TizenRendererImpl::OnNewFrameAvailable,
-                            base::Unretained(this)));
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()
+        ->SetFrameAvailableCallback(base::BindRepeating(
+            &TizenRendererImpl::OnNewFrameAvailable, base::Unretained(this)));
 #endif
   }
 }
@@ -261,13 +251,14 @@ void TizenRendererImpl::Flush(base::OnceClosure flush_cb) {
 
   flush_cb_ = std::move(flush_cb);
   state_ = STATE_FLUSHING;
-  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Flush(std::move(flush_cb));
+  media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Flush(
+      std::move(flush_cb));
 }
 
 void TizenRendererImpl::Seek(base::TimeDelta time) {
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
             << " time : " << time.InMicroseconds();
-  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Seek(time);
+  media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Seek(time);
 }
 
 void TizenRendererImpl::StartPlayingFrom(base::TimeDelta time) {
@@ -277,14 +268,26 @@ void TizenRendererImpl::StartPlayingFrom(base::TimeDelta time) {
   TRACE_EVENT1("media", "TizenRendererImpl::StartPlayingFrom", "time_us",
                time.InMicroseconds());
 
+  if (!media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()
+           ->IsInitialized())
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Initialize(
+        sink_);
+
+  if (!media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()
+           ->IsPrepared()) {
+    SetStreamInfo();
+    SetPlayerVolume();
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Prepare();
+  }
+
+  state_ = STATE_INITIALIZING;
+  media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Seek(time);
   state_ = STATE_PLAYING;
-  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Prepare();
-  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Seek(time);
 }
 
 void TizenRendererImpl::SetPlaybackRate(double playback_rate) {
-  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << "("
-            << playback_rate << ")";
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " "
+            << playback_rate << "/ " << playback_rate_;
   DCHECK(task_runner_->BelongsToCurrentThread());
   TRACE_EVENT1("media", "TizenRendererImpl::SetPlaybackRate", "rate",
                playback_rate);
@@ -295,12 +298,12 @@ void TizenRendererImpl::SetPlaybackRate(double playback_rate) {
   playback_rate_ = playback_rate;
 
   // TODO: Random error is observed on TM1.
-  // MediaPlayerESPlusPlayer::GetEsppPlayer()->SetRate(playback_rate_);
+  // MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->SetRate(playback_rate_);
 
   if (playback_rate > 0)
-    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Play();
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Play();
   else
-    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Pause();
+    media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->Pause();
 }
 
 void TizenRendererImpl::SetVolume(float volume) {
@@ -310,11 +313,17 @@ void TizenRendererImpl::SetVolume(float volume) {
     return;
 
   volume_ = volume;
-  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetVolume(volume);
+  SetPlayerVolume();
+}
+
+void TizenRendererImpl::SetPlayerVolume() const {
+  media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()->SetVolume(
+      volume_);
 }
 
 base::TimeDelta TizenRendererImpl::GetMediaTime() {
-  return media::MediaPlayerESPlusPlayer::GetEsppPlayer()->GetCurrentTime();
+  return media::MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer()
+      ->GetCurrentTime();
 }
 
 void TizenRendererImpl::OnSelectedVideoTracksChanged(
@@ -330,10 +339,12 @@ void TizenRendererImpl::OnEnabledAudioTracksChanged(
 }
 
 void TizenRendererImpl::OnRendererEnded() {
+  playback_rate_ = 0.0;
   client_->OnEnded();
 }
 
 void TizenRendererImpl::OnError(media::PipelineStatus error) {
+  playback_rate_ = 0.0;
   state_ = STATE_ERROR;
   client_->OnError(error);
 }
index 4c4078f..6c26839 100644 (file)
@@ -95,6 +95,7 @@ class CONTENT_EXPORT TizenRendererImpl
 
  private:
   class RendererClientInternal;
+  const float kDefaultVolume = 1.0;
 
   enum State {
     STATE_UNINITIALIZED,
@@ -107,6 +108,7 @@ class CONTENT_EXPORT TizenRendererImpl
   };
 
   void SetStreamInfo();
+  void SetPlayerVolume() const;
   void OnRendererEnded();
   void OnError(media::PipelineStatus error);
   void OnStatisticsUpdate(const media::PipelineStatistics& stats);
@@ -145,8 +147,8 @@ class CONTENT_EXPORT TizenRendererImpl
   media::DemuxerStream* audio_stream_;
   media::DemuxerStream* video_stream_;
 
-  double volume_ = 0.0;
-  double playback_rate_ = 0;
+  double volume_ = kDefaultVolume;
+  double playback_rate_ = 0.0;
 
   // The time to start playback from after starting/seeking has completed.
   base::TimeDelta start_time_;
@@ -155,8 +157,8 @@ class CONTENT_EXPORT TizenRendererImpl
   media::BufferingState video_buffering_state_;
 
   // Whether we've received the audio/video ended events.
-  bool media_ended_;
-  bool web_contents_muted_;
+  bool media_ended_ = false;
+  bool web_contents_muted_ = false;
   mojo::Receiver<MediaPlayerRendererExtension> renderer_extension_receiver_;
   raw_ptr<content::MediaPlayerRendererWebContentsObserver>
       web_contents_observer_;
index d8fa257..296c17f 100644 (file)
@@ -51,6 +51,7 @@ class BufferObserver {
   virtual void SetEos(DemuxerStream::Type) = 0;
   virtual void UpdateBufferedSize(DemuxerStream::Type, espp_buffer_size_t) = 0;
   virtual void ResetBufferStatus() = 0;
+  virtual void ResetBufferStatusCallbacks(esplusplayer_handle) = 0;
   virtual int SetMediaStreamStatusCallback(
       esplusplayer_handle,
       esplusplayer_stream_type stream_type) = 0;
index cc04f52..6ccb9e4 100644 (file)
@@ -82,7 +82,13 @@ void BufferObserverImpl::UpdateBufferedSize(DemuxerStream::Type type,
               << GetBuffer(DemuxerStream::VIDEO).buffer_percent << "%"
               << "\tAUDIO: " << GetBuffer(DemuxerStream::AUDIO).buffer_percent
               << "%";
-
+#if defined(OS_TIZEN_TV_PRODUCT)
+  // The buffer is controlled in time domain, however we want to stop
+  // pushing data in case of size overflow as well
+  buffer.has_size_overflow = (buffer_status == kBufferOverflow);
+  if (buffer.has_size_overflow == false)
+    return;
+#endif
   CallbackIfNeed(type, buffer_status);
 }
 
@@ -207,6 +213,13 @@ bool BufferObserverImpl::EnsureSimilarBufferedDuration() {
   }
 }
 
+void BufferObserverImpl::ResetBufferStatusCallbacks(
+    esplusplayer_handle player) {
+  esplusplayer_set_buffer_byte_status_cb(player, nullptr, this);
+  esplusplayer_set_buffer_time_status_cb(player, nullptr, this);
+  buffering_callback_.Reset();
+}
+
 int BufferObserverImpl::SetBufferStatusCallbacks(
     esplusplayer_handle player,
     esplusplayer_stream_type type,
@@ -250,7 +263,7 @@ int BufferObserverImpl::SetBufferStatusCallbacks(
   }
 
   // FIXME: TM1 is giving wrong time size for pipeline.
-#if 0
+#if defined(OS_TIZEN_TV_PRODUCT)
   if ((ret = esplusplayer_set_buffer_time_status_cb(
            player, callbacks.time_status_cb, this)) !=
       ESPLUSPLAYER_ERROR_TYPE_NONE) {
index bfae69e..ac1f6b0 100644 (file)
@@ -35,6 +35,7 @@ class BufferObserverImpl : public BufferObserver {
 
   // |handler| will be invoked from player's thread.
   void SetBufferingCallback(const BufferingCallback&) final;
+  void ResetBufferStatusCallbacks(esplusplayer_handle player) final;
   void SetEos(DemuxerStream::Type) final;
   void UpdateBufferedSize(DemuxerStream::Type, espp_buffer_size_t) final;
   void ResetBufferStatus() final;
index 1eab5b8..531e06f 100644 (file)
@@ -176,4 +176,21 @@ PipelineStatus GetPipelineError(const esplusplayer_error_type error) {
   return PIPELINE_ERROR_DECODE;
 }
 
+gfx::Size GetMaxCodecResolution(esplusplayer_video_mime_type mime_type) {
+  switch (mime_type) {
+    case ESPLUSPLAYER_VIDEO_MIME_TYPE_AV1:
+      return {k8KVideoMaxWidth, k8KVideoMaxHeight};
+    case ESPLUSPLAYER_VIDEO_MIME_TYPE_VP8:
+      return {kFHDVideoMaxWidth, kFHDVideoMaxHeight};
+    case ESPLUSPLAYER_VIDEO_MIME_TYPE_VP9:
+    case ESPLUSPLAYER_VIDEO_MIME_TYPE_HEVC:
+      return {k8KVideoMaxWidth, k8KVideoMaxHeight};
+    case ESPLUSPLAYER_VIDEO_MIME_TYPE_H264:
+      return {kFHDVideoMaxWidth, kFHDVideoMaxHeight};
+    default:
+      // for all kind of codecs.
+      return {kFHDVideoMaxWidth, kFHDVideoMaxHeight};
+  }
+}
+
 }  // namespace media
index a89992e..aba62d7 100644 (file)
@@ -11,6 +11,7 @@
 #include "media/base/pipeline_status.h"
 #include "media/base/video_codecs.h"
 #include "tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace media {
 
@@ -22,6 +23,8 @@ const int kFHDVideoMaxWidth = 1920;
 const int kFHDVideoMaxHeight = 1080;
 const int k4KVideoMaxWidth = 3840;
 const int k4KVideoMaxHeight = 2160;
+const int k8KVideoMaxWidth = 7680;
+const int k8KVideoMaxHeight = 4320;
 const int kMaxFramerate = 60;
 const int kBuffingLimit = 5;  // Seconds
 
@@ -42,6 +45,8 @@ esplusplayer_stream_type GetESPlusPlayerStreamType(DemuxerStream::Type type);
 
 PipelineStatus GetPipelineError(const esplusplayer_error_type error);
 
+gfx::Size GetMaxCodecResolution(esplusplayer_video_mime_type mime_type);
+
 }  // namespace media
 
 #endif  // MEDIA_FILTERS_ESPP_PLAYER_UTIL_H_
index f50d4c4..ea6f04e 100644 (file)
 #include "third_party/libyuv/include/libyuv/convert.h"
 
 namespace media {
+using player_buffer_size_t = unsigned long long;
+
+// Limit of platform player's total (audio and video) buffer size in bytes
+const media::player_buffer_size_t kPlayerTotalBufferSize = 64 * 1024 * 1024;
+// Limit of platform player's audio buffer in bytes
+const media::player_buffer_size_t kPlayerAudioBufferSize = 768 * 1024;
 
 espp_buffer_size_t GetMaxAudioBufferSize() {
-  // Assume MPEG-1 Audio Layer III 320kbit/s audio.
-  return 320 * 1000 / 8 * kBuffingLimit;
+  return kPlayerAudioBufferSize;
 }
 
 espp_buffer_size_t GetMaxVideoBufferSize(gfx::Size size) {
-  const int fps = 60;  // Asume max.
-
-  // Reference of buffer size estimation:
-  // http://stackoverflow.com/questions/5024114/suggested-compression-ratio-with-h-264
-  return static_cast<espp_buffer_size_t>(size.width()) * size.height() * fps *
-         2 * 7 / 100 / 8 * kBuffingLimit;
+  return kPlayerTotalBufferSize - kPlayerAudioBufferSize;
 }
 
 void ReadyToPrepareCallback(const esplusplayer_stream_type stream_type,
@@ -117,7 +117,7 @@ void ErrorCallback(const esplusplayer_error_type error_type, void* user_data) {
 }
 
 // static
-MediaPlayerESPlusPlayer* MediaPlayerESPlusPlayer::GetEsppPlayer() {
+MediaPlayerESPlusPlayer* MediaPlayerESPlusPlayer::GetMediaPlayerESPlusPlayer() {
   return base::Singleton<MediaPlayerESPlusPlayer>::get();
 }
 
@@ -133,19 +133,29 @@ MediaPlayerESPlusPlayer::~MediaPlayerESPlusPlayer() {
   esplayer_ = nullptr;
 }
 
-bool MediaPlayerESPlusPlayer::Initialize(VideoRendererSink* sink) {
-  if (esplayer_) {
-    LOG(INFO) << "esplus player exists!";
-    sink_ = sink;
+bool MediaPlayerESPlusPlayer::CreatePlayer() {
+  if (esplayer_)
     return true;
-  }
 
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
   esplayer_ = esplusplayer_create();
   if (!esplayer_) {
     LOG(ERROR) << "Cannot create esplus player!";
     return false;
   }
 
+  last_frames_.get<DemuxerStream::AUDIO>().first = media::kNoTimestamp;
+  last_frames_.get<DemuxerStream::AUDIO>().second = media::kNoTimestamp;
+  last_frames_.get<DemuxerStream::VIDEO>().first = media::kNoTimestamp;
+  last_frames_.get<DemuxerStream::VIDEO>().second = media::kNoTimestamp;
+
+  buffer_observer_.reset(BufferObserver::CreateBufferObserver());
+
+  return true;
+}
+
+void MediaPlayerESPlusPlayer::Initialize(VideoRendererSink* sink) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
   esplusplayer_set_ready_to_prepare_cb(esplayer_, &ReadyToPrepareCallback,
                                        this);
   esplusplayer_set_prepare_async_done_cb(esplayer_, &PrepareCompleteCallback,
@@ -159,32 +169,38 @@ bool MediaPlayerESPlusPlayer::Initialize(VideoRendererSink* sink) {
   esplusplayer_set_resource_conflicted_cb(esplayer_, &ResourceConflictCallback,
                                           this);
   esplusplayer_set_error_cb(esplayer_, &ErrorCallback, this);
+  buffer_observer_->SetBufferingCallback(
+      base::BindRepeating(&MediaPlayerESPlusPlayer::OnBufferingStatusChanged,
+                          weak_factory_.GetWeakPtr()));
 
   int error = esplusplayer_open(esplayer_);
   if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
     LOG(ERROR) << "esplusplayer_open failed. error #"
                << esplusplayer_get_error_string(
                       static_cast<esplusplayer_error_type>(error));
-    return false;
+    return;
   }
 
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
             << " state:" << GetString(GetPlayerState());
-  error = esplusplayer_set_video_frame_buffer_type(
-      esplayer_, ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY);
+
+  esplusplayer_decoded_video_frame_buffer_type video_frame_buffer_type =
+      ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY;
+
+  error = esplusplayer_set_video_frame_buffer_type(esplayer_,
+                                                   video_frame_buffer_type);
   if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
     LOG(ERROR) << "esplusplayer_set_video_frame_buffer_type failed. error #"
                << esplusplayer_get_error_string(
                       static_cast<esplusplayer_error_type>(error));
-    return false;
+    return;
   }
 
   sink_ = sink;
-  buffer_observer_.reset(BufferObserver::CreateBufferObserver());
-  buffer_observer_->SetBufferingCallback(
-      base::BindRepeating(&MediaPlayerESPlusPlayer::OnBufferingStatusChanged,
-                          weak_factory_.GetWeakPtr()));
-  return true;
+}
+
+bool MediaPlayerESPlusPlayer::IsInitialized() {
+  return (GetPlayerState() >= ESPLUSPLAYER_STATE_IDLE);
 }
 
 void MediaPlayerESPlusPlayer::SetTaskRunner(
@@ -207,6 +223,10 @@ void MediaPlayerESPlusPlayer::SetStreamInfo(DemuxerStream::Type type,
 }
 
 void MediaPlayerESPlusPlayer::Prepare() {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  if (is_prepared_ || is_preparing_)
+    return;
+
   if (GetPlayerState() != ESPLUSPLAYER_STATE_IDLE) {
     LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
                << " Prepare called on invalid state : "
@@ -228,11 +248,72 @@ void MediaPlayerESPlusPlayer::Prepare() {
           ->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
     return;
   }
+  is_preparing_ = true;
+}
+
+bool MediaPlayerESPlusPlayer::IsPrepared() {
+  return (GetPlayerState() >= ESPLUSPLAYER_STATE_READY);
+}
+
+void MediaPlayerESPlusPlayer::Release() {
+  if (!esplayer_)
+    return;
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  SetIsEos(DemuxerStream::AUDIO, false);
+  SetShouldFeed(DemuxerStream::AUDIO, false);
+  SetIsValid(DemuxerStream::AUDIO, false);
+  SetReadRequested(DemuxerStream::AUDIO, false);
+
+  SetIsEos(DemuxerStream::VIDEO, false);
+  SetShouldFeed(DemuxerStream::VIDEO, false);
+  SetIsValid(DemuxerStream::VIDEO, false);
+  SetReadRequested(DemuxerStream::VIDEO, false);
+
+  esplusplayer_set_ready_to_prepare_cb(esplayer_, nullptr, this);
+  esplusplayer_set_prepare_async_done_cb(esplayer_, nullptr, this);
+  esplusplayer_set_eos_cb(esplayer_, nullptr, this);
+  esplusplayer_set_media_packet_video_decoded_cb(esplayer_, nullptr, this);
+  esplusplayer_set_flush_done_cb(esplayer_, nullptr, this);
+  esplusplayer_set_ready_to_seek_cb(esplayer_, nullptr, this);
+  esplusplayer_set_seek_done_cb(esplayer_, nullptr, this);
+  esplusplayer_set_resource_conflicted_cb(esplayer_, nullptr, this);
+  esplusplayer_set_error_cb(esplayer_, nullptr, this);
 
   buffer_observer_->ResetBufferStatus();
+  buffer_observer_->ResetBufferStatusCallbacks(esplayer_);
+
+  volume_ = 1.0;
+  playback_rate_ = 0.0;
+  is_prepared_ = false;
+  is_preparing_ = false;
+  is_paused_ = true;
+  is_buffering_ = false;
+  current_position_ = base::TimeDelta();
+  is_flushing_ = false;
+  is_seeking_ = false;
+  seek_position_ = base::TimeDelta();
+  pending_seek_ = false;
+  pending_seek_position_ = base::TimeDelta();
+  should_set_playback_rate_ = false;
+
+  GetBufferQueue(DemuxerStream::VIDEO).clear();
+  GetBufferQueue(DemuxerStream::AUDIO).clear();
+  data_cb_.Reset();
+  sink_ = nullptr;
+
+  if (GetPlayerState() == ESPLUSPLAYER_STATE_NONE) {
+    LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
+               << " Release called on invalid state : "
+               << GetString(GetPlayerState());
+    return;
+  }
+
+  esplusplayer_close(esplayer_);
 }
 
 void MediaPlayerESPlusPlayer::Play() {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
   int error = ESPLUSPLAYER_ERROR_TYPE_NONE;
   esplusplayer_state state = GetPlayerState();
 
@@ -272,6 +353,12 @@ void MediaPlayerESPlusPlayer::Play() {
 }
 
 void MediaPlayerESPlusPlayer::Pause(bool is_media_related_action) {
+  if (is_paused_) {
+    LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+              << " already paused";
+    return;
+  }
+
   if (!is_media_related_action)
     is_paused_ = true;
 
@@ -295,6 +382,8 @@ void MediaPlayerESPlusPlayer::Pause(bool is_media_related_action) {
 }
 
 void MediaPlayerESPlusPlayer::SetRate(double rate) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " : "
+            << rate;
   if (playback_rate_ == rate)
     return;
 
@@ -317,9 +406,14 @@ void MediaPlayerESPlusPlayer::SetRate(double rate) {
 }
 
 void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time) {
-  if (GetCurrentTime() == time)
+  // Ignore seek to 0 during initialization.
+  if (GetPlayerState() < ESPLUSPLAYER_STATE_READY && time.InSecondsF() == 00) {
+    LOG(INFO) << __func__ << " Ignore seek to 0 during initialization.";
     return;
+  }
 
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " :"
+            << time;
   if (GetPlayerState() < ESPLUSPLAYER_STATE_READY || !is_prepared_ ||
       is_seeking_) {
     LOG(INFO) << "Add to pending seek ("
@@ -331,6 +425,17 @@ void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time) {
     return;
   }
 
+  last_frames_.get<DemuxerStream::AUDIO>().first = media::kNoTimestamp;
+  last_frames_.get<DemuxerStream::VIDEO>().first = media::kNoTimestamp;
+
+  UpdateBufferedDtsDifference();
+
+  SetShouldFeed(DemuxerStream::AUDIO, false);
+  SetShouldFeed(DemuxerStream::VIDEO, false);
+  GetBufferQueue(DemuxerStream::VIDEO).clear();
+  GetBufferQueue(DemuxerStream::AUDIO).clear();
+  buffer_observer_->ResetBufferStatus();
+
   LOG(INFO) << __func__ << " : " << time;
   int error = esplusplayer_seek(esplayer_, time.InMilliseconds());
   if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
@@ -342,17 +447,25 @@ void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time) {
   }
 
   is_seeking_ = true;
-  pending_seek_ = false;
   seek_position_ = time;
+  pending_seek_ = false;
   pending_seek_position_ = base::TimeDelta();
 }
 
 void MediaPlayerESPlusPlayer::Flush(base::OnceClosure flush_cb) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  last_frames_.get<DemuxerStream::AUDIO>().first = media::kNoTimestamp;
+  last_frames_.get<DemuxerStream::VIDEO>().first = media::kNoTimestamp;
+
+  UpdateBufferedDtsDifference();
+
   NOTIMPLEMENTED();
   std::move(flush_cb).Run();
 }
 
 void MediaPlayerESPlusPlayer::SetVolume(double volume) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " :"
+            << volume;
   volume_ = volume;
   if (GetPlayerState() == ESPLUSPLAYER_STATE_NONE)
     return;
@@ -365,12 +478,16 @@ void MediaPlayerESPlusPlayer::SetVolume(double volume) {
 }
 
 base::TimeDelta MediaPlayerESPlusPlayer::GetCurrentTime() {
-  if (GetPlayerState() < ESPLUSPLAYER_STATE_PLAYING)
-    return base::TimeDelta();
+  if (pending_seek_)
+    return pending_seek_position_;
 
   if (is_seeking_)
     return seek_position_;
 
+  // Seek can be called before PLAY / PAUSE. Return last known time.
+  if (GetPlayerState() < ESPLUSPLAYER_STATE_PLAYING)
+    return current_position_;
+
   uint64_t time = 0;  // In milliseconds.
   int error = esplusplayer_get_playing_time(esplayer_, &time);
   if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
@@ -380,14 +497,17 @@ base::TimeDelta MediaPlayerESPlusPlayer::GetCurrentTime() {
     return base::TimeDelta();
   }
 
-  return base::Milliseconds(time);
+  current_position_ = base::Milliseconds(time);
+  return current_position_;
 }
 
 esplusplayer_state MediaPlayerESPlusPlayer::GetPlayerState() {
-  return esplusplayer_get_state(esplayer_);
+  return (esplayer_ ? esplusplayer_get_state(esplayer_)
+                    : ESPLUSPLAYER_STATE_NONE);
 }
 
 void MediaPlayerESPlusPlayer::InitializeStreamConfig(DemuxerStream::Type type) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
   DemuxerStream* stream = GetDemuxerStream(type);
   CHECK(stream);
 
@@ -418,20 +538,22 @@ void MediaPlayerESPlusPlayer::InitializeStreamConfig(DemuxerStream::Type type) {
       audio_stream_info.codec_data = NULL;
     }
 
-    int error =
-        esplusplayer_set_audio_stream_info(esplayer_, &audio_stream_info);
-    if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
-      LOG(ERROR) << "esplusplayer_set_audio_stream_info failed. error code "
-                 << error;
-      GetRendererClient(type)->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
-      return;
+    int error;
+    if (!IsValid(type)) {
+      error = esplusplayer_set_audio_stream_info(esplayer_, &audio_stream_info);
+      if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+        LOG(ERROR) << "esplusplayer_set_audio_stream_info failed. error code "
+                   << error;
+        GetRendererClient(type)->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+        return;
+      }
+      buffer_observer_->SetMediaStreamStatusCallback(
+          esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO);
     }
 
     espp_buffer_size_t max_buffer_size = GetMaxAudioBufferSize();
-    buffer_observer_->SetMediaStreamStatusCallback(
-        esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO);
     buffer_observer_->SetBufferSize(type, max_buffer_size);
-
+    LOG(INFO) << "Audio Max buffer size " << max_buffer_size;
     error = esplusplayer_set_buffer_size(
         esplayer_, ESPLUSPLAYER_BUFFER_AUDIO_MAX_BYTE_SIZE, max_buffer_size);
     if (error != ESPLUSPLAYER_ERROR_TYPE_NONE)
@@ -468,25 +590,29 @@ void MediaPlayerESPlusPlayer::InitializeStreamConfig(DemuxerStream::Type type) {
     } else {
       video_stream_info.codec_data = NULL;
     }
-
-    video_stream_info.max_width = k4KVideoMaxWidth;
-    video_stream_info.max_height = k4KVideoMaxHeight;
-
-    int error =
-        esplusplayer_set_video_stream_info(esplayer_, &video_stream_info);
-    if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
-      LOG(ERROR) << "esplusplayer_set_video_stream_info failed. error code "
-                 << error;
-      GetRendererClient(type)->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
-      return;
+    auto max_resolution = GetMaxCodecResolution(video_stream_info.mime_type);
+
+    video_stream_info.max_width = max_resolution.width();
+    video_stream_info.max_height = max_resolution.height();
+
+    int error;
+    if (!IsValid(type)) {
+      error = esplusplayer_set_video_stream_info(esplayer_, &video_stream_info);
+      if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+        LOG(ERROR) << "esplusplayer_set_video_stream_info failed. error code "
+                   << error;
+        GetRendererClient(type)->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+        return;
+      }
+      buffer_observer_->SetMediaStreamStatusCallback(
+          esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO);
     }
 
     espp_buffer_size_t max_buffer_size =
         GetMaxVideoBufferSize(video_config.coded_size());
-    buffer_observer_->SetMediaStreamStatusCallback(
-        esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO);
     buffer_observer_->SetBufferSize(type, max_buffer_size);
 
+    LOG(INFO) << "Video Max buffer size " << max_buffer_size;
     error = esplusplayer_set_buffer_size(
         esplayer_, ESPLUSPLAYER_BUFFER_VIDEO_MAX_BYTE_SIZE, max_buffer_size);
     if (error != ESPLUSPLAYER_ERROR_TYPE_NONE)
@@ -499,7 +625,10 @@ void MediaPlayerESPlusPlayer::InitializeStreamConfig(DemuxerStream::Type type) {
 }
 
 void MediaPlayerESPlusPlayer::ReadBuffer(DemuxerStream::Type type) {
-  // TODO: Check and read from Queue.
+  if (!ReadFromBufferQueue(type))
+    return;
+
+  // Avoid unnecessary or redundant read requests.
   if (!ShouldFeed(type) || ReadRequested(type))
     return;
 
@@ -518,18 +647,15 @@ void MediaPlayerESPlusPlayer::OnBufferReady(
     case DemuxerStream::kError:
       GetBufferQueue(type).clear();
       break;
+    case DemuxerStream::kNeedBuffer:
+      break;
     case DemuxerStream::kConfigChanged:
       // Clear pending buffer of older config
       GetBufferQueue(type).clear();
       InitializeStreamConfig(type);
       break;
     case DemuxerStream::kOk: {
-      if (buffer.get()->end_of_stream()) {
-        SubmitEosPacket(type);
-        return;
-      }
-
-      SubmitEsPacket(type, std::move(buffer));
+      GetBufferQueue(type).push_back(buffer);
       break;
     }
   }
@@ -538,23 +664,48 @@ void MediaPlayerESPlusPlayer::OnBufferReady(
   ReadBuffer(type);
 }
 
-void MediaPlayerESPlusPlayer::SubmitEosPacket(DemuxerStream::Type type) {
+bool MediaPlayerESPlusPlayer::ReadFromBufferQueue(DemuxerStream::Type type) {
+  if (!GetBufferQueue(type).size())
+    return true;
+
+  esplusplayer_submit_status status = ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS;
+  scoped_refptr<DecoderBuffer> buffer = GetBufferQueue(type).front();
+  if (buffer.get()->end_of_stream())
+    status = SubmitEosPacket(type);
+  else
+    status = SubmitEsPacket(type, buffer);
+
+  if (status == ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED ||
+      status == ESPLUSPLAYER_SUBMIT_STATUS_OUT_OF_MEMORY ||
+      status == ESPLUSPLAYER_SUBMIT_STATUS_FULL) {
+    return false;
+  }
+
+  UpdateBufferedDtsDifference();
+  GetBufferQueue(type).pop_front();
+  return true;
+}
+
+esplusplayer_submit_status MediaPlayerESPlusPlayer::SubmitEosPacket(
+    DemuxerStream::Type type) {
   if (IsEos(type))
-    return;
+    return ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS;
 
-  esplusplayer_submit_status ret = esplusplayer_submit_eos_packet(
+  last_frames_[type].first = base::TimeDelta::Max();
+
+  esplusplayer_submit_status status = esplusplayer_submit_eos_packet(
       esplayer_, GetESPlusPlayerStreamType(type));
-  if (ret != ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS) {
+  if (status != ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS) {
     LOG(ERROR) << "Submit Eos (" << DemuxerStream::GetTypeName(type)
-               << ") Packet failed, ret:" << ret;
-    return;
+               << ") Packet failed, ret:" << GetString(status);
+    return status;
   }
-
   buffer_observer_->SetEos(type);
   SetIsEos(type, true);
+  return status;
 }
 
-void MediaPlayerESPlusPlayer::SubmitEsPacket(
+esplusplayer_submit_status MediaPlayerESPlusPlayer::SubmitEsPacket(
     DemuxerStream::Type type,
     scoped_refptr<DecoderBuffer> buffer) {
   esplusplayer_es_packet packet;
@@ -568,17 +719,29 @@ void MediaPlayerESPlusPlayer::SubmitEsPacket(
 
   // This filed only set when PushMediaPacket
   packet.matroska_color_info = nullptr;
-  esplusplayer_submit_status ret =
+  esplusplayer_submit_status status =
       esplusplayer_submit_packet(esplayer_, &packet);
-  if (ret != ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS) {
-    if (ret == ESPLUSPLAYER_SUBMIT_STATUS_FULL ||
-        ret == ESPLUSPLAYER_SUBMIT_STATUS_OUT_OF_MEMORY ||
-        ret == ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED) {
-      GetBufferQueue(type).push_back(buffer);
-    }
-
+  if (status != ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS) {
     LOG(WARNING) << "submit " << DemuxerStream::GetTypeName(type)
-                 << " packet : " << GetString(ret);
+                 << " packet : " << GetString(status);
+  }
+
+  last_frames_[type].first = base::Milliseconds(packet.pts);
+  last_frames_[type].second = base::Milliseconds(packet.duration);
+  return status;
+}
+
+void MediaPlayerESPlusPlayer::UpdateBufferedDtsDifference() {
+  const auto& audio_ts = last_frames_.get<DemuxerStream::AUDIO>().first;
+  const auto& video_ts = last_frames_.get<DemuxerStream::VIDEO>().first;
+
+  if ((audio_ts != media::kNoTimestamp) && (video_ts != media::kNoTimestamp) &&
+      (audio_ts != base::TimeDelta::Max()) &&
+      (video_ts != base::TimeDelta::Max())) {
+    const auto av_diff = (audio_ts - video_ts).InMilliseconds();
+    buffer_observer_->SetAudioVideoDtsDifference(av_diff);
+  } else {
+    buffer_observer_->SetAudioVideoDtsDifference(0);
   }
 }
 
@@ -611,7 +774,6 @@ void MediaPlayerESPlusPlayer::OnBufferingStatusChanged(DemuxerStream::Type type,
       SetShouldFeed(type, true);
       ReadBuffer(type);
       break;
-
     case kBufferMaxThreshold:
     case kBufferOverflow:
     case kBufferEos:
@@ -632,7 +794,7 @@ void MediaPlayerESPlusPlayer::OnBufferingStatusChanged(DemuxerStream::Type type,
     GetRendererClient(type)->OnBufferingStateChange(BUFFERING_HAVE_NOTHING,
                                                     DEMUXER_UNDERFLOW);
   } else if (status == kBufferMaxThreshold || status == kBufferOverflow ||
-             status == kBufferAhead) {
+             status == kBufferAhead || status == kBufferEos) {
     GetRendererClient(type)->OnBufferingStateChange(
         BUFFERING_HAVE_ENOUGH, BUFFERING_CHANGE_REASON_UNKNOWN);
   }
@@ -686,6 +848,7 @@ void MediaPlayerESPlusPlayer::OnPrepareComplete(bool result) {
   }
 
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << " is_paused_ : " << is_paused_
             << " seek_position : " << seek_position_
             << " pending_seek_ : " << pending_seek_
             << " pending_seek_position_ : " << pending_seek_position_;
@@ -693,7 +856,8 @@ void MediaPlayerESPlusPlayer::OnPrepareComplete(bool result) {
   is_prepared_ = true;
   if (pending_seek_) {
     Seek(pending_seek_position_);
-    pending_seek_position_ = base::TimeDelta();
+  } else if (!is_paused_) {
+    Play();
   }
 }
 
@@ -710,6 +874,7 @@ void MediaPlayerESPlusPlayer::OnEos() {
     GetRendererClient(DemuxerStream::VIDEO)->OnEnded();
   else if (IsValid(DemuxerStream::AUDIO))
     GetRendererClient(DemuxerStream::AUDIO)->OnEnded();
+  Release();
 }
 
 void MediaPlayerESPlusPlayer::OnFrameReady(
@@ -845,7 +1010,7 @@ void MediaPlayerESPlusPlayer::OnReadyToSeek(
             << " : " << seek_time;
 
   SetShouldFeed(GetDemuxerStreamType(stream_type), true);
-  SetReadRequested(GetDemuxerStreamType(stream_type), false);
+  SetIsEos(GetDemuxerStreamType(stream_type), false);
   ReadBuffer(GetDemuxerStreamType(stream_type));
 }
 
@@ -859,7 +1024,10 @@ void MediaPlayerESPlusPlayer::OnSeekComplete() {
 
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
             << " is_paused:" << is_paused_;
+
+  current_position_ = seek_position_;
   is_seeking_ = false;
+  seek_position_ = base::TimeDelta();
   if (pending_seek_) {
     Seek(pending_seek_position_);
     pending_seek_position_ = base::TimeDelta();
@@ -898,6 +1066,7 @@ void MediaPlayerESPlusPlayer::OnError(const esplusplayer_error_type error) {
     GetRendererClient(DemuxerStream::VIDEO)->OnError(GetPipelineError(error));
   else if (IsValid(DemuxerStream::AUDIO))
     GetRendererClient(DemuxerStream::AUDIO)->OnError(GetPipelineError(error));
+  Release();
 }
 
 MediaPlayerESPlusPlayer::ElementryStream&
@@ -974,8 +1143,7 @@ void MediaPlayerESPlusPlayer::SetRendererClient(DemuxerStream::Type type,
   GetElementryStream(type).renderer_client_ = client;
 }
 
-base::circular_deque<scoped_refptr<DecoderBuffer>>
-MediaPlayerESPlusPlayer::GetBufferQueue(DemuxerStream::Type type) {
+Queue& MediaPlayerESPlusPlayer::GetBufferQueue(DemuxerStream::Type type) {
   return GetElementryStream(type).pending_buffers_;
 }
 
@@ -983,4 +1151,5 @@ void MediaPlayerESPlusPlayer::SetFrameAvailableCallback(
     const DataRequestCB& datacb) {
   data_cb_ = datacb;
 }
+
 }  // namespace media
index ec5ce91..b6d22db 100644 (file)
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 #include "media/base/demuxer_stream.h"
 #include "media/base/video_renderer_sink.h"
+#include "tizen_src/chromium_impl/base/tizen/static_map.h"
 #include "tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h"
 #include "tizen_src/chromium_impl/media/filters/esplusplayer_util.h"
 
@@ -22,10 +23,12 @@ namespace media {
 class DemuxerStream;
 class RendererClient;
 
+using Queue = base::circular_deque<scoped_refptr<DecoderBuffer>>;
+
 // This class handles media source extensions for CAPI port.
 class MEDIA_EXPORT MediaPlayerESPlusPlayer {
  public:
-  static MediaPlayerESPlusPlayer* GetEsppPlayer();
+  static MediaPlayerESPlusPlayer* GetMediaPlayerESPlusPlayer();
 
   using DataRequestCB =
       base::RepeatingCallback<void(uint32_t,
@@ -34,8 +37,13 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayer {
                                    base::TimeDelta,
                                    uint32_t,
                                    uint32_t)>;
-  bool Initialize(VideoRendererSink* sink);
+  bool CreatePlayer();
+  void Initialize(VideoRendererSink* sink);
+  bool IsInitialized();
   void Prepare();
+  bool IsPrepared();
+  void Release();
+
   void SetTaskRunner(
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
   void SetStreamInfo(DemuxerStream::Type type,
@@ -91,9 +99,12 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayer {
   void OnBufferReady(DemuxerStream::Type type,
                      DemuxerStream::Status status,
                      scoped_refptr<DecoderBuffer> buffer);
-  void SubmitEosPacket(DemuxerStream::Type type);
-  void SubmitEsPacket(DemuxerStream::Type type,
-                      scoped_refptr<DecoderBuffer> buffer);
+  bool ReadFromBufferQueue(DemuxerStream::Type type);
+  esplusplayer_submit_status SubmitEosPacket(DemuxerStream::Type type);
+  esplusplayer_submit_status SubmitEsPacket(
+      DemuxerStream::Type type,
+      scoped_refptr<DecoderBuffer> buffer);
+  void UpdateBufferedDtsDifference();
 
   ElementryStream& GetElementryStream(DemuxerStream::Type type);
   const ElementryStream& GetElementryStream(DemuxerStream::Type type) const;
@@ -111,16 +122,24 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayer {
   void SetDemuxerStream(DemuxerStream::Type type, DemuxerStream* stream);
   RendererClient* GetRendererClient(DemuxerStream::Type type) const;
   void SetRendererClient(DemuxerStream::Type type, RendererClient* client);
-  base::circular_deque<scoped_refptr<DecoderBuffer>> GetBufferQueue(
-      DemuxerStream::Type type);
+  Queue& GetBufferQueue(DemuxerStream::Type type);
 
   esplusplayer_handle esplayer_ = nullptr;
+  // first -> pts, second -> duration
+  base::StaticMap<DemuxerStream::Type,
+                  std::pair<base::TimeDelta, base::TimeDelta>,
+                  base::StaticKeys<DemuxerStream::Type,
+                                   DemuxerStream::AUDIO,
+                                   DemuxerStream::VIDEO>>
+      last_frames_;
 
   double volume_ = 1.0;
   bool playback_rate_ = 0.0;
   bool is_prepared_ = false;
+  bool is_preparing_ = false;
   bool is_paused_ = true;
   bool is_buffering_ = false;
+  base::TimeDelta current_position_;
 
   bool is_flushing_ = false;
   bool is_seeking_ = false;