| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef MEDIA_BASE_MEDIA_SERIALIZERS_H_ |
| #define MEDIA_BASE_MEDIA_SERIALIZERS_H_ |
| |
| #include <sstream> |
| #include <vector> |
| |
| #include "base/location.h" |
| #include "base/strings/stringprintf.h" |
| #include "media/base/audio_decoder_config.h" |
| #include "media/base/buffering_state.h" |
| #include "media/base/decoder.h" |
| #include "media/base/media_serializers_base.h" |
| #include "media/base/renderer_factory_selector.h" |
| #include "media/base/status.h" |
| #include "media/base/status_codes.h" |
| #include "media/base/text_track_config.h" |
| #include "media/base/video_decoder_config.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/hdr_metadata.h" |
| |
| namespace media { |
| |
| namespace internal { |
| |
| // Serializing any const or reference combination. |
| template <typename T> |
| struct MediaSerializer<const T> { |
| static base::Value Serialize(const T& value) { |
| return MediaSerializer<T>::Serialize(value); |
| } |
| }; |
| |
| template <typename T> |
| struct MediaSerializer<T&> { |
| static base::Value Serialize(const T& value) { |
| return MediaSerializer<T>::Serialize(value); |
| } |
| }; |
| |
| // Serialize default value. |
| template <> |
| struct MediaSerializer<base::Value> { |
| static base::Value Serialize(const base::Value& value) { |
| return value.Clone(); |
| } |
| }; |
| |
| // Serialize vectors of things |
| template <typename VecType> |
| struct MediaSerializer<std::vector<VecType>> { |
| static base::Value Serialize(const std::vector<VecType>& vec) { |
| #if defined(STARBOARD) |
| NOTREACHED(); |
| return base::Value(); |
| #else // defined(STARBOARD) |
| base::Value result(base::Value::Type::LIST); |
| for (const VecType& value : vec) |
| result.Append(MediaSerializer<VecType>::Serialize(value)); |
| return result; |
| #endif // defined(STARBOARD) |
| } |
| }; |
| |
| // Serialize unique pointers |
| template <typename T> |
| struct MediaSerializer<std::unique_ptr<T>> { |
| static base::Value Serialize(const std::unique_ptr<T>& ptr) { |
| if (!ptr) |
| return base::Value("nullptr"); |
| return MediaSerializer<T>::Serialize(*ptr); |
| } |
| }; |
| |
| // serialize optional types |
| template <typename OptType> |
| struct MediaSerializer<absl::optional<OptType>> { |
| static base::Value Serialize(const absl::optional<OptType>& opt) { |
| return opt ? MediaSerializer<OptType>::Serialize(opt.value()) |
| : base::Value("unset"); // TODO(tmathmeyer) maybe empty string? |
| } |
| }; |
| |
| // Sometimes raw strings wont template match to a char*. |
| template <int len> |
| struct MediaSerializer<char[len]> { |
| static inline base::Value Serialize(const char* code) { |
| return base::Value(code); |
| } |
| }; |
| |
| // Can't send non-finite double values to a base::Value. |
| template <> |
| struct MediaSerializer<double> { |
| static inline base::Value Serialize(double value) { |
| return std::isfinite(value) ? base::Value(value) : base::Value("unknown"); |
| } |
| }; |
| |
| template <> |
| struct MediaSerializer<int64_t> { |
| static inline base::Value Serialize(int64_t value) { |
| std::stringstream stream; |
| stream << "0x" << std::hex << value; |
| return MediaSerializer<std::string>::Serialize(stream.str()); |
| } |
| }; |
| |
| template <> |
| struct MediaSerializer<uint32_t> { |
| static inline base::Value Serialize(uint32_t value) { |
| std::stringstream stream; |
| stream << "0x" << std::hex << value; |
| return MediaSerializer<std::string>::Serialize(stream.str()); |
| } |
| }; |
| |
| // Just upcast this to get the NaN check. |
| template <> |
| struct MediaSerializer<float> { |
| static inline base::Value Serialize(float value) { |
| return MediaSerializer<double>::Serialize(value); |
| } |
| }; |
| |
| // Serialization for chromium-specific types. |
| // Each serializer should be commented like: |
| // Class/Enum (simple/complex) |
| // where Classes should take constref arguments, and "simple" methods should |
| // be declared inline. |
| |
| // the FIELD_SERIALIZE method can be used whenever the result is a dict named |
| // |result|. |
| #define FIELD_SERIALIZE(NAME, CONSTEXPR) \ |
| result.SetKey(NAME, MediaSerialize(CONSTEXPR)) |
| |
| // Class (simple) |
| template <> |
| struct MediaSerializer<gfx::Size> { |
| static inline base::Value Serialize(const gfx::Size& value) { |
| return base::Value(value.ToString()); |
| } |
| }; |
| |
| // Class (simple) |
| template <> |
| struct MediaSerializer<gfx::Rect> { |
| static inline base::Value Serialize(const gfx::Rect& value) { |
| return base::Value(value.ToString()); |
| } |
| }; |
| |
| // enum (simple) |
| template <> |
| struct MediaSerializer<base::TimeDelta> { |
| static inline base::Value Serialize(const base::TimeDelta value) { |
| return MediaSerializer<double>::Serialize(value.InSecondsF()); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<RendererType> { |
| static inline base::Value Serialize(RendererType value) { |
| return base::Value(GetRendererName(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<VideoDecoderType> { |
| static inline base::Value Serialize(VideoDecoderType value) { |
| return base::Value(GetDecoderName(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<AudioDecoderType> { |
| static inline base::Value Serialize(AudioDecoderType value) { |
| return base::Value(GetDecoderName(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<AudioCodec> { |
| static inline base::Value Serialize(AudioCodec value) { |
| return base::Value(GetCodecName(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<AudioCodecProfile> { |
| static inline base::Value Serialize(AudioCodecProfile value) { |
| return base::Value(GetProfileName(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<VideoCodec> { |
| static inline base::Value Serialize(VideoCodec value) { |
| return base::Value(GetCodecName(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<VideoCodecProfile> { |
| static inline base::Value Serialize(VideoCodecProfile value) { |
| return base::Value(GetProfileName(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<ChannelLayout> { |
| static inline base::Value Serialize(ChannelLayout value) { |
| return base::Value(ChannelLayoutToString(value)); |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<SampleFormat> { |
| static inline base::Value Serialize(SampleFormat value) { |
| return base::Value(SampleFormatToString(value)); |
| } |
| }; |
| |
| // Enum (complex) |
| template <> |
| struct MediaSerializer<EncryptionScheme> { |
| static base::Value Serialize(const EncryptionScheme& value) { |
| std::ostringstream encryptionSchemeString; |
| encryptionSchemeString << value; |
| return base::Value(encryptionSchemeString.str()); |
| } |
| }; |
| |
| // Class (complex) |
| template <> |
| struct MediaSerializer<VideoTransformation> { |
| static base::Value Serialize(const VideoTransformation& value) { |
| std::string rotation = VideoRotationToString(value.rotation); |
| if (value.mirrored) |
| rotation += ", mirrored"; |
| return base::Value(rotation); |
| } |
| }; |
| |
| // Class (simple) |
| template <> |
| struct MediaSerializer<VideoColorSpace> { |
| static inline base::Value Serialize(const VideoColorSpace& value) { |
| return base::Value(value.ToGfxColorSpace().ToString()); |
| } |
| }; |
| |
| // Class (complex) |
| template <> |
| struct MediaSerializer<gfx::HDRMetadata> { |
| static base::Value Serialize(const gfx::HDRMetadata& value) { |
| // TODO(tmathmeyer) serialize more fields here potentially. |
| base::Value result(base::Value::Type::DICTIONARY); |
| FIELD_SERIALIZE( |
| "luminance range", |
| base::StringPrintf("%.2f => %.2f", |
| value.color_volume_metadata.luminance_min, |
| value.color_volume_metadata.luminance_max)); |
| FIELD_SERIALIZE("primaries", |
| base::StringPrintf( |
| "[r:%.4f,%.4f, g:%.4f,%.4f, b:%.4f,%.4f, wp:%.4f,%.4f]", |
| value.color_volume_metadata.primary_r.x(), |
| value.color_volume_metadata.primary_r.y(), |
| value.color_volume_metadata.primary_g.x(), |
| value.color_volume_metadata.primary_g.y(), |
| value.color_volume_metadata.primary_b.x(), |
| value.color_volume_metadata.primary_b.y(), |
| value.color_volume_metadata.white_point.x(), |
| value.color_volume_metadata.white_point.y())); |
| return result; |
| } |
| }; |
| |
| // Class (complex) |
| template <> |
| struct MediaSerializer<AudioDecoderConfig> { |
| static base::Value Serialize(const AudioDecoderConfig& value) { |
| base::Value result(base::Value::Type::DICTIONARY); |
| FIELD_SERIALIZE("codec", value.codec()); |
| FIELD_SERIALIZE("profile", value.profile()); |
| FIELD_SERIALIZE("bytes per channel", value.bytes_per_channel()); |
| FIELD_SERIALIZE("channel layout", value.channel_layout()); |
| FIELD_SERIALIZE("channels", value.channels()); |
| FIELD_SERIALIZE("samples per second", value.samples_per_second()); |
| FIELD_SERIALIZE("sample format", value.sample_format()); |
| FIELD_SERIALIZE("bytes per frame", value.bytes_per_frame()); |
| FIELD_SERIALIZE("codec delay", value.codec_delay()); |
| FIELD_SERIALIZE("has extra data", !value.extra_data().empty()); |
| FIELD_SERIALIZE("encryption scheme", value.encryption_scheme()); |
| FIELD_SERIALIZE("discard decoder delay", |
| value.should_discard_decoder_delay()); |
| |
| // TODO(tmathmeyer) drop the units, let the frontend handle it. |
| // use ostringstreams because windows & linux have _different types_ |
| // defined for int64_t, (long vs long long) so format specifiers dont work. |
| std::ostringstream preroll; |
| preroll << value.seek_preroll().InMicroseconds() << "us"; |
| |
| #if defined(STARBOARD) |
| result.SetKey("seek preroll", base::Value(preroll.str())); |
| #else // defined(STARBOARD) |
| result.SetStringKey("seek preroll", preroll.str()); |
| #endif // defined(STARBOARD) |
| |
| return result; |
| } |
| }; |
| |
| // Enum (simple) |
| template <> |
| struct MediaSerializer<VideoDecoderConfig::AlphaMode> { |
| static inline base::Value Serialize(VideoDecoderConfig::AlphaMode value) { |
| return base::Value(value == VideoDecoderConfig::AlphaMode::kHasAlpha |
| ? "has_alpha" |
| : "is_opaque"); |
| } |
| }; |
| |
| // Class (complex) |
| template <> |
| struct MediaSerializer<VideoDecoderConfig> { |
| static base::Value Serialize(const VideoDecoderConfig& value) { |
| base::Value result(base::Value::Type::DICTIONARY); |
| FIELD_SERIALIZE("codec", value.codec()); |
| FIELD_SERIALIZE("profile", value.profile()); |
| FIELD_SERIALIZE("alpha mode", value.alpha_mode()); |
| FIELD_SERIALIZE("coded size", value.coded_size()); |
| FIELD_SERIALIZE("visible rect", value.visible_rect()); |
| FIELD_SERIALIZE("natural size", value.natural_size()); |
| FIELD_SERIALIZE("has extra data", !value.extra_data().empty()); |
| FIELD_SERIALIZE("encryption scheme", value.encryption_scheme()); |
| FIELD_SERIALIZE("orientation", value.video_transformation()); |
| FIELD_SERIALIZE("color space", value.color_space_info()); |
| FIELD_SERIALIZE("hdr metadata", value.hdr_metadata()); |
| return result; |
| } |
| }; |
| |
| // Class (complex) |
| template <> |
| struct MediaSerializer<TextTrackConfig> { |
| static base::Value Serialize(const TextTrackConfig& value) { |
| base::Value result(base::Value::Type::DICTIONARY); |
| FIELD_SERIALIZE("kind", value.kind()); |
| FIELD_SERIALIZE("language", value.language()); |
| if (value.label().length()) |
| FIELD_SERIALIZE("label", value.label()); |
| return result; |
| } |
| }; |
| |
| // enum (simple) |
| template <> |
| struct MediaSerializer<TextKind> { |
| static base::Value Serialize(const TextKind value) { |
| switch (value) { |
| case kTextSubtitles: |
| return base::Value("Subtitles"); |
| case kTextCaptions: |
| return base::Value("Captions"); |
| case kTextDescriptions: |
| return base::Value("Descriptions"); |
| case kTextMetadata: |
| return base::Value("Metadata"); |
| case kTextChapters: |
| return base::Value("Chapters"); |
| case kTextNone: |
| return base::Value("None"); |
| } |
| } |
| }; |
| |
| // enum (simple) |
| template <> |
| struct MediaSerializer<BufferingState> { |
| static inline base::Value Serialize(const BufferingState value) { |
| return base::Value(value == BufferingState::BUFFERING_HAVE_ENOUGH |
| ? "BUFFERING_HAVE_ENOUGH" |
| : "BUFFERING_HAVE_NOTHING"); |
| } |
| }; |
| |
| // enum (complex) |
| template <> |
| struct MediaSerializer<BufferingStateChangeReason> { |
| static base::Value Serialize(const BufferingStateChangeReason value) { |
| switch (value) { |
| case DEMUXER_UNDERFLOW: |
| return base::Value("DEMUXER_UNDERFLOW"); |
| case DECODER_UNDERFLOW: |
| return base::Value("DECODER_UNDERFLOW"); |
| case REMOTING_NETWORK_CONGESTION: |
| return base::Value("REMOTING_NETWORK_CONGESTION"); |
| case BUFFERING_CHANGE_REASON_UNKNOWN: |
| return base::Value("BUFFERING_CHANGE_REASON_UNKNOWN"); |
| } |
| } |
| }; |
| |
| // Class (complex) |
| template <SerializableBufferingStateType T> |
| struct MediaSerializer<SerializableBufferingState<T>> { |
| static base::Value Serialize(const SerializableBufferingState<T>& value) { |
| base::Value result(base::Value::Type::DICTIONARY); |
| FIELD_SERIALIZE("state", value.state); |
| |
| switch (value.reason) { |
| case DEMUXER_UNDERFLOW: |
| case DECODER_UNDERFLOW: |
| case REMOTING_NETWORK_CONGESTION: |
| FIELD_SERIALIZE("reason", value.reason); |
| break; |
| |
| // Don't write anything here if the reason is unknown. |
| case BUFFERING_CHANGE_REASON_UNKNOWN: |
| break; |
| } |
| |
| #if defined(STARBOARD) |
| if (T == SerializableBufferingStateType::kPipeline) |
| result.SetKey("for_suspended_start", base::Value(value.suspended_start)); |
| #else // defined(STARBOARD) |
| if (T == SerializableBufferingStateType::kPipeline) |
| result.SetBoolKey("for_suspended_start", value.suspended_start); |
| #endif // defined(STARBOARD) |
| |
| return result; |
| } |
| }; |
| |
| // enum (simple) |
| template <> |
| struct MediaSerializer<StatusCode> { |
| static inline base::Value Serialize(StatusCode code) { |
| return base::Value(static_cast<int>(code)); |
| } |
| }; |
| |
| // Class (complex) |
| template <typename T> |
| struct MediaSerializer<TypedStatus<T>> { |
| static base::Value Serialize(const TypedStatus<T>& status) { |
| // TODO: replace this with some kind of static "description" |
| // of the default type, instead of "Ok". |
| if (status.is_ok()) |
| return base::Value("Ok"); |
| return MediaSerialize(status.data_); |
| } |
| }; |
| |
| // Class (complex) |
| template <> |
| struct MediaSerializer<StatusData> { |
| static base::Value Serialize(const StatusData& status) { |
| base::Value result(base::Value::Type::DICTIONARY); |
| // TODO: replace code with a stringified version, since |
| // this representation will only go to medialog anyway. |
| FIELD_SERIALIZE("code", status.code); |
| FIELD_SERIALIZE("group", status.group); |
| FIELD_SERIALIZE("message", status.message); |
| FIELD_SERIALIZE("stack", status.frames); |
| FIELD_SERIALIZE("data", status.data); |
| FIELD_SERIALIZE("causes", status.causes); |
| return result; |
| } |
| }; |
| |
| // Class (complex) |
| template <> |
| struct MediaSerializer<base::Location> { |
| static base::Value Serialize(const base::Location& value) { |
| base::Value result(base::Value::Type::DICTIONARY); |
| FIELD_SERIALIZE("file", value.file_name() ? value.file_name() : "unknown"); |
| FIELD_SERIALIZE("line", value.line_number()); |
| return result; |
| } |
| }; |
| |
| #undef FIELD_SERIALIZE |
| |
| } // namespace internal |
| |
| } // namespace media |
| |
| #endif // MEDIA_BASE_MEDIA_SERIALIZERS_H_ |