| // Copyright 2016 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. |
| |
| #include "base/logging.h" |
| #include "services/media/framework/parts/lpcm_reformatter.h" |
| |
| namespace mojo { |
| namespace media { |
| |
| // LpcmReformatter implementation that accepts samples of type TIn and |
| // produces samples of type TOut. |
| template<typename TIn, typename TOut> |
| class LpcmReformatterImpl : public LpcmReformatter { |
| public: |
| LpcmReformatterImpl( |
| const LpcmStreamType& in_type, |
| const LpcmStreamTypeSet& out_type); |
| |
| ~LpcmReformatterImpl() override; |
| |
| // Transform implementation. |
| bool TransformPacket( |
| const PacketPtr& input, |
| bool new_input, |
| PayloadAllocator* allocator, |
| PacketPtr* output) override; |
| |
| private: |
| LpcmStreamType in_type_; |
| LpcmStreamType out_type_; |
| }; |
| |
| std::shared_ptr<LpcmReformatter> LpcmReformatter::Create( |
| const LpcmStreamType& in_type, |
| const LpcmStreamTypeSet& out_type) { |
| LpcmReformatter* result = nullptr; |
| |
| switch (in_type.sample_format()) { |
| case LpcmStreamType::SampleFormat::kUnsigned8: |
| switch (out_type.sample_format()) { |
| case LpcmStreamType::SampleFormat::kUnsigned8: |
| case LpcmStreamType::SampleFormat::kAny: |
| result = new LpcmReformatterImpl<uint8_t, uint8_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned16: |
| result = new LpcmReformatterImpl<uint8_t, int16_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned24In32: |
| result = new LpcmReformatterImpl<uint8_t, int32_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kFloat: |
| result = new LpcmReformatterImpl<uint8_t, float>( |
| in_type, out_type); |
| break; |
| default: |
| NOTREACHED() << "unsupported sample format"; |
| result = nullptr; |
| break; |
| } |
| break; |
| case LpcmStreamType::SampleFormat::kSigned16: |
| switch (out_type.sample_format()) { |
| case LpcmStreamType::SampleFormat::kUnsigned8: |
| result = new LpcmReformatterImpl<int16_t, uint8_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned16: |
| case LpcmStreamType::SampleFormat::kAny: |
| result = new LpcmReformatterImpl<int16_t, int16_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned24In32: |
| result = new LpcmReformatterImpl<int16_t, int32_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kFloat: |
| result = new LpcmReformatterImpl<int16_t, float>( |
| in_type, out_type); |
| break; |
| default: |
| NOTREACHED() << "unsupported sample format"; |
| result = nullptr; |
| break; |
| } |
| break; |
| case LpcmStreamType::SampleFormat::kSigned24In32: |
| switch (out_type.sample_format()) { |
| case LpcmStreamType::SampleFormat::kUnsigned8: |
| result = new LpcmReformatterImpl<int32_t, uint8_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned16: |
| result = new LpcmReformatterImpl<int32_t, int16_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned24In32: |
| case LpcmStreamType::SampleFormat::kAny: |
| result = new LpcmReformatterImpl<int32_t, int32_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kFloat: |
| result = new LpcmReformatterImpl<int32_t, float>( |
| in_type, out_type); |
| break; |
| default: |
| NOTREACHED() << "unsupported sample format"; |
| result = nullptr; |
| break; |
| } |
| break; |
| case LpcmStreamType::SampleFormat::kFloat: |
| switch (out_type.sample_format()) { |
| case LpcmStreamType::SampleFormat::kUnsigned8: |
| result = new LpcmReformatterImpl<float, uint8_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned16: |
| result = new LpcmReformatterImpl<float, int16_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kSigned24In32: |
| result = new LpcmReformatterImpl<float, int32_t>( |
| in_type, out_type); |
| break; |
| case LpcmStreamType::SampleFormat::kFloat: |
| case LpcmStreamType::SampleFormat::kAny: |
| result = new LpcmReformatterImpl<float, float>(in_type, out_type); |
| break; |
| default: |
| NOTREACHED() << "unsupported sample format"; |
| result = nullptr; |
| break; |
| } |
| break; |
| default: |
| NOTREACHED() << "unsupported sample format"; |
| result = nullptr; |
| break; |
| } |
| |
| return std::shared_ptr<LpcmReformatter>(result); |
| } |
| |
| template<typename TIn, typename TOut> |
| LpcmReformatterImpl<TIn, TOut>::LpcmReformatterImpl( |
| const LpcmStreamType& in_type, |
| const LpcmStreamTypeSet& out_type) : |
| in_type_(in_type), |
| out_type_( |
| out_type.sample_format() == LpcmStreamType::SampleFormat::kAny ? |
| in_type.sample_format() : |
| out_type.sample_format(), |
| in_type.channels(), |
| in_type.frames_per_second()) {} |
| |
| template<typename TIn, typename TOut> |
| LpcmReformatterImpl<TIn, TOut>::~LpcmReformatterImpl() {} |
| |
| namespace { |
| |
| template <typename T> |
| inline constexpr T Clamp(T val, T min, T max) { |
| return (val > max) ? max : ((val < min) ? min : val); |
| } |
| |
| template <typename T> |
| inline constexpr T Clamp(T val); |
| |
| template <> |
| inline constexpr float Clamp(float val) { |
| return Clamp(val, -1.0f, 1.0f); |
| } |
| |
| template <> |
| inline constexpr int32_t Clamp(int32_t val) { |
| return Clamp(val, 1 << 23, -(1 << 23)); |
| } |
| |
| template<typename TIn, typename TOut> |
| inline void CopySample(TOut* dest, const TIn* source) { |
| *dest = static_cast<TOut>(*source); |
| } |
| |
| inline void CopySample(uint8_t* dest, const int16_t* source) { |
| *dest = static_cast<uint8_t>((*source >> 8) ^ 0x80); |
| } |
| |
| inline void CopySample(uint8_t* dest, const int32_t* source) { |
| *dest = static_cast<uint8_t>((Clamp(*source) >> 16) ^ 0x80); |
| } |
| |
| inline void CopySample(uint8_t* dest, const float* source) { |
| *dest = static_cast<uint8_t>((Clamp(*source) * 0x7f) + 128); |
| } |
| |
| inline void CopySample(int16_t* dest, const uint8_t* source) { |
| *dest = static_cast<int16_t>(*source ^ 0x80) << 8; |
| } |
| |
| inline void CopySample(int16_t* dest, const int32_t* source) { |
| *dest = static_cast<int16_t>(Clamp(*source) >> 8); |
| } |
| |
| inline void CopySample(int16_t* dest, const float* source) { |
| *dest = static_cast<int16_t>(Clamp(*source) * 0x7fff); |
| } |
| |
| inline void CopySample(int32_t* dest, const uint8_t* source) { |
| *dest = static_cast<int32_t>(*source ^ 0x80) << 16; |
| } |
| |
| inline void CopySample(int32_t* dest, const int16_t* source) { |
| *dest = static_cast<int32_t>(*source << 8); |
| } |
| |
| inline void CopySample(int32_t* dest, const float* source) { |
| *dest = static_cast<int32_t>(Clamp(*source) * 0x7fffff); |
| } |
| |
| inline void CopySample(float* dest, const uint8_t* source) { |
| *dest = static_cast<float>(*source ^ 0x80) / 0x80; |
| } |
| |
| inline void CopySample(float* dest, const int16_t* source) { |
| *dest = static_cast<float>(*source) / 0x8000; |
| } |
| |
| inline void CopySample(float* dest, const int32_t* source) { |
| *dest = static_cast<float>(Clamp(*source)) / 0x800000; |
| } |
| |
| } // namespace |
| |
| template<typename TIn, typename TOut> |
| bool LpcmReformatterImpl<TIn, TOut>::TransformPacket( |
| const PacketPtr& input, |
| bool new_input, |
| PayloadAllocator* allocator, |
| PacketPtr* output) { |
| DCHECK(input); |
| DCHECK(allocator); |
| DCHECK(output); |
| |
| uint64_t in_size = input->size(); |
| if (in_size == 0) { |
| // Zero-sized input packet. Make a copy. |
| *output = Packet::Create( |
| input->presentation_time(), |
| input->duration(), |
| input->end_of_stream(), |
| 0, |
| nullptr, |
| nullptr); |
| return true; |
| } |
| |
| size_t frame_count = input->duration(); |
| uint64_t out_size = out_type_.min_buffer_size(frame_count); |
| |
| void* buffer = allocator->AllocatePayloadBuffer(out_size); |
| if (buffer == nullptr) { |
| LOG(WARNING) << "lpcm reformatter starved for buffers"; |
| // Starved for buffer space. Can't process now. |
| *output = nullptr; |
| return false; |
| } |
| |
| const TIn* in_channel = static_cast<const TIn*>(input->payload()); |
| TOut* out_channel = static_cast<TOut*>(buffer); |
| |
| for (uint32_t channel = 0; channel < in_type_.channels(); channel++) { |
| const TIn* in_sample = in_channel; |
| TOut* out_sample = out_channel; |
| for (size_t sample = 0; sample < frame_count; sample++) { |
| CopySample(out_sample, in_sample); |
| in_sample += in_type_.channels(); |
| out_sample += out_type_.channels(); |
| } |
| ++in_channel; |
| ++out_channel; |
| } |
| |
| *output = Packet::Create( |
| input->presentation_time(), |
| frame_count, |
| input->end_of_stream(), |
| out_size, |
| buffer, |
| allocator); |
| |
| return true; |
| } |
| |
| } // namespace media |
| } // namespace mojo |