diff --git a/misc/prerequisites.sh b/misc/prerequisites.sh index 085441149..bfd899cc5 100755 --- a/misc/prerequisites.sh +++ b/misc/prerequisites.sh @@ -19,7 +19,7 @@ PCRE2_VERSION=10.39 OPENH264_VERSION=2.4.0 HIREDIS_VERSION=1.0.2 NVCC_HDR_VERSION=11.1.5.2 -X264_VERSION=20191217-2245-stable +X264_VERSION=31e19f92 INTEL_QSV_HWACCELS=false NETINT_LOGAN_HWACCELS=false @@ -113,7 +113,7 @@ install_libx264() (DIR=${TEMP_PATH}/x264 && \ mkdir -p ${DIR} && \ cd ${DIR} && \ - curl -sLf https://download.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-${X264_VERSION}.tar.bz2 | tar -jx --strip-components=1 && \ + curl -sLf https://code.videolan.org/videolan/x264/-/archive/master/x264-${X264_VERSION}.tar.bz2 | tar -jx --strip-components=1 && \ ./configure --prefix="${PREFIX}" --enable-shared --enable-pic --disable-cli && \ make -j$(nproc) && \ sudo make install && \ diff --git a/src/projects/base/info/audio_track.cpp b/src/projects/base/info/audio_track.cpp index f944f5cf9..7a5bc2d85 100644 --- a/src/projects/base/info/audio_track.cpp +++ b/src/projects/base/info/audio_track.cpp @@ -29,16 +29,6 @@ int32_t AudioTrack::GetSampleRate() const return (int32_t)_sample.GetRate(); } -void AudioTrack::SetAudioTimestampScale(double scale) -{ - _audio_timescale = scale; -} - -double AudioTrack::GetAudioTimestampScale() const -{ - return _audio_timescale; -} - AudioSample &AudioTrack::GetSample() { return _sample; diff --git a/src/projects/base/info/audio_track.h b/src/projects/base/info/audio_track.h index ef7451d16..1bcb06a73 100644 --- a/src/projects/base/info/audio_track.h +++ b/src/projects/base/info/audio_track.h @@ -20,9 +20,6 @@ class AudioTrack void SetSampleRate(int32_t samplerate); int32_t GetSampleRate() const; - void SetAudioTimestampScale(double scale); - double GetAudioTimestampScale() const; - cmn::AudioSample &GetSample(); const cmn::AudioSample &GetSample() const; diff --git a/src/projects/base/info/decoder_configuration_record.h b/src/projects/base/info/decoder_configuration_record.h index 027a4bc05..8a929c344 100644 --- a/src/projects/base/info/decoder_configuration_record.h +++ b/src/projects/base/info/decoder_configuration_record.h @@ -5,7 +5,7 @@ class DecoderConfigurationRecord { public: - std::shared_ptr<ov::Data> GetData() + std::shared_ptr<const ov::Data> GetData() { if (_updated) { @@ -16,7 +16,7 @@ class DecoderConfigurationRecord return _data; } - virtual bool Parse(const std::shared_ptr<ov::Data> &data) = 0; + virtual bool Parse(const std::shared_ptr<const ov::Data> &data) = 0; virtual bool IsValid() const = 0; virtual bool Equals(const std::shared_ptr<DecoderConfigurationRecord> &other) = 0; @@ -24,10 +24,10 @@ class DecoderConfigurationRecord virtual ov::String GetCodecsParameter() const = 0; protected: - virtual std::shared_ptr<ov::Data> Serialize() = 0; + virtual std::shared_ptr<const ov::Data> Serialize() = 0; // Set serialized data - void SetData(const std::shared_ptr<ov::Data> &data) + void SetData(const std::shared_ptr<const ov::Data> &data) { _data = data; _updated = false; @@ -39,6 +39,6 @@ class DecoderConfigurationRecord } private: - std::shared_ptr<ov::Data> _data = nullptr; + std::shared_ptr<const ov::Data> _data = nullptr; bool _updated = true; }; \ No newline at end of file diff --git a/src/projects/base/ovlibrary/bit_reader.cpp b/src/projects/base/ovlibrary/bit_reader.cpp new file mode 100644 index 000000000..cd3be5711 --- /dev/null +++ b/src/projects/base/ovlibrary/bit_reader.cpp @@ -0,0 +1,15 @@ +#include "bit_reader.h" + +BitReader::BitReader(const uint8_t *buffer, size_t capacity) + : _buffer(buffer), + _position(buffer), + _capacity(capacity) +{ +} + +BitReader::BitReader(const std::shared_ptr<const ov::Data> &data) + : _buffer(data->GetDataAs<uint8_t>()), + _position(data->GetDataAs<uint8_t>()), + _capacity(data->GetLength()) +{ +} diff --git a/src/projects/base/ovlibrary/bit_reader.h b/src/projects/base/ovlibrary/bit_reader.h index 7495d3228..651b8696d 100644 --- a/src/projects/base/ovlibrary/bit_reader.h +++ b/src/projects/base/ovlibrary/bit_reader.h @@ -1,30 +1,26 @@ #pragma once -#include <utility> +#include <base/ovlibrary/ovlibrary.h> #include <stdint.h> -#include <type_traits> + #include <algorithm> -#include <base/ovlibrary/ovlibrary.h> +#include <type_traits> +#include <utility> #include "byte_io.h" class BitReader { - public: - BitReader(const uint8_t *buffer, size_t capacity) : - _buffer(buffer), - _position(buffer), - _capacity(capacity) - { - } - - template<typename T> + BitReader(const uint8_t *buffer, size_t capacity); + BitReader(const std::shared_ptr<const ov::Data> &data); + + template <typename T> T ReadBytes(bool big_endian = true) { T value; - bool result = ReadBytes(value, big_endian); - if(result == false) + + if (ReadBytes(value, big_endian) == false) { return 0; } @@ -39,7 +35,7 @@ class BitReader bool SkipBytes(size_t length) { - if (length > static_cast<size_t>(_capacity - (_position - _buffer))) + if (length > static_cast<size_t>(_capacity - (_position - _buffer))) { return false; } @@ -61,88 +57,64 @@ class BitReader } // Note: ReadBytes() API obtains the bits without considering _bit_offset - template<typename T> - bool ReadBytes(T& value, bool big_endian = true) + template <typename T> + bool ReadBytes(T &value, bool big_endian = true) { - if (sizeof(value) > static_cast<size_t>(_capacity - (_position - _buffer))) + if (sizeof(value) > static_cast<size_t>(_capacity - (_position - _buffer))) { return false; } - if(big_endian == true) - { - value = ByteReader<T>::ReadBigEndian(_position); - } - else - { - value = ByteReader<T>::ReadLittleEndian(_position); - } + value = big_endian ? ByteReader<T>::ReadBigEndian(_position) : ByteReader<T>::ReadLittleEndian(_position); _position += sizeof(value); return true; } - template<typename T> - T ReadBits(uint8_t bits) + template <typename T> + T ReadBits(uint8_t bits) { T value; - bool result = ReadBits(bits, value); - if(result == false) + + if (ReadBits<T>(bits, value)) { - return 0; + return value; } - return value; + return static_cast<T>(0); } - template<typename T> - bool ReadBits(uint8_t bits, T& value) - { - if (bits > sizeof(value) * 8) - { - OV_ASSERT2(false); - return false; - } - value = 0; - if (bits == 0) + template <typename T> + bool ReadBits(uint8_t bits, T &value) + { + if constexpr (std::is_enum_v<T>) { - return 0; - } + auto underlying_value = static_cast<std::underlying_type_t<T>>(value); + if (ReadBitsInternal(bits, underlying_value)) + { + value = static_cast<T>(underlying_value); + return true; + } - if (static_cast<size_t>((bits + 7) / 8) > static_cast<size_t>(_capacity - (_position - _buffer))) - { return false; } - - while (bits) - { - const uint8_t bits_from_this_byte = std::min(bits >= 8 ? 8 : bits % 8, 8 - _bit_offset); - const uint8_t mask_offset = 8 - bits_from_this_byte - _bit_offset; - const uint8_t mask = ((1 << bits_from_this_byte ) - 1) << mask_offset; - value <<= bits_from_this_byte; - value |= (*_position & mask) >> mask_offset; - bits -= bits_from_this_byte; - _bit_offset += bits_from_this_byte; - if (_bit_offset == 8) - { - NextPosition(); - _bit_offset = 0; - } - } - return true; - } + else + { + return ReadBitsInternal(bits, value); + } + } bool ReadBit(uint8_t &value) - { - return ReadBits<uint8_t>(1, value); - } + { + return ReadBits<uint8_t>(1, value); + } - bool ReadBoolBit() + bool ReadBoolBit() { bool value; bool result = ReadBit(value); - if(result == false) + if (result == false) { return false; } @@ -150,22 +122,22 @@ class BitReader return value; } - bool ReadBit(bool &value) - { - uint8_t bit; - if (ReadBit(bit)) - { - value = bit == 1 ? true : false; - return true; - } - return false; - } + bool ReadBit(bool &value) + { + uint8_t bit; + if (ReadBit(bit)) + { + value = bit == 1 ? true : false; + return true; + } + return false; + } uint8_t ReadBit() { uint8_t value; bool result = ReadBit(value); - if(result == false) + if (result == false) { return 0; } @@ -180,7 +152,7 @@ class BitReader size_t BytesSetionConsumed() { - if(_lap_position == nullptr) + if (_lap_position == nullptr) { return 0; } @@ -189,7 +161,7 @@ class BitReader return bytes; } - const uint8_t* CurrentPosition() + const uint8_t *CurrentPosition() { return _position; } @@ -204,26 +176,66 @@ class BitReader return (_capacity * 8) - BitsConsumed(); } - size_t BytesConsumed() const - { - return _position - _buffer; - } + size_t BytesConsumed() const + { + return _position - _buffer; + } - size_t BitsConsumed() const - { - return (BytesConsumed() * 8) + _bit_offset; - } + size_t BitsConsumed() const + { + return (BytesConsumed() * 8) + _bit_offset; + } protected: + template <typename T> + bool ReadBitsInternal(uint8_t bits, T &value) + { + if (bits > sizeof(value) * 8) + { + OV_ASSERT2(false); + return false; + } + + value = 0; + + if (bits == 0) + { + return true; + } + + if (static_cast<size_t>((bits + 7) / 8) > static_cast<size_t>(_capacity - (_position - _buffer))) + { + return false; + } + + while (bits) + { + const uint8_t bits_from_this_byte = std::min(bits >= 8 ? 8 : bits % 8, 8 - _bit_offset); + const uint8_t mask_offset = 8 - bits_from_this_byte - _bit_offset; + const uint8_t mask = ((1 << bits_from_this_byte) - 1) << mask_offset; + value <<= bits_from_this_byte; + value |= (*_position & mask) >> mask_offset; + bits -= bits_from_this_byte; + _bit_offset += bits_from_this_byte; + if (_bit_offset == 8) + { + NextPosition(); + _bit_offset = 0; + } + } + + return true; + } + virtual void NextPosition() { - _position ++; + _position++; } - const uint8_t* _buffer; - const uint8_t* _position; - const uint8_t* _lap_position; - size_t _capacity; - int _bit_offset = 0; - uint8_t _mask = 0x80; + const uint8_t *_buffer; + const uint8_t *_position; + const uint8_t *_lap_position; + size_t _capacity; + int _bit_offset = 0; + uint8_t _mask = 0x80; }; diff --git a/src/projects/base/ovlibrary/data.cpp b/src/projects/base/ovlibrary/data.cpp index a3ff8855d..2989b02a2 100644 --- a/src/projects/base/ovlibrary/data.cpp +++ b/src/projects/base/ovlibrary/data.cpp @@ -210,6 +210,16 @@ namespace ov return IsEqual(data->GetData(), data->GetLength()); } + bool Data::IsEqual(const std::shared_ptr<const Data> &data) const + { + return IsEqual(data->GetData(), data->GetLength()); + } + + bool Data::IsEqual(const std::shared_ptr<Data> &data) const + { + return IsEqual(data->GetData(), data->GetLength()); + } + bool Data::IsEmpty() const { return (GetLength() == 0); @@ -406,6 +416,11 @@ namespace ov return String(GetDataAs<const char>(), GetLength()); } + String Data::ToHexString(size_t length) const + { + return ov::ToHexString(GetDataAs<const uint8_t>(), std::min(GetLength(), length)); + } + String Data::ToHexString() const { return ov::ToHexString(GetDataAs<const uint8_t>(), GetLength()); diff --git a/src/projects/base/ovlibrary/data.h b/src/projects/base/ovlibrary/data.h index f7a56d692..93ccba821 100644 --- a/src/projects/base/ovlibrary/data.h +++ b/src/projects/base/ovlibrary/data.h @@ -193,10 +193,8 @@ namespace ov bool IsEqual(const void *data, size_t length) const; bool IsEqual(const Data &data) const; bool IsEqual(const Data *data) const; - bool IsEqual(const std::shared_ptr<Data> &data) const - { - return IsEqual(data->GetData(), data->GetLength()); - } + bool IsEqual(const std::shared_ptr<const Data> &data) const; + bool IsEqual(const std::shared_ptr<Data> &data) const; bool IsEmpty() const; @@ -204,6 +202,7 @@ namespace ov String Dump(const char *title, const char *line_prefix) const noexcept; String Dump(const char *title, off_t offset = 0, size_t max_bytes = 1024, const char *line_prefix = nullptr) const noexcept; String ToString() const; + String ToHexString(size_t length) const; String ToHexString() const; protected: diff --git a/src/projects/base/ovlibrary/type.h b/src/projects/base/ovlibrary/type.h index cfed8fad1..95428343b 100644 --- a/src/projects/base/ovlibrary/type.h +++ b/src/projects/base/ovlibrary/type.h @@ -13,30 +13,35 @@ namespace ov { template <typename T> - std::underlying_type_t<T> ToUnderlyingType(T enum_value) + inline std::underlying_type_t<T> ToUnderlyingType(T enum_value) { return static_cast<std::underlying_type_t<T>>(enum_value); } -} // namespace ov -#define OV_DEFINE_SETTER(type, setter, member, extra_qualifier) \ - void setter(const type &value) extra_qualifier \ - { \ - member = value; \ - } + template <typename T> + using UnderylingType = std::underlying_type_t<T>; +} // namespace ov -#define OV_DEFINE_GETTER(type, getter, member, extra_qualifier) \ - type getter() extra_qualifier \ - { \ - return member; \ +#define OV_DEFINE_SETTER(value_type, setter_return, setter, member, extra_qualifier, pre_process, post_process) \ + setter_return setter(const value_type &value) extra_qualifier \ + { \ + pre_process; \ + member = value; \ + post_process; \ } -#define OV_DEFINE_CONST_GETTER(type, getter, member, extra_qualifier) \ - const type &getter() const extra_qualifier \ +#define OV_DEFINE_GETTER(value_type, getter, member, extra_qualifier) \ + value_type getter() extra_qualifier \ { \ return member; \ } -#define OV_DEFINE_SETTER_CONST_GETTER(type, setter, getter, member, extra_qualifier) \ - OV_DEFINE_SETTER(type, setter, member, extra_qualifier) \ - OV_DEFINE_CONST_GETTER(type, getter, member, extra_qualifier) +#define OV_DEFINE_CONST_GETTER(value_type, getter, member, extra_qualifier) \ + const value_type &getter() const extra_qualifier \ + { \ + return member; \ + } + +#define OV_DEFINE_SETTER_CONST_GETTER(value_type, setter_return, setter, getter, member, extra_qualifier, pre_process, post_process) \ + OV_DEFINE_SETTER(value_type, setter_return, setter, member, extra_qualifier, pre_process, post_process) \ + OV_DEFINE_CONST_GETTER(value_type, getter, member, extra_qualifier) diff --git a/src/projects/base/ovlibrary/url.cpp b/src/projects/base/ovlibrary/url.cpp index 38eafac6d..ce01895d7 100644 --- a/src/projects/base/ovlibrary/url.cpp +++ b/src/projects/base/ovlibrary/url.cpp @@ -118,10 +118,14 @@ namespace ov return result_string; } - void Url::SetPath(const ov::String &path) + bool Url::SetPath(const ov::String &path) { _path = path; + _file = ""; + _stream = ""; + _app = ""; + // split <path> to /<app>/<stream>/<file> (4 tokens) auto tokens = _path.Split("/"); @@ -142,34 +146,58 @@ namespace ov // Nothing to do break; } + + return true; } - std::shared_ptr<Url> Url::Parse(const ov::String &url) + bool Url::ParseFromSource() { - auto object = std::make_shared<Url>(); - std::string url_string = url.CStr(); - - object->_source = url; - - auto matches = g_url_parse_regex.Matches(url); + auto matches = g_url_parse_regex.Matches(_source); if (matches.GetError() != nullptr) { - return nullptr; + _scheme = ""; + _id = ""; + _password = ""; + _host = ""; + _port = 0; + _path = ""; + _query_string = ""; + _has_query_string = false; + _query_parsed = false; + + _app = ""; + _stream = ""; + _file = ""; + + return false; } auto group_list = matches.GetNamedGroupList(); - object->_scheme = group_list["scheme"].GetValue(); - object->_id = group_list["id"].GetValue(); - object->_password = group_list["password"].GetValue(); - object->_host = group_list["host"].GetValue(); - object->_port = ov::Converter::ToUInt32(group_list["port"].GetValue()); - object->SetPath(group_list["path"].GetValue()); - object->_query_string = group_list["qs"].GetValue(); - object->_has_query_string = (object->_query_string.IsEmpty() == false); + _scheme = group_list["scheme"].GetValue(); + _id = group_list["id"].GetValue(); + _password = group_list["password"].GetValue(); + _host = group_list["host"].GetValue(); + _port = ov::Converter::ToUInt32(group_list["port"].GetValue()); + SetPath(group_list["path"].GetValue()); + _query_string = group_list["qs"].GetValue(); + _has_query_string = (_query_string.IsEmpty() == false); + _query_parsed = false; - return object; + return true; + } + + std::shared_ptr<Url> Url::Parse(const ov::String &url) + { + auto object = std::make_shared<Url>(); + + if (object->SetSource(url)) + { + return object; + } + + return nullptr; } bool Url::PushBackQueryKey(const ov::String &key) @@ -249,6 +277,39 @@ namespace ov return true; } + bool Url::UpdateUrl(bool is_source_updated) + { + if (is_source_updated) + { + // Get component values from the _source + return ParseFromSource(); + } + + _source = ToUrlString(); + + _path = ""; + + if (_app.IsEmpty() == false) + { + _path.Append("/"); + _path.Append(_app); + } + + if (_stream.IsEmpty() == false) + { + _path.Append("/"); + _path.Append(_stream); + } + + if (_file.IsEmpty() == false) + { + _path.Append("/"); + _path.Append(_file); + } + + return true; + } + void Url::ParseQueryIfNeeded() const { if ((_has_query_string == false) || _query_parsed) @@ -331,6 +392,8 @@ namespace ov { _source = other._source; _scheme = other._scheme; + _id = other._id; + _password = other._password; _host = other._host; _port = other._port; _path = other._path; @@ -349,6 +412,8 @@ namespace ov { _source = other._source; _scheme = other._scheme; + _id = other._id; + _password = other._password; _host = other._host; _port = other._port; _path = other._path; @@ -375,21 +440,48 @@ namespace ov ov::String Url::ToUrlString(bool include_query_string) const { - return ov::String::FormatString( - "%s://%s%s%s%s%s", - _scheme.CStr(), - _host.CStr(), (_port > 0) ? ov::String::FormatString(":%d", _port).CStr() : "", - _path.CStr(), ((include_query_string == false) || _query_string.IsEmpty()) ? "" : "?", include_query_string ? _query_string.CStr() : ""); + ov::String url; + + url.AppendFormat("%s://", _scheme.CStr()); + + if (_id.IsEmpty() == false) + { + url.Append(_id.CStr()); + + if (_password.IsEmpty()) + { + url.Append('@'); + } + } + + if (_password.IsEmpty() == false) + { + url.AppendFormat(":%s@", _password.CStr()); + } + + url.Append(_host.CStr()); + if (_port > 0) + { + url.AppendFormat(":%d", _port); + } + + url.Append(_path); + + if (include_query_string && (_query_string.IsEmpty() == false)) + { + url.Append("?"); + url.Append(_query_string); + } + + return url; } ov::String Url::ToString() const { - return ov::String::FormatString( - "%s://%s%s%s%s%s%s (app: %s, stream: %s, file: %s)", - _scheme.CStr(), - _id.IsEmpty() ? "" : ov::String::FormatString("%s:%s@", _id.CStr(), _password.CStr()).CStr(), - _host.CStr(), (_port > 0) ? ov::String::FormatString(":%d", _port).CStr() : "", - _path.CStr(), _query_string.IsEmpty() ? "" : "?", _query_string.CStr(), - _app.CStr(), _stream.CStr(), _file.CStr()); + auto url = ToUrlString(); + + url.AppendFormat(" (app: %s, stream: %s, file: %s)", _app.CStr(), _stream.CStr(), _file.CStr()); + + return url; } -} // namespace ov \ No newline at end of file +} // namespace ov diff --git a/src/projects/base/ovlibrary/url.h b/src/projects/base/ovlibrary/url.h index c773a09c9..21b033880 100644 --- a/src/projects/base/ovlibrary/url.h +++ b/src/projects/base/ovlibrary/url.h @@ -23,17 +23,17 @@ namespace ov // <scheme>://<host>[:<port>][/<path/to/resource>][?<query string>] static std::shared_ptr<Url> Parse(const ov::String &url); - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetSource, Source, _source, ) - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetScheme, Scheme, _scheme, ) - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetHost, Host, _host, ) - OV_DEFINE_SETTER_CONST_GETTER(uint32_t, SetPort, Port, _port, ) + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetSource, Source, _source, , , return UpdateUrl(true)) + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetScheme, Scheme, _scheme, , , return UpdateUrl(false)) + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetHost, Host, _host, , , return UpdateUrl(false)) + OV_DEFINE_SETTER_CONST_GETTER(uint32_t, bool, SetPort, Port, _port, , , return UpdateUrl(false)) OV_DEFINE_CONST_GETTER(ov::String, Path, _path, ) - void SetPath(const ov::String &path); - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetApp, App, _app, ) - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetStream, Stream, _stream, ) - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetFile, File, _file, ) - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetId, Id, _id, ) - OV_DEFINE_SETTER_CONST_GETTER(ov::String, SetPassword, Password, _password, ) + bool SetPath(const ov::String &path); + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetApp, App, _app, , , return UpdateUrl(false)) + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetStream, Stream, _stream, , , return UpdateUrl(false)) + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetFile, File, _file, , , return UpdateUrl(false)) + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetId, Id, _id, , , return UpdateUrl(false)) + OV_DEFINE_SETTER_CONST_GETTER(ov::String, bool, SetPassword, Password, _password, , , return UpdateUrl(false)) bool HasQueryString() const; const ov::String &Query() const; @@ -50,11 +50,15 @@ namespace ov Url &operator=(const Url &other) noexcept; - Url() =default; + Url() = default; Url(const Url &other); std::shared_ptr<Url> Clone() const; + protected: + bool ParseFromSource(); + bool UpdateUrl(bool is_source_updated); + private: void ParseQueryIfNeeded() const; @@ -66,8 +70,8 @@ namespace ov ov::String _host; uint32_t _port = 0; ov::String _path; - bool _has_query_string = false; ov::String _query_string; + bool _has_query_string = false; // To reduce the cost of parsing the query map, parsing the query only when Query() or QueryMap() is called mutable bool _query_parsed = false; mutable std::mutex _query_map_mutex; diff --git a/src/projects/config/engine/data_source.cpp b/src/projects/config/engine/data_source.cpp index 6c4357f6f..37c1838f7 100644 --- a/src/projects/config/engine/data_source.cpp +++ b/src/projects/config/engine/data_source.cpp @@ -32,253 +32,6 @@ namespace cfg } }; - DataSource::DataSource(const ov::String ¤t_path, const ov::String &file_name, const std::shared_ptr<pugi::xml_document> &document, const pugi::xml_node &node, cfg::CheckUnknownItems check_unknown_items) - : _type(DataType::Xml), - - _check_unknown_items(check_unknown_items), - - _document(document), - _node(node), - _current_file_path(current_path), - _file_name(file_name) - { - logtd("Trying to create a DataSource from XML node [%s]... (%s, cwd: %s)", node.name(), file_name.CStr(), current_path.CStr()); - } - - DataSource::DataSource(const ov::String ¤t_path, const ov::String &file_name, const ov::String json_name, const Json::Value &json, cfg::CheckUnknownItems check_unknown_items) - : _type(DataType::Json), - - _check_unknown_items(check_unknown_items), - - _json_name(json_name), - _json(json), - _current_file_path(current_path), - _file_name(file_name) - { - logtd("Trying to create a DataSource from JSON value [%s]... (%s, cwd: %s)", json_name.CStr(), file_name.CStr(), current_path.CStr()); - } - - DataSource::DataSource(DataType type, const ov::String ¤t_path, const ov::String &file_name, const ItemName &root_name, cfg::CheckUnknownItems check_unknown_items) - : _type(type), - - _check_unknown_items(check_unknown_items), - - _current_file_path(current_path) - { - _full_file_path = file_name; - - if (ov::PathManager::IsAbsolute(_full_file_path)) - { - _current_file_path.Clear(); - } - else - { - if (_current_file_path.IsEmpty() == false) - { - _full_file_path = ov::PathManager::Combine(_current_file_path, _full_file_path); - } - } - - if (_current_file_path.IsEmpty()) - { - _current_file_path = ov::PathManager::ExtractPath(_full_file_path); - } - - logtd("Trying to create a DataSource for %s from %s file: %s [cwd: %s => %s, file: %s]", - root_name.ToString().CStr(), - (type == DataType::Xml) ? "XML" : "JSON", - _full_file_path.CStr(), - current_path.CStr(), _current_file_path.CStr(), - file_name.CStr()); - - LoadFromFile(_full_file_path, root_name); - } - - DataSource::DataSource(DataType type, const ov::String &file_path, const ItemName &root_name, cfg::CheckUnknownItems check_unknown_items) - : DataSource( - type, - ov::PathManager::ExtractPath(file_path), - ov::PathManager::ExtractFileName(file_path), - root_name, - check_unknown_items) - { - } - - void DataSource::LoadFromFile(ov::String file_name, const ItemName &root_name) - { - _file_name = file_name; - - logtd("Trying to load data source from %s", file_name.CStr()); - - switch (_type) - { - case DataType::Xml: - LoadFromXmlFile(file_name, root_name.GetName(_type)); - return; - - case DataType::Json: - LoadFromJson(file_name, root_name.GetName(_type)); - return; - } - - throw CreateConfigError("Not implemented for type: %d (%s)", _type, file_name.CStr()); - } - - void DataSource::LoadFromXmlFile(const ov::String &file_name, const ov::String &root_name) - { - auto document = std::make_shared<pugi::xml_document>(); - - pugi::xml_parse_result result = document->load_file(file_name); - - if (result == false) - { - throw CreateConfigError("Could not read the file: %s (reason: %s, offset: %td)", - file_name.CStr(), result.description(), result.offset); - } - - _document = document; - _node = document->root().child(root_name); - - if (_node.empty()) - { - throw CreateConfigError("Could not find the root element: <%s> in %s", root_name.CStr(), file_name.CStr()); - } - } - - void DataSource::LoadFromJson(const ov::String &file_name, const ov::String &root_name) - { - std::ifstream json_file; - json_file.open(file_name, std::ifstream::in | std::ifstream::binary); - - if (json_file.is_open()) - { - json_file >> _json; - json_file.close(); - } - else - { - throw CreateConfigError("Could not read the file: %s", file_name.CStr()); - } - } - - void DataSource::CheckUnknownItems(const ov::String &path, - const std::unordered_map<ov::String, std::shared_ptr<Child>> &children_for_xml, - const std::unordered_map<ov::String, std::shared_ptr<Child>> &children_for_json) const - { - if (_check_unknown_items == CheckUnknownItems::DontCheck) - { - logtd("Checking unknown items is skipped: %s", path.CStr()); - return; - } - - auto file_path = GetFileName(); - - switch (_type) - { - case DataType::Xml: - for (auto &child_node : _node.children()) - { - ov::String name(child_node.name()); - - if (children_for_xml.find(name) == children_for_xml.end()) - { - if (file_path.IsEmpty()) - { - throw CreateConfigError("Unknown item found: %s.%s", path.CStr(), name.CStr()); - } - else - { - throw CreateConfigError("Unknown item found: %s.%s in %s", path.CStr(), name.CStr(), file_path.CStr()); - } - } - } - break; - - case DataType::Json: { - if (_json.isObject()) - { - auto members = _json.getMemberNames(); - - for (auto &member : members) - { - ov::String name(member.c_str()); - - if (name == "$") - { - // $ == attributes - continue; - } - - if (children_for_json.find(name) == children_for_json.end()) - { - if (file_path.IsEmpty()) - { - throw CreateConfigError("Unknown item found: %s.%s", path.CStr(), name.CStr()); - } - else - { - throw CreateConfigError("Unknown item found: %s.%s in %s", path.CStr(), name.CStr(), file_path.CStr()); - } - } - } - } - - break; - } - } - } - - bool DataSource::IsArray(const ItemName &name) const - { - switch (_type) - { - case DataType::Xml: - // if (_node) - // { - // auto iterator = _node.children(name.GetName(DataType::Json)); - // auto count = std::distance(iterator.begin(), iterator.end()); - // return count > 1; - // } - return true; - - case DataType::Json: - return _json.isArray(); - } - - OV_ASSERT2(false); - return false; - } - - Variant DataSource::GetRootValue(ValueType value_type, bool resolve_path, bool omit_json, Json::Value *original_value) const - { - switch (_type) - { - case DataType::Xml: - return GetValueFromXml(value_type, "", false, resolve_path, original_value); - - case DataType::Json: - return GetValueFromJson(value_type, "", false, resolve_path, omit_json, original_value); - } - - OV_ASSERT2(false); - return {}; - } - - Variant DataSource::GetValue(ValueType value_type, const ItemName &name, bool resolve_path, bool omit_json, Json::Value *original_value) const - { - switch (_type) - { - case DataType::Xml: - return GetValueFromXml(value_type, name.GetName(_type), true, resolve_path, original_value); - - case DataType::Json: - return GetValueFromJson(value_type, name.GetName(_type), true, resolve_path, omit_json, original_value); - } - - OV_ASSERT2(false); - return {}; - } - ov::String GetEnv(const char *key, const char *default_value, bool *is_default_value) { auto env = std::getenv(key); @@ -414,7 +167,7 @@ namespace cfg const pugi::xml_node &node, const bool resolve_path) { - const auto ignore = GetAttribute(current_file_path, node, "ignore", resolve_path); + const auto ignore = GetAttribute(current_file_path, node, "ignore", false); if (ignore.HasValue()) { @@ -426,6 +179,7 @@ namespace cfg return true; } + // Otherwise, use the value as-is } catch (const CastException &cast_exception) @@ -677,6 +431,259 @@ namespace cfg return data_sources; } + DataSource::DataSource(const ov::String ¤t_path, const ov::String &file_name, const std::shared_ptr<pugi::xml_document> &document, const pugi::xml_node &node, cfg::CheckUnknownItems check_unknown_items) + : _type(DataType::Xml), + + _check_unknown_items(check_unknown_items), + + _document(document), + _node(node), + _current_file_path(current_path), + _file_name(file_name) + { + logtd("Trying to create a DataSource from XML node [%s]... (%s, cwd: %s)", node.name(), file_name.CStr(), current_path.CStr()); + } + + DataSource::DataSource(const ov::String ¤t_path, const ov::String &file_name, const ov::String json_name, const Json::Value &json, cfg::CheckUnknownItems check_unknown_items) + : _type(DataType::Json), + + _check_unknown_items(check_unknown_items), + + _json_name(json_name), + _json(json), + _current_file_path(current_path), + _file_name(file_name) + { + logtd("Trying to create a DataSource from JSON value [%s]... (%s, cwd: %s)", json_name.CStr(), file_name.CStr(), current_path.CStr()); + } + + DataSource::DataSource(DataType type, const ov::String ¤t_path, const ov::String &file_name, const ItemName &root_name, cfg::CheckUnknownItems check_unknown_items) + : _type(type), + + _check_unknown_items(check_unknown_items), + + _current_file_path(current_path) + { + _full_file_path = file_name; + + if (ov::PathManager::IsAbsolute(_full_file_path)) + { + _current_file_path.Clear(); + } + else + { + if (_current_file_path.IsEmpty() == false) + { + _full_file_path = ov::PathManager::Combine(_current_file_path, _full_file_path); + } + } + + if (_current_file_path.IsEmpty()) + { + _current_file_path = ov::PathManager::ExtractPath(_full_file_path); + } + + logtd("Trying to create a DataSource for %s from %s file: %s [cwd: %s => %s, file: %s]", + root_name.ToString().CStr(), + (type == DataType::Xml) ? "XML" : "JSON", + _full_file_path.CStr(), + current_path.CStr(), _current_file_path.CStr(), + file_name.CStr()); + + LoadFromFile(_full_file_path, root_name); + } + + DataSource::DataSource(DataType type, const ov::String &file_path, const ItemName &root_name, cfg::CheckUnknownItems check_unknown_items) + : DataSource( + type, + ov::PathManager::ExtractPath(file_path), + ov::PathManager::ExtractFileName(file_path), + root_name, + check_unknown_items) + { + } + + void DataSource::LoadFromFile(ov::String file_name, const ItemName &root_name) + { + _file_name = file_name; + + logtd("Trying to load data source from %s", file_name.CStr()); + + switch (_type) + { + case DataType::Xml: + LoadFromXmlFile(file_name, root_name.GetName(_type)); + return; + + case DataType::Json: + LoadFromJson(file_name, root_name.GetName(_type)); + return; + } + + throw CreateConfigError("Not implemented for type: %d (%s)", _type, file_name.CStr()); + } + + void DataSource::LoadFromXmlFile(const ov::String &file_name, const ov::String &root_name) + { + auto document = std::make_shared<pugi::xml_document>(); + + pugi::xml_parse_result result = document->load_file(file_name); + + if (result == false) + { + throw CreateConfigError("Could not read the file: %s (reason: %s, offset: %td)", + file_name.CStr(), result.description(), result.offset); + } + + _document = document; + _node = document->root().child(root_name); + + if (_node.empty()) + { + throw CreateConfigError("Could not find the root element: <%s> in %s", root_name.CStr(), file_name.CStr()); + } + } + + void DataSource::LoadFromJson(const ov::String &file_name, const ov::String &root_name) + { + std::ifstream json_file; + json_file.open(file_name, std::ifstream::in | std::ifstream::binary); + + if (json_file.is_open()) + { + json_file >> _json; + json_file.close(); + } + else + { + throw CreateConfigError("Could not read the file: %s", file_name.CStr()); + } + } + + void DataSource::CheckUnknownItems(const ov::String &path, + const std::unordered_map<ov::String, std::shared_ptr<Child>> &children_for_xml, + const std::unordered_map<ov::String, std::shared_ptr<Child>> &children_for_json) const + { + if (_check_unknown_items == CheckUnknownItems::DontCheck) + { + logtd("Checking unknown items is skipped: %s", path.CStr()); + return; + } + + auto file_path = GetFileName(); + + switch (_type) + { + case DataType::Xml: + for (auto &child_node : _node.children()) + { + ov::String name(child_node.name()); + + if (children_for_xml.find(name) == children_for_xml.end()) + { + if (NeedToIgnore(file_path, child_node, false)) + { + logtw("Unknown item found, but ignored: %s.%s", path.CStr(), name.CStr()); + continue; + } + + if (file_path.IsEmpty()) + { + throw CreateConfigError("Unknown item found: %s.%s", path.CStr(), name.CStr()); + } + else + { + throw CreateConfigError("Unknown item found: %s.%s in %s", path.CStr(), name.CStr(), file_path.CStr()); + } + } + } + break; + + case DataType::Json: { + if (_json.isObject()) + { + auto members = _json.getMemberNames(); + + for (auto &member : members) + { + ov::String name(member.c_str()); + + if (name == "$") + { + // $ == attributes + continue; + } + + if (children_for_json.find(name) == children_for_json.end()) + { + if (file_path.IsEmpty()) + { + throw CreateConfigError("Unknown item found: %s.%s", path.CStr(), name.CStr()); + } + else + { + throw CreateConfigError("Unknown item found: %s.%s in %s", path.CStr(), name.CStr(), file_path.CStr()); + } + } + } + } + + break; + } + } + } + + bool DataSource::IsArray(const ItemName &name) const + { + switch (_type) + { + case DataType::Xml: + // if (_node) + // { + // auto iterator = _node.children(name.GetName(DataType::Json)); + // auto count = std::distance(iterator.begin(), iterator.end()); + // return count > 1; + // } + return true; + + case DataType::Json: + return _json.isArray(); + } + + OV_ASSERT2(false); + return false; + } + + Variant DataSource::GetRootValue(ValueType value_type, bool resolve_path, bool omit_json, Json::Value *original_value) const + { + switch (_type) + { + case DataType::Xml: + return GetValueFromXml(value_type, "", false, resolve_path, original_value); + + case DataType::Json: + return GetValueFromJson(value_type, "", false, resolve_path, omit_json, original_value); + } + + OV_ASSERT2(false); + return {}; + } + + Variant DataSource::GetValue(ValueType value_type, const ItemName &name, bool resolve_path, bool omit_json, Json::Value *original_value) const + { + switch (_type) + { + case DataType::Xml: + return GetValueFromXml(value_type, name.GetName(_type), true, resolve_path, original_value); + + case DataType::Json: + return GetValueFromJson(value_type, name.GetName(_type), true, resolve_path, omit_json, original_value); + } + + OV_ASSERT2(false); + return {}; + } + Variant DataSource::GetValueFromJson(ValueType value_type, const ov::String &name, bool is_child, bool resolve_path, bool omit_json, Json::Value *original_value) const { switch (value_type) diff --git a/src/projects/mediarouter/mediarouter_stream.cpp b/src/projects/mediarouter/mediarouter_stream.cpp index 39b2a9269..782e165e5 100644 --- a/src/projects/mediarouter/mediarouter_stream.cpp +++ b/src/projects/mediarouter/mediarouter_stream.cpp @@ -549,7 +549,7 @@ bool MediaRouteStream::ProcessAACRawStream(std::shared_ptr<MediaTrack> &media_tr return false; } - media_track->SetSampleRate(audio_config->SamplerateNum()); + media_track->SetSampleRate(audio_config->Samplerate()); media_track->GetChannel().SetLayout(audio_config->Channel() == 1 ? AudioChannel::Layout::LayoutMono : AudioChannel::Layout::LayoutStereo); media_track->SetDecoderConfigurationRecord(audio_config); @@ -611,10 +611,10 @@ bool MediaRouteStream::ProcessAACAdtsStream(std::shared_ptr<MediaTrack> &media_t auto audio_config = std::make_shared<AudioSpecificConfig>(); audio_config->SetObjectType(adts.ObjectType()); - audio_config->SetSamplingFrequency(adts.Samplerate()); + audio_config->SetSamplingFrequencyIndex(adts.SamplingFrequencyIndex()); audio_config->SetChannel(adts.ChannelConfiguration()); - media_track->SetSampleRate(audio_config->SamplerateNum()); + media_track->SetSampleRate(audio_config->Samplerate()); media_track->GetChannel().SetLayout(audio_config->Channel() == 1 ? AudioChannel::Layout::LayoutMono : AudioChannel::Layout::LayoutStereo); media_track->SetDecoderConfigurationRecord(audio_config); diff --git a/src/projects/modules/bitstream/aac/aac_adts.cpp b/src/projects/modules/bitstream/aac/aac_adts.cpp index 92488821e..edcd5680f 100755 --- a/src/projects/modules/bitstream/aac/aac_adts.cpp +++ b/src/projects/modules/bitstream/aac/aac_adts.cpp @@ -48,7 +48,7 @@ bool AACAdts::Parse(const uint8_t *data, size_t data_length, AACAdts &adts) adts._layer = parser.ReadBits<uint8_t>(2); adts._protection_absent = parser.ReadBoolBit(); adts._profile = parser.ReadBits<uint8_t>(2); - adts._sampling_frequency_index = parser.ReadBits<uint8_t>(4); + adts._sampling_frequency_index = parser.ReadBits<AacSamplingFrequencies>(4); adts._private_bit = parser.ReadBit(); adts._channel_configuration = parser.ReadBits<uint8_t>(3); adts._original_copy = parser.ReadBoolBit(); @@ -100,102 +100,14 @@ AudioObjectType AACAdts::ObjectType() return AudioObjectType::Null; } -#define OBJECT_TYPE_DESC_CASE(value, description) \ - case AudioObjectType::value: \ - return description - -ov::String AACAdts::ObjectTypeString() -{ - switch (ObjectType()) - { - OBJECT_TYPE_DESC_CASE(Null, "Null"); - OBJECT_TYPE_DESC_CASE(AacMain, "AAC main"); - OBJECT_TYPE_DESC_CASE(AacLc, "AAC LC"); - OBJECT_TYPE_DESC_CASE(AacSsr, "AAC SSR"); - OBJECT_TYPE_DESC_CASE(AacLtp, "AAC LTP"); - OBJECT_TYPE_DESC_CASE(Sbr, "SBR"); - OBJECT_TYPE_DESC_CASE(AacScalable, "AAC Scalable"); - OBJECT_TYPE_DESC_CASE(Twinvq, "TwinVQ"); - OBJECT_TYPE_DESC_CASE(Celp, "CELP"); - OBJECT_TYPE_DESC_CASE(Hvxc, "HVXC"); - OBJECT_TYPE_DESC_CASE(Reserved10, "(reserved-10)"); - OBJECT_TYPE_DESC_CASE(Reserved11, "(reserved-11)"); - OBJECT_TYPE_DESC_CASE(Ttsi, "TTSI"); - OBJECT_TYPE_DESC_CASE(MainSynthetic, "Main synthetic"); - OBJECT_TYPE_DESC_CASE(WavetableSynthesis, "Wavetable synthesis"); - OBJECT_TYPE_DESC_CASE(GeneralMidi, "General MIDI"); - OBJECT_TYPE_DESC_CASE(AlgorithmicSynthesisAndAudioFx, "Algorithmic Synthesis and Audio FX"); - OBJECT_TYPE_DESC_CASE(ErAacLc, "ER AAC LC"); - OBJECT_TYPE_DESC_CASE(Reserved18, "(reserved-18)"); - OBJECT_TYPE_DESC_CASE(ErAacLtp, "ER AAC LTP"); - OBJECT_TYPE_DESC_CASE(ErAacScalable, "ER AAC Scalable"); - OBJECT_TYPE_DESC_CASE(ErTwinvq, "ER TwinVQ"); - OBJECT_TYPE_DESC_CASE(ErBsac, "ER BSAC"); - OBJECT_TYPE_DESC_CASE(ErAacLd, "ER AAC LD"); - OBJECT_TYPE_DESC_CASE(ErCelp, "ER CELP"); - OBJECT_TYPE_DESC_CASE(ErHvxc, "ER HVXC"); - OBJECT_TYPE_DESC_CASE(ErHiln, "ER HILN"); - OBJECT_TYPE_DESC_CASE(ErParametric, "ER Parametric"); - OBJECT_TYPE_DESC_CASE(Ssc, "SSC"); - OBJECT_TYPE_DESC_CASE(Ps, "PS"); - OBJECT_TYPE_DESC_CASE(MpegSurround, "MPEG Surround"); - OBJECT_TYPE_DESC_CASE(Escape, "(escape)"); - OBJECT_TYPE_DESC_CASE(Layer1, "Layer-1"); - OBJECT_TYPE_DESC_CASE(Layer2, "Layer-2"); - OBJECT_TYPE_DESC_CASE(Layer3, "Layer-3"); - OBJECT_TYPE_DESC_CASE(Dst, "DST"); - OBJECT_TYPE_DESC_CASE(Als, "ALS"); - OBJECT_TYPE_DESC_CASE(Sls, "SLS"); - OBJECT_TYPE_DESC_CASE(SlsNonCore, "SLS non-core"); - OBJECT_TYPE_DESC_CASE(ErAacEld, "ER AAC ELD"); - OBJECT_TYPE_DESC_CASE(SmrSimple, "SMR Simple"); - OBJECT_TYPE_DESC_CASE(SmrMain, "SMR Main"); - } - - return "Unknown"; -} - -AacSamplingFrequencies AACAdts::Samplerate() +AacSamplingFrequencies AACAdts::SamplingFrequencyIndex() { - return static_cast<AacSamplingFrequencies>(_sampling_frequency_index); + return _sampling_frequency_index; } -uint32_t AACAdts::SamplerateNum() +uint32_t AACAdts::Samplerate() { - switch (Samplerate()) - { - case RATES_96000HZ: - return 96000; - case RATES_88200HZ: - return 88200; - case RATES_64000HZ: - return 64000; - case RATES_48000HZ: - return 48000; - case RATES_44100HZ: - return 44100; - case RATES_32000HZ: - return 32000; - case RATES_24000HZ: - return 24000; - case RATES_22050HZ: - return 22050; - case RATES_16000HZ: - return 16000; - case RATES_12000HZ: - return 12000; - case RATES_11025HZ: - return 11025; - case RATES_8000HZ: - return 8000; - case RATES_7350HZ: - return 7350; - case RATES_RESERVED: - case EXPLICIT_RATE: - return 0; - } - - return 0; + return GetAacSamplingFrequency(_sampling_frequency_index); } uint8_t AACAdts::ChannelConfiguration() @@ -225,8 +137,8 @@ ov::String AACAdts::GetInfoString() out_str.AppendFormat("\tId(%d)\n", Id()); out_str.AppendFormat("\tLayer(%d)\n", Layer()); out_str.AppendFormat("\tProtectionAbsent(%s)\n", ProtectionAbsent() ? "true" : "false"); - out_str.AppendFormat("\tObjectType(%d/%s)\n", ObjectType(), ObjectTypeString().CStr()); - out_str.AppendFormat("\tSamplerate(%d/%d)\n", Samplerate(), SamplerateNum()); + out_str.AppendFormat("\tObjectType(%d/%s)\n", ObjectType(), StringFromAudioObjectType(ObjectType())); + out_str.AppendFormat("\tSamplerate(%d/%d)\n", SamplingFrequencyIndex(), Samplerate()); out_str.AppendFormat("\tChannelConfiguration(%d)\n", ChannelConfiguration()); out_str.AppendFormat("\tHome(%s)\n", Home() ? "true" : "false"); out_str.AppendFormat("\tAacFrameLength(%d)\n", AacFrameLength()); diff --git a/src/projects/modules/bitstream/aac/aac_adts.h b/src/projects/modules/bitstream/aac/aac_adts.h index 49f593bec..8a369fb4a 100755 --- a/src/projects/modules/bitstream/aac/aac_adts.h +++ b/src/projects/modules/bitstream/aac/aac_adts.h @@ -19,9 +19,8 @@ class AACAdts uint8_t Layer(); bool ProtectionAbsent(); AudioObjectType ObjectType(); - ov::String ObjectTypeString(); - AacSamplingFrequencies Samplerate(); - uint32_t SamplerateNum(); + AacSamplingFrequencies SamplingFrequencyIndex(); + uint32_t Samplerate(); uint8_t ChannelConfiguration(); bool Originality(); bool Home(); @@ -35,7 +34,7 @@ class AACAdts uint8_t _layer = 0; // 2 bits (always 0) bool _protection_absent; // 1 bit (1: no CRC | 0: CRC) uint8_t _profile; // 2 bits (AacObjectType - 1) - uint8_t _sampling_frequency_index; // 4 bits (15 is forbidden) + AacSamplingFrequencies _sampling_frequency_index; // 4 bits (15 is forbidden) uint8_t _private_bit; // 1 bit (never to be used by MPEG, set 0: encoding ignore when decoding) uint8_t _channel_configuration; // 3 bits (0 : sent via an inband PCE) bool _original_copy; // 1 bit (set 0: encoding, ignore when decoding) diff --git a/src/projects/modules/bitstream/aac/aac_converter.cpp b/src/projects/modules/bitstream/aac/aac_converter.cpp index 6d09511e5..b8b0ba0d4 100755 --- a/src/projects/modules/bitstream/aac/aac_converter.cpp +++ b/src/projects/modules/bitstream/aac/aac_converter.cpp @@ -9,7 +9,7 @@ #define OV_LOG_TAG "AACConverter" -std::shared_ptr<ov::Data> AacConverter::MakeAdtsHeader(uint8_t aac_profile, uint8_t aac_sample_rate, uint8_t aac_channels, int16_t data_length) +std::shared_ptr<ov::Data> AacConverter::MakeAdtsHeader(uint8_t aac_profile, uint8_t aac_sample_rate, uint8_t aac_channels, int16_t data_length, const std::shared_ptr<ov::Data> &data) { uint8_t ADTS_HEADER_LENGTH = 7; int16_t aac_frame_length = data_length + 7; @@ -32,8 +32,12 @@ std::shared_ptr<ov::Data> AacConverter::MakeAdtsHeader(uint8_t aac_profile, uint bits.WriteBits(11, 0x3F); // adts_buffer_fullness[11b] bits.WriteBits(2, 0); // no_raw_data_blocks_inframe[2b] - std::shared_ptr<ov::Data> data = std::make_shared<ov::Data>(bits.GetData(), bits.GetDataSize()); + if (data == nullptr) + { + return std::make_shared<ov::Data>(bits.GetData(), bits.GetDataSize()); + } + data->Append(bits.GetData(), bits.GetDataSize()); return data; } @@ -73,19 +77,18 @@ std::shared_ptr<ov::Data> AacConverter::MakeAdtsHeader(uint8_t aac_profile, uint 11 (3) (reserved) AAC LTP */ -// Raw audio data msut be 1 frame +// Raw audio data must be 1 frame std::shared_ptr<ov::Data> AacConverter::ConvertRawToAdts(const uint8_t *data, size_t data_len, const AudioSpecificConfig &aac_config) { auto adts_data = std::make_shared<ov::Data>(data_len + 16); - //Get the AudioSpecificConfig value from extradata; - uint8_t aac_profile = (uint8_t)aac_config.GetAacProfile(); - uint8_t aac_sample_rate = (uint8_t)aac_config.SamplingFrequency(); - uint8_t aac_channels = (uint8_t)aac_config.Channel(); - - auto adts_header = MakeAdtsHeader(aac_profile, aac_sample_rate, aac_channels, data_len); + MakeAdtsHeader( + static_cast<uint8_t>(aac_config.GetAacProfile()), + static_cast<uint8_t>(aac_config.ProbeAacSamplingFrequencyIndex()), + aac_config.Channel(), + data_len, + adts_data); - adts_data->Append(adts_header); adts_data->Append(data, data_len); return adts_data; @@ -93,17 +96,17 @@ std::shared_ptr<ov::Data> AacConverter::ConvertRawToAdts(const uint8_t *data, si std::shared_ptr<ov::Data> AacConverter::ConvertRawToAdts(const std::shared_ptr<const ov::Data> &data, const std::shared_ptr<AudioSpecificConfig> &aac_config) { - if(aac_config == nullptr) + if (aac_config == nullptr) { return nullptr; } - + return ConvertRawToAdts(data->GetDataAs<uint8_t>(), data->GetLength(), *aac_config); } std::shared_ptr<ov::Data> AacConverter::ConvertRawToAdts(const std::shared_ptr<const ov::Data> &data, const std::shared_ptr<ov::Data> &aac_config_data) { - if(aac_config_data == nullptr) + if (aac_config_data == nullptr) { return nullptr; } @@ -211,12 +214,12 @@ std::shared_ptr<ov::Data> AacConverter::ConvertAdtsToRaw(const std::shared_ptr<c ov::String AacConverter::GetProfileString(const std::shared_ptr<AudioSpecificConfig> &aac_config) { - if(aac_config == nullptr) + if (aac_config == nullptr) { return ""; } - return ov::String::FormatString("%d", static_cast<int>(aac_config->ObjectType())); + return ov::String::FormatString("%d", static_cast<int>(aac_config->ObjectType())); } ov::String AacConverter::GetProfileString(const std::shared_ptr<ov::Data> &aac_config_data) diff --git a/src/projects/modules/bitstream/aac/aac_converter.h b/src/projects/modules/bitstream/aac/aac_converter.h index fac27fc28..92c11a078 100755 --- a/src/projects/modules/bitstream/aac/aac_converter.h +++ b/src/projects/modules/bitstream/aac/aac_converter.h @@ -1,10 +1,11 @@ #pragma once #include <base/ovlibrary/ovlibrary.h> -#include "base/mediarouter/media_buffer.h" -#include "audio_specific_config.h" #include <stdint.h> +#include "audio_specific_config.h" +#include "base/mediarouter/media_buffer.h" + // Default = AacObjectTypeAacLC #define AAC_CONVERTER_DEFAULT_PROFILE "2" @@ -19,5 +20,5 @@ class AacConverter static ov::String GetProfileString(const std::shared_ptr<AudioSpecificConfig> &aac_config); static ov::String GetProfileString(const std::shared_ptr<ov::Data> &aac_config_data); - static std::shared_ptr<ov::Data> MakeAdtsHeader(uint8_t aac_profile, uint8_t aac_sample_rate, uint8_t aac_channels, int16_t data_length); + static std::shared_ptr<ov::Data> MakeAdtsHeader(uint8_t aac_profile, uint8_t aac_sample_rate, uint8_t aac_channels, int16_t data_length, const std::shared_ptr<ov::Data> &data = nullptr); }; diff --git a/src/projects/modules/bitstream/aac/aac_defines.cpp b/src/projects/modules/bitstream/aac/aac_defines.cpp new file mode 100644 index 000000000..366b1a8f6 --- /dev/null +++ b/src/projects/modules/bitstream/aac/aac_defines.cpp @@ -0,0 +1,91 @@ +#include "aac_defines.h" + +#define OBJECT_TYPE_DESC_CASE(value, description) \ + case AudioObjectType::value: \ + return description + +const char *StringFromAudioObjectType(AudioObjectType object_type) +{ + switch (object_type) + { + OBJECT_TYPE_DESC_CASE(Null, "Null"); + OBJECT_TYPE_DESC_CASE(AacMain, "AAC main"); + OBJECT_TYPE_DESC_CASE(AacLc, "AAC LC"); + OBJECT_TYPE_DESC_CASE(AacSsr, "AAC SSR"); + OBJECT_TYPE_DESC_CASE(AacLtp, "AAC LTP"); + OBJECT_TYPE_DESC_CASE(Sbr, "SBR"); + OBJECT_TYPE_DESC_CASE(AacScalable, "AAC Scalable"); + OBJECT_TYPE_DESC_CASE(Twinvq, "TwinVQ"); + OBJECT_TYPE_DESC_CASE(Celp, "CELP"); + OBJECT_TYPE_DESC_CASE(Hvxc, "HVXC"); + OBJECT_TYPE_DESC_CASE(Reserved10, "(reserved-10)"); + OBJECT_TYPE_DESC_CASE(Reserved11, "(reserved-11)"); + OBJECT_TYPE_DESC_CASE(Ttsi, "TTSI"); + OBJECT_TYPE_DESC_CASE(MainSynthetic, "Main synthetic"); + OBJECT_TYPE_DESC_CASE(WavetableSynthesis, "Wavetable synthesis"); + OBJECT_TYPE_DESC_CASE(GeneralMidi, "General MIDI"); + OBJECT_TYPE_DESC_CASE(AlgorithmicSynthesisAndAudioFx, "Algorithmic Synthesis and Audio FX"); + OBJECT_TYPE_DESC_CASE(ErAacLc, "ER AAC LC"); + OBJECT_TYPE_DESC_CASE(Reserved18, "(reserved-18)"); + OBJECT_TYPE_DESC_CASE(ErAacLtp, "ER AAC LTP"); + OBJECT_TYPE_DESC_CASE(ErAacScalable, "ER AAC Scalable"); + OBJECT_TYPE_DESC_CASE(ErTwinvq, "ER TwinVQ"); + OBJECT_TYPE_DESC_CASE(ErBsac, "ER BSAC"); + OBJECT_TYPE_DESC_CASE(ErAacLd, "ER AAC LD"); + OBJECT_TYPE_DESC_CASE(ErCelp, "ER CELP"); + OBJECT_TYPE_DESC_CASE(ErHvxc, "ER HVXC"); + OBJECT_TYPE_DESC_CASE(ErHiln, "ER HILN"); + OBJECT_TYPE_DESC_CASE(ErParametric, "ER Parametric"); + OBJECT_TYPE_DESC_CASE(Ssc, "SSC"); + OBJECT_TYPE_DESC_CASE(Ps, "PS"); + OBJECT_TYPE_DESC_CASE(MpegSurround, "MPEG Surround"); + OBJECT_TYPE_DESC_CASE(Escape, "(escape)"); + OBJECT_TYPE_DESC_CASE(Layer1, "Layer-1"); + OBJECT_TYPE_DESC_CASE(Layer2, "Layer-2"); + OBJECT_TYPE_DESC_CASE(Layer3, "Layer-3"); + OBJECT_TYPE_DESC_CASE(Dst, "DST"); + OBJECT_TYPE_DESC_CASE(Als, "ALS"); + OBJECT_TYPE_DESC_CASE(Sls, "SLS"); + OBJECT_TYPE_DESC_CASE(SlsNonCore, "SLS non-core"); + OBJECT_TYPE_DESC_CASE(ErAacEld, "ER AAC ELD"); + OBJECT_TYPE_DESC_CASE(SmrSimple, "SMR Simple"); + OBJECT_TYPE_DESC_CASE(SmrMain, "SMR Main"); + } + + return "Unknown"; +} + +#define _SAMPLE_RATE_CASE(rate, value) \ + case AacSamplingFrequencies::rate: \ + return value; + +uint32_t GetAacSamplingFrequency(AacSamplingFrequencies sampling_frequency_index) +{ + switch (sampling_frequency_index) + { + _SAMPLE_RATE_CASE(_96000, 96000); + _SAMPLE_RATE_CASE(_88200, 88200); + _SAMPLE_RATE_CASE(_64000, 64000); + _SAMPLE_RATE_CASE(_48000, 48000); + _SAMPLE_RATE_CASE(_44100, 44100); + _SAMPLE_RATE_CASE(_32000, 32000); + _SAMPLE_RATE_CASE(_24000, 24000); + _SAMPLE_RATE_CASE(_22050, 22050); + _SAMPLE_RATE_CASE(_16000, 16000); + _SAMPLE_RATE_CASE(_12000, 12000); + _SAMPLE_RATE_CASE(_11025, 11025); + _SAMPLE_RATE_CASE(_8000, 8000); + _SAMPLE_RATE_CASE(_7350, 7350); + case AacSamplingFrequencies::RESERVED1: + [[fallthrough]]; + case AacSamplingFrequencies::RESERVED2: + break; + + case AacSamplingFrequencies::ESCAPE_VALUE: + // ESCAPE_VALUE must be handled in the if statement above + OV_ASSERT2(false); + break; + } + + return 0; +} diff --git a/src/projects/modules/bitstream/aac/aac_defines.h b/src/projects/modules/bitstream/aac/aac_defines.h new file mode 100644 index 000000000..487c02178 --- /dev/null +++ b/src/projects/modules/bitstream/aac/aac_defines.h @@ -0,0 +1,104 @@ +#pragma once + +#include <base/ovlibrary/ovlibrary.h> + +// Table 1.3 – Audio Profiles definition +// @see ISO/IEC 14496-3 (2009), Information technology - Coding of audio-visual objects - Part 3: Audio +enum class AudioObjectType : uint8_t +{ + Null = 0, // Null + AacMain = 1, // Main + AacLc = 2, // Low Complexity + AacSsr = 3, // Scalable Sample Rate + AacLtp = 4, // Long Term Predictor + Sbr = 5, // SBR Spectral Band Replication + AacScalable = 6, // AAC Scalable + Twinvq = 7, // Twin VQ Vector Quantizer + Celp = 8, // Code Excited Linear Prediction + Hvxc = 9, // Harmonic Vector eXcitation Coding + Reserved10 = 10, // (reserved) + Reserved11 = 11, // (reserved) + Ttsi = 12, // Text to Speech Interface + MainSynthetic = 13, // Main Synthetic + WavetableSynthesis = 14, // Wavetable Synthesis + GeneralMidi = 15, // General MIDI + AlgorithmicSynthesisAndAudioFx = 16, // Algorithmic Synthesis and Audio FX + ErAacLc = 17, // Error Resilient (ER) AAC Low Complexity (LC) + Reserved18 = 18, // (reserved) + ErAacLtp = 19, // Error Resilient (ER) AAC Long Term Predictor (LTP) + ErAacScalable = 20, // Error Resilient (ER) AAC scalable + ErTwinvq = 21, // Error Resilient (ER) TwinVQ + ErBsac = 22, // Error Resilient (ER) Bit Sliced Arithmetic Coding + ErAacLd = 23, // Error Resilient (ER) AAC Low Delay + ErCelp = 24, // Error Resilient (ER) Code Excited Linear Prediction + ErHvxc = 25, // Error Resilient (ER) Harmonic Vector eXcitation Coding + ErHiln = 26, // Error Resilient (ER) Harmonic and Individual Lines plus Noise + ErParametric = 27, // Error Resilient (ER) Parametric + Ssc = 28, // SinuSoidal Coding + Ps = 29, // Parametric Stereo + MpegSurround = 30, // MPEG Surround + Escape = 31, // (escape) + Layer1 = 32, // Layer-1 Audio + Layer2 = 33, // Layer-2 Audio + Layer3 = 34, // Layer-3 Audio + Dst = 35, // Direct Stream Transfer + Als = 36, // Audio Lossless Coding + Sls = 37, // Scalable Lossless Coding + SlsNonCore = 38, // Scalable Lossless Non-Core Audio + ErAacEld = 39, // Error Resilient (ER) AAC Enhanced Low Delay + SmrSimple = 40, // Symbolic Music Representation Simple + SmrMain = 41, // Symbolic Music Representation Main +}; +const char *StringFromAudioObjectType(AudioObjectType object_type); + +enum class AacProfile : uint8_t +{ + Reserved = 3, + + // @see 7.1 Profiles, aac-iso-13818-7.pdf, page 40 + Main = 0, + LC = 1, + SSR = 2 +}; + +// Table 1.18 – Sampling Frequency Index +// +// | samplingFrequencyIndex | Value | +// +------------------------+--------------| +// | 0x0 | 96000 | +// | 0x1 | 88200 | +// | 0x2 | 64000 | +// | 0x3 | 48000 | +// | 0x4 | 44100 | +// | 0x5 | 32000 | +// | 0x6 | 24000 | +// | 0x7 | 22050 | +// | 0x8 | 16000 | +// | 0x9 | 12000 | +// | 0xa | 11025 | +// | 0xb | 8000 | +// | 0xc | 7350 | +// | 0xd | reserved | +// | 0xe | reserved | +// | 0xf | escape value | +// +enum class AacSamplingFrequencies : uint8_t +{ + _96000 = 0, + _88200 = 1, + _64000 = 2, + _48000 = 3, + _44100 = 4, + _32000 = 5, + _24000 = 6, + _22050 = 7, + _16000 = 8, + _12000 = 9, + _11025 = 10, + _8000 = 11, + _7350 = 12, + RESERVED1 = 13, + RESERVED2 = 14, + ESCAPE_VALUE = 15 +}; +uint32_t GetAacSamplingFrequency(AacSamplingFrequencies sampling_frequency_index); diff --git a/src/projects/modules/bitstream/aac/audio_specific_config.cpp b/src/projects/modules/bitstream/aac/audio_specific_config.cpp index 776c6bba5..db48f1d59 100644 --- a/src/projects/modules/bitstream/aac/audio_specific_config.cpp +++ b/src/projects/modules/bitstream/aac/audio_specific_config.cpp @@ -7,16 +7,24 @@ #define OV_LOG_TAG "AACSpecificConfig" -bool AudioSpecificConfig::IsValid() const -{ - if (_object_type == AudioObjectType::Null || - _sampling_frequency_index == AacSamplingFrequencies::RATES_RESERVED || - _channel == 15) - { - return false; +#define _SET_PROBED_FREQUENCY_INDEX(frequency, max, min, index) \ + if ((max > frequency) && (frequency >= min)) \ + { \ + _probed_sampling_frequency_index = AacSamplingFrequencies::index; \ + break; \ } - return true; +#define _RETURN_IF_FAIL(expression) \ + if (expression == false) \ + { \ + return false; \ + } + +bool AudioSpecificConfig::IsValid() const +{ + return (_audio_object_type != AudioObjectType::Null) && + ((_sampling_frequency_index < AacSamplingFrequencies::RESERVED1) || (_probed_sampling_frequency_index < AacSamplingFrequencies::RESERVED1)) && + (_channel_configuration < 15); } ov::String AudioSpecificConfig::GetCodecsParameter() const @@ -30,10 +38,177 @@ ov::String AudioSpecificConfig::GetCodecsParameter() const // // OTN == profile_number // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#MPEG-4_audio - return ov::String::FormatString("mp4a.40.%d", static_cast<int>(_object_type)); + return ov::String::FormatString("mp4a.40.%d", static_cast<int>(_audio_object_type)); +} + +bool AudioSpecificConfig::GetAudioObjectType(BitReader &reader, AudioObjectType &audio_object_type) const +{ + // Table 1.16 - Syntax of GetAudioObjectType() + // GetAudioObjectType() + // { + // audioObjectType; 5 uimsbf + // if (audioObjectType == 31) { + // audioObjectType = 32 + audioObjectTypeExt; 6 uimsbf + // } + // return audioObjectType; + // } + _RETURN_IF_FAIL(reader.ReadBits<AudioObjectType>(5, audio_object_type)); + if (audio_object_type == AudioObjectType::Escape) + { + uint8_t audio_object_type_ext; + _RETURN_IF_FAIL(reader.ReadBits<uint8_t>(6, audio_object_type_ext)); + + audio_object_type = static_cast<AudioObjectType>(32 + audio_object_type_ext); + } + + return true; +} + +uint32_t AudioSpecificConfig::CalculateFrameLength(AudioObjectType audio_object_type, bool frame_length_flag) const +{ + // frameLengthFlag + // + // Length of the frame, number of spectral lines, respective. + // For all General Audio Object Types except AAC SSR and ER AAC LD: + // If set to "0" a 1024/128 lines IMDCT is used and frameLength is set to + // 1024, if set to "1" a 960/120 line IMDCT is used and frameLength is set + // to 960. + // For ER AAC LD: If set to "0" a 512 lines IMDCT is used and + // frameLength is set to 512, if set to "1" a 480 line IMDCT is used and + // frameLength is set to 480. + // For AAC SSR: Must be set to "0". A 256/32 lines IMDCT (first or second value) + // is distinguished by the value of window_sequence. + switch (audio_object_type) + { + // For all General Audio Object Types except AAC SSR and ER AAC LD + default: + return frame_length_flag ? 960 : 1024; + + // For ER AAC LD + case AudioObjectType::ErAacLd: + return frame_length_flag ? 480 : 512; + + // For AAC SSR + case AudioObjectType::AacSsr: + OV_ASSERT(frame_length_flag == 0, "AAC SSR must be set to 0"); + + // TODO: need to parse window_sequence +#ifdef DEBUG + logtw("AAC SSR is not supported yet"); +#endif // DEBUG + + return 256; + } +} + +bool AudioSpecificConfig::GASpecificConfig(BitReader &reader) +{ + // Table 4.1 - Syntax of GASpecificConfig() + // + // GASpecificConfig (samplingFrequencyIndex, + // channelConfiguration, + // audioObjectType) + // + // { + // frameLengthFlag; 1 bslbf + // dependsOnCoreCoder; 1 bslbf + // if (dependsOnCoreCoder) { + // coreCoderDelay; 14 uimsbf + // } + // extensionFlag; 1 bslbf + // if (! channelConfiguration) { + // program_config_element (); + // } + // if ((audioObjectType == 6) || (audioObjectType == 20)) { + // layerNr; 3 uimsbf + // } + // if (extensionFlag) { + // if (audioObjectType == 22) { + // numOfSubFrame; 5 bslbf + // layer_length; 11 bslbf + // } + // if (audioObjectType == 17 || audioObjectType == 19 || + // audioObjectType == 20 || audioObjectType == 23) { + // aacSectionDataResilienceFlag; 1 bslbf + // aacScalefactorDataResilienceFlag; 1 bslbf + // aacSpectralDataResilienceFlag; 1 bslbf + // } + // extensionFlag3; 1 bslbf + // if (extensionFlag3) { + // /* tbd in version 3 */ + // } + // } + // } + + bool frame_length_flag; + _RETURN_IF_FAIL(reader.ReadBit(frame_length_flag)); + + _frame_length = CalculateFrameLength(_audio_object_type, frame_length_flag); + + uint8_t depends_on_core_coder; + _RETURN_IF_FAIL(reader.ReadBits(1, depends_on_core_coder)); + + if (depends_on_core_coder) + { + [[maybe_unused]] uint16_t core_coder_delay; + _RETURN_IF_FAIL(reader.ReadBits(14, core_coder_delay)); + } + + uint8_t extension_flag; + _RETURN_IF_FAIL(reader.ReadBits(1, extension_flag)); + + if (_channel_configuration == 0) + { + // Not implemented + // program_config_element(); + } + + if ( + (_audio_object_type == AudioObjectType::AacScalable) || + (_audio_object_type == AudioObjectType::ErAacScalable)) + { + [[maybe_unused]] uint8_t layer_nr; + _RETURN_IF_FAIL(reader.ReadBits(3, layer_nr)); + } + + if (extension_flag) + { + if (_audio_object_type == AudioObjectType::ErBsac) + { + [[maybe_unused]] uint8_t num_of_sub_frame; + _RETURN_IF_FAIL(reader.ReadBits(5, num_of_sub_frame)); + + [[maybe_unused]] uint16_t layer_length; + _RETURN_IF_FAIL(reader.ReadBits(11, layer_length)); + } + else if ((_audio_object_type == AudioObjectType::ErAacLc) || + (_audio_object_type == AudioObjectType::ErAacLtp) || + (_audio_object_type == AudioObjectType::ErAacScalable) || + (_audio_object_type == AudioObjectType::ErAacLd)) + { + uint8_t aac_section_data_resilience_flag; + _RETURN_IF_FAIL(reader.ReadBits(1, aac_section_data_resilience_flag)); + + uint8_t aac_scalefactor_data_resilience_flag; + _RETURN_IF_FAIL(reader.ReadBits(1, aac_scalefactor_data_resilience_flag)); + + uint8_t aac_spectral_data_resilience_flag; + _RETURN_IF_FAIL(reader.ReadBits(1, aac_spectral_data_resilience_flag)); + } + + uint8_t extension_flag3; + _RETURN_IF_FAIL(reader.ReadBits(1, extension_flag3)); + + if (extension_flag3) + { + // /* tbd in version 3 */ + } + } + + return true; } -bool AudioSpecificConfig::Parse(const std::shared_ptr<ov::Data> &data) +bool AudioSpecificConfig::Parse(const std::shared_ptr<const ov::Data> &data) { if (data->GetLength() < MIN_AAC_SPECIFIC_CONFIG_SIZE) { @@ -41,11 +216,166 @@ bool AudioSpecificConfig::Parse(const std::shared_ptr<ov::Data> &data) return false; } - BitReader parser(data->GetDataAs<uint8_t>(), data->GetLength()); + BitReader reader(data->GetDataAs<uint8_t>(), data->GetLength()); + + // audioObjectType = GetAudioObjectType(); + if (GetAudioObjectType(reader, _audio_object_type) == false) + { + return false; + } + + // samplingFrequencyIndex; 4 bslbf + // if ( samplingFrequencyIndex == 0xf ) { + // samplingFrequency; 24 uimsbf + // } + _RETURN_IF_FAIL(reader.ReadBits(4, _sampling_frequency_index)); + + if (_sampling_frequency_index == AacSamplingFrequencies::ESCAPE_VALUE) + { + _RETURN_IF_FAIL(reader.ReadBits(24, _sampling_frequency)); + + do + { + // Table 4.82 - Sampling frequency mapping + // + // +-------------------------+-------------------------------------------+ + // | Frequency range (in Hz) | Use tables for sampling frequency (in Hz) | + // +-------------------------+-------------------------------------------+ + // | f >= 92017 | 96000 | + // | 92017 > f >= 75132 | 88200 | + // | 75132 > f >= 55426 | 64000 | + // | 55426 > f >= 46009 | 48000 | + // | 46009 > f >= 37566 | 44100 | + // | 37566 > f >= 27713 | 32000 | + // | 27713 > f >= 23004 | 24000 | + // | 23004 > f >= 18783 | 22050 | + // | 18783 > f >= 13856 | 16000 | + // | 13856 > f >= 11502 | 12000 | + // | 11502 > f >= 9391 | 11025 | + // | 9391 > f | 8000 | + // +-------------------------+-------------------------------------------+ + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, UINT32_MAX, 92017, _96000); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 92016, 75132, _88200); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 75131, 55426, _64000); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 55425, 46009, _48000); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 46008, 37566, _44100); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 37565, 27713, _32000); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 27712, 23004, _24000); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 23003, 18783, _22050); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 18782, 13856, _16000); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 13855, 11502, _12000); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 11501, 9391, _11025); + _SET_PROBED_FREQUENCY_INDEX(_sampling_frequency, 9390, 0, _8000); + } while (false); + } + else + { + SetSamplingFrequencyIndex(_sampling_frequency_index); + } - _object_type = static_cast<AudioObjectType>(parser.ReadBits<uint8_t>(5)); - _sampling_frequency_index = static_cast<AacSamplingFrequencies>(parser.ReadBits<uint8_t>(4)); - _channel = parser.ReadBits<uint8_t>(4); + // channelConfiguration; 4 bslbf + _RETURN_IF_FAIL(reader.ReadBits(4, _channel_configuration)); + // sbrPresentFlag = -1; + // psPresentFlag = -1; + // if ( audioObjectType == 5 || + // audioObjectType == 29 ) { + // extensionAudioObjectType = 5; + // sbrPresentFlag = 1; + // if ( audioObjectType == 29 ) { + // psPresentFlag = 1; + // } + // extensionSamplingFrequencyIndex; 4 uimsbf + // if ( extensionSamplingFrequencyIndex == 0xf ) + // extensionSamplingFrequency; 24 uimsbf + // audioObjectType = GetAudioObjectType(); + // if ( audioObjectType == 22 ) + // extensionChannelConfiguration; 4 uimsbf + // } + // else { + // extensionAudioObjectType = 0; + // } + // int32_t sbrPresentFlag = -1; + // int32_t psPresentFlag = -1; + // AudioObjectType extensionAudioObjectType; + + if ((_audio_object_type == AudioObjectType::Sbr) || (_audio_object_type == AudioObjectType::Ps)) + { + // extensionAudioObjectType = AudioObjectType::Sbr; + // sbrPresentFlag = 1; + // if (audioObjectType == AudioObjectType::Ps) { + // psPresentFlag = 1; + // } + + AacSamplingFrequencies extensionSamplingFrequencyIndex; + _RETURN_IF_FAIL(reader.ReadBits(4, extensionSamplingFrequencyIndex)); + if (extensionSamplingFrequencyIndex == AacSamplingFrequencies::ESCAPE_VALUE) + { + [[maybe_unused]] uint32_t extensionSamplingFrequency; + _RETURN_IF_FAIL(reader.ReadBits(24, _sampling_frequency)); + } + + _RETURN_IF_FAIL(GetAudioObjectType(reader, _audio_object_type)); + if (_audio_object_type == AudioObjectType::ErBsac) + { + [[maybe_unused]] uint8_t extensionChannelConfiguration; + _RETURN_IF_FAIL(reader.ReadBits(4, extensionChannelConfiguration)); + } + } + else + { + // extensionAudioObjectType = AudioObjectType::Null; + } + + // switch (audioObjectType) { + // case 1: + // case 2: + // case 3: + // case 4: + // case 6: + // case 7: + // case 17: + // case 19: + // case 20: + // case 21: + // case 22: + // case 23: + // + // GASpecificConfig(); + // break: + switch (_audio_object_type) + { + case AudioObjectType::AacMain: + [[fallthrough]]; + case AudioObjectType::AacLc: + [[fallthrough]]; + case AudioObjectType::AacSsr: + [[fallthrough]]; + case AudioObjectType::AacLtp: + [[fallthrough]]; + case AudioObjectType::AacScalable: + [[fallthrough]]; + case AudioObjectType::Twinvq: + [[fallthrough]]; + case AudioObjectType::ErAacLc: + [[fallthrough]]; + case AudioObjectType::ErAacLtp: + [[fallthrough]]; + case AudioObjectType::ErAacScalable: + [[fallthrough]]; + case AudioObjectType::ErTwinvq: + [[fallthrough]]; + case AudioObjectType::ErBsac: + [[fallthrough]]; + case AudioObjectType::ErAacLd: + GASpecificConfig(reader); + break; + + default: + break; + } + + // Set serialized data + SetData(data->Clone()); return true; } @@ -68,7 +398,7 @@ bool AudioSpecificConfig::Equals(const std::shared_ptr<DecoderConfigurationRecor return false; } - if (SamplingFrequency() != other_config->SamplingFrequency()) + if (SamplingFrequencyIndex() != other_config->SamplingFrequencyIndex()) { return false; } @@ -81,88 +411,81 @@ bool AudioSpecificConfig::Equals(const std::shared_ptr<DecoderConfigurationRecor return true; } -std::shared_ptr<ov::Data> AudioSpecificConfig::Serialize() +// This is not used after Parse() is called. It is only used when values are set with Setter. +std::shared_ptr<const ov::Data> AudioSpecificConfig::Serialize() { ov::BitWriter bits(2); - bits.WriteBits(5, ov::ToUnderlyingType(_object_type)); - bits.WriteBits(4, _sampling_frequency_index); - bits.WriteBits(4, _channel); + bits.WriteBits(5, ov::ToUnderlyingType(_audio_object_type)); + bits.WriteBits(4, ov::ToUnderlyingType(_sampling_frequency_index)); + bits.WriteBits(4, _channel_configuration); + // frame_length : only support 1024 + bits.WriteBits(1, 0); + // dependsOnCoreCoder + bits.WriteBits(1, 0); + // extensionFlag + bits.WriteBits(1, 0); return std::make_shared<ov::Data>(bits.GetData(), bits.GetDataSize()); } AudioObjectType AudioSpecificConfig::ObjectType() const { - return _object_type; + return _audio_object_type; } void AudioSpecificConfig::SetObjectType(AudioObjectType object_type) { - _object_type = object_type; -} - -uint32_t AudioSpecificConfig::SamplerateNum() const -{ - switch (SamplingFrequency()) - { - case RATES_96000HZ: - return 96000; - case RATES_88200HZ: - return 88200; - case RATES_64000HZ: - return 64000; - case RATES_48000HZ: - return 48000; - case RATES_44100HZ: - return 44100; - case RATES_32000HZ: - return 32000; - case RATES_24000HZ: - return 24000; - case RATES_22050HZ: - return 22050; - case RATES_16000HZ: - return 16000; - case RATES_12000HZ: - return 12000; - case RATES_11025HZ: - return 11025; - case RATES_8000HZ: - return 8000; - case RATES_7350HZ: - return 7350; - case RATES_RESERVED: - case EXPLICIT_RATE: - return 0; - } - - return 0; -} - -AacSamplingFrequencies AudioSpecificConfig::SamplingFrequency() const + _audio_object_type = object_type; +} + +uint32_t AudioSpecificConfig::Samplerate() const +{ + return _sampling_frequency; +} + +AacSamplingFrequencies AudioSpecificConfig::SamplingFrequencyIndex() const { return _sampling_frequency_index; } -void AudioSpecificConfig::SetSamplingFrequency(AacSamplingFrequencies sampling_frequency_index) +AacSamplingFrequencies AudioSpecificConfig::ProbeAacSamplingFrequencyIndex() const +{ + // If _sampling_frequency_index is valid, return it. + if (_sampling_frequency_index < AacSamplingFrequencies::RESERVED1) + { + return _sampling_frequency_index; + } + + // Return the probed frequency index + return _probed_sampling_frequency_index; +} + +void AudioSpecificConfig::SetSamplingFrequencyIndex(AacSamplingFrequencies sampling_frequency_index) { _sampling_frequency_index = sampling_frequency_index; + _probed_sampling_frequency_index = AacSamplingFrequencies::ESCAPE_VALUE; + _sampling_frequency = GetAacSamplingFrequency(_sampling_frequency_index); } uint8_t AudioSpecificConfig::Channel() const { - return _channel; + return _channel_configuration; } void AudioSpecificConfig::SetChannel(uint8_t channel) { - _channel = channel; + _channel_configuration = channel; +} + +uint32_t AudioSpecificConfig::FrameLength() const +{ + return _frame_length; } AacProfile AudioSpecificConfig::GetAacProfile() const { - switch (_object_type) + switch (_audio_object_type) { case AudioObjectType::AacMain: return AacProfile::Main; @@ -184,8 +507,8 @@ ov::String AudioSpecificConfig::GetInfoString() const { ov::String out_str = ov::String::FormatString("\n[AudioSpecificConfig]\n"); - out_str.AppendFormat("\tObjectType(%d)\n", ObjectType()); - out_str.AppendFormat("\tSamplingFrequency(%d)\n", SamplingFrequency()); + out_str.AppendFormat("\tObjectType(%d, %s)\n", ObjectType(), StringFromAudioObjectType(ObjectType())); + out_str.AppendFormat("\tSamplingFrequency(%d)\n", SamplingFrequencyIndex()); out_str.AppendFormat("\tChannel(%d)\n", Channel()); return out_str; diff --git a/src/projects/modules/bitstream/aac/audio_specific_config.h b/src/projects/modules/bitstream/aac/audio_specific_config.h index 0ccb830ff..4ce04fe6b 100644 --- a/src/projects/modules/bitstream/aac/audio_specific_config.h +++ b/src/projects/modules/bitstream/aac/audio_specific_config.h @@ -87,113 +87,53 @@ #include <base/info/decoder_configuration_record.h> #include <base/ovlibrary/ovlibrary.h> -// Table 1.3 – Audio Profiles definition -// @see ISO/IEC 14496-3 (2009), Information technology - Coding of audio-visual objects - Part 3: Audio -enum class AudioObjectType : uint8_t -{ - Null = 0, // Null - AacMain = 1, // Main - AacLc = 2, // Low Complexity - AacSsr = 3, // Scalable Sample Rate - AacLtp = 4, // Long Term Predictor - Sbr = 5, // SBR Spectral Band Replication - AacScalable = 6, // AAC Scalable - Twinvq = 7, // Twin VQ Vector Quantizer - Celp = 8, // Code Excited Linear Prediction - Hvxc = 9, // Harmonic Vector eXcitation Coding - Reserved10 = 10, // (reserved) - Reserved11 = 11, // (reserved) - Ttsi = 12, // Text to Speech Interface - MainSynthetic = 13, // Main Synthetic - WavetableSynthesis = 14, // Wavetable Synthesis - GeneralMidi = 15, // General MIDI - AlgorithmicSynthesisAndAudioFx = 16, // Algorithmic Synthesis and Audio FX - ErAacLc = 17, // Error Resilient (ER) AAC Low Complexity (LC) - Reserved18 = 18, // (reserved) - ErAacLtp = 19, // Error Resilient (ER) AAC Long Term Predictor (LTP) - ErAacScalable = 20, // Error Resilient (ER) AAC scalable - ErTwinvq = 21, // Error Resilient (ER) TwinVQ - ErBsac = 22, // Error Resilient (ER) Bit Sliced Arithmetic Coding - ErAacLd = 23, // Error Resilient (ER) AAC Low Delay - ErCelp = 24, // Error Resilient (ER) Code Excited Linear Prediction - ErHvxc = 25, // Error Resilient (ER) Harmonic Vector eXcitation Coding - ErHiln = 26, // Error Resilient (ER) Harmonic and Individual Lines plus Noise - ErParametric = 27, // Error Resilient (ER) Parametric - Ssc = 28, // SinuSoidal Coding - Ps = 29, // Parametric Stereo - MpegSurround = 30, // MPEG Surround - Escape = 31, // (escape) - Layer1 = 32, // Layer-1 Audio - Layer2 = 33, // Layer-2 Audio - Layer3 = 34, // Layer-3 Audio - Dst = 35, // Direct Stream Transfer - Als = 36, // Audio Lossless Coding - Sls = 37, // Scalable Lossless Coding - SlsNonCore = 38, // Scalable Lossless Non-Core Audio - ErAacEld = 39, // Error Resilient (ER) AAC Enhanced Low Delay - SmrSimple = 40, // Symbolic Music Representation Simple - SmrMain = 41, // Symbolic Music Representation Main -}; - -enum class AacProfile : uint8_t -{ - Reserved = 3, - - // @see 7.1 Profiles, aac-iso-13818-7.pdf, page 40 - Main = 0, - LC = 1, - SSR = 2 -}; - -enum AacSamplingFrequencies : uint8_t -{ - RATES_96000HZ = 0, - RATES_88200HZ = 1, - RATES_64000HZ = 2, - RATES_48000HZ = 3, - RATES_44100HZ = 4, - RATES_32000HZ = 5, - RATES_24000HZ = 6, - RATES_22050HZ = 7, - RATES_16000HZ = 8, - RATES_12000HZ = 9, - RATES_11025HZ = 10, - RATES_8000HZ = 11, - RATES_7350HZ = 12, - RATES_RESERVED = 13, - EXPLICIT_RATE = 15 -}; +#include "aac_defines.h" #define MIN_AAC_SPECIFIC_CONFIG_SIZE 2 +// Based on ISO14496-3 class AudioSpecificConfig : public DecoderConfigurationRecord { public: bool IsValid() const override; // Instance can be initialized by putting raw data in AudioSpecificConfig. - bool Parse(const std::shared_ptr<ov::Data> &data) override; + bool Parse(const std::shared_ptr<const ov::Data> &data) override; bool Equals(const std::shared_ptr<DecoderConfigurationRecord> &other) override; - std::shared_ptr<ov::Data> Serialize() override; + std::shared_ptr<const ov::Data> Serialize() override; AudioObjectType ObjectType() const; - AacSamplingFrequencies SamplingFrequency() const; - uint32_t SamplerateNum() const; + AacSamplingFrequencies SamplingFrequencyIndex() const; + AacSamplingFrequencies ProbeAacSamplingFrequencyIndex() const; + uint32_t Samplerate() const; uint8_t Channel() const; + uint32_t FrameLength() const; + AacProfile GetAacProfile() const; ov::String GetInfoString() const; void SetObjectType(AudioObjectType object_type); - void SetSamplingFrequency(AacSamplingFrequencies sampling_frequency_index); + void SetSamplingFrequencyIndex(AacSamplingFrequencies sampling_frequency_index); void SetChannel(uint8_t channel); // Helpers ov::String GetCodecsParameter() const; +protected: + // Table 1.16 - Syntax of GetAudioObjectType() + bool GetAudioObjectType(BitReader &reader, AudioObjectType &audio_object_type) const; + + uint32_t CalculateFrameLength(AudioObjectType audio_object_type, bool frame_length_flag) const; + + // Table 4.1 - Syntax of GASpecificConfig() + bool GASpecificConfig(BitReader &reader); + private: - AudioObjectType _object_type = AudioObjectType::Null; // 5 bits - AacSamplingFrequencies _sampling_frequency_index = AacSamplingFrequencies::RATES_RESERVED; // 4 bits + AudioObjectType _audio_object_type = AudioObjectType::Null; // 5 bits + AacSamplingFrequencies _sampling_frequency_index = AacSamplingFrequencies::ESCAPE_VALUE; // 4 bits + AacSamplingFrequencies _probed_sampling_frequency_index = AacSamplingFrequencies::ESCAPE_VALUE; + uint32_t _sampling_frequency = 0; // 24 bits // 0 : Defined in AOT Specifc Config // 1 : 1 channel: front-center @@ -202,5 +142,7 @@ class AudioSpecificConfig : public DecoderConfigurationRecord // 4 : 4 channels: front-center, front-left, front-right, back-center // ... // 8-15 : Reserved - uint8_t _channel = 15; // 4 bits -}; \ No newline at end of file + uint8_t _channel_configuration = 15; // 4 bits + + uint32_t _frame_length = 0; +}; diff --git a/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.cpp b/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.cpp index e54a562ba..d86eda235 100644 --- a/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.cpp +++ b/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.cpp @@ -22,7 +22,7 @@ ov::String AVCDecoderConfigurationRecord::GetCodecsParameter() const return ov::String::FormatString("avc1.%02x%02x%02x", ProfileIndication(), Compatibility(), LevelIndication()); } -bool AVCDecoderConfigurationRecord::Parse(const std::shared_ptr<ov::Data> &data) +bool AVCDecoderConfigurationRecord::Parse(const std::shared_ptr<const ov::Data> &data) { if (data == nullptr) { @@ -165,7 +165,7 @@ bool AVCDecoderConfigurationRecord::Equals(const std::shared_ptr<DecoderConfigur return true; } -std::shared_ptr<ov::Data> AVCDecoderConfigurationRecord::Serialize() +std::shared_ptr<const ov::Data> AVCDecoderConfigurationRecord::Serialize() { ov::BitWriter bits(512); diff --git a/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.h b/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.h index 7c34f50c9..1fa171b7b 100644 --- a/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.h +++ b/src/projects/modules/bitstream/h264/h264_decoder_configuration_record.h @@ -50,7 +50,7 @@ class AVCDecoderConfigurationRecord : public DecoderConfigurationRecord ov::String GetCodecsParameter() const override; // Instance can be initialized by putting raw data in AVCDecoderConfigurationRecord. - bool Parse(const std::shared_ptr<ov::Data> &data) override; + bool Parse(const std::shared_ptr<const ov::Data> &data) override; bool Equals(const std::shared_ptr<DecoderConfigurationRecord> &other) override; // Instance can be initialized by putting SPS/PPS in AVCDecoderConfigurationRecord. @@ -58,7 +58,7 @@ class AVCDecoderConfigurationRecord : public DecoderConfigurationRecord bool AddPPS(const std::shared_ptr<ov::Data> &pps); bool AddSPSExt(const std::shared_ptr<ov::Data> &sps_ext); - std::shared_ptr<ov::Data> Serialize() override; + std::shared_ptr<const ov::Data> Serialize() override; uint8_t Version() const; uint8_t ProfileIndication() const; diff --git a/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.cpp b/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.cpp index 9ac877547..d7fe9c2d2 100644 --- a/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.cpp +++ b/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.cpp @@ -101,7 +101,7 @@ ov::String HEVCDecoderConfigurationRecord::GetCodecsParameter() const return codecs_parameter; } -bool HEVCDecoderConfigurationRecord::Parse(const std::shared_ptr<ov::Data> &data) +bool HEVCDecoderConfigurationRecord::Parse(const std::shared_ptr<const ov::Data> &data) { if (data == nullptr) { @@ -279,7 +279,7 @@ bool HEVCDecoderConfigurationRecord::Equals(const std::shared_ptr<DecoderConfigu return true; } -std::shared_ptr<ov::Data> HEVCDecoderConfigurationRecord::Serialize() +std::shared_ptr<const ov::Data> HEVCDecoderConfigurationRecord::Serialize() { if (IsValid() == false) { diff --git a/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.h b/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.h index c625be8a5..1c72899d1 100644 --- a/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.h +++ b/src/projects/modules/bitstream/h265/h265_decoder_configuration_record.h @@ -50,10 +50,10 @@ class HEVCDecoderConfigurationRecord : public DecoderConfigurationRecord public: bool IsValid() const override; ov::String GetCodecsParameter() const override; - bool Parse(const std::shared_ptr<ov::Data> &data) override; + bool Parse(const std::shared_ptr<const ov::Data> &data) override; bool Equals(const std::shared_ptr<DecoderConfigurationRecord> &other) override; - std::shared_ptr<ov::Data> Serialize() override; + std::shared_ptr<const ov::Data> Serialize() override; void AddNalUnit(H265NALUnitType nal_type, const std::shared_ptr<ov::Data> &nal_unit); // SPS, PPS, VPS, etc. diff --git a/src/projects/modules/bitstream/opus/opus_specific_config.h b/src/projects/modules/bitstream/opus/opus_specific_config.h index d94c9f36a..9fa30d1a1 100644 --- a/src/projects/modules/bitstream/opus/opus_specific_config.h +++ b/src/projects/modules/bitstream/opus/opus_specific_config.h @@ -55,7 +55,7 @@ class OpusSpecificConfig : public DecoderConfigurationRecord return (_header == _header_const) && (_version == 1) && (_channels > 0) && (_sample_rate == 48000); } - bool Parse(const std::shared_ptr<ov::Data> &data) override + bool Parse(const std::shared_ptr<const ov::Data> &data) override { if (data->GetLength() < MIN_OPUS_SPECIFIC_CONFIG_SIZE) { @@ -81,7 +81,7 @@ class OpusSpecificConfig : public DecoderConfigurationRecord return other->GetData()->IsEqual(GetData()); } - std::shared_ptr<ov::Data> Serialize() override + std::shared_ptr<const ov::Data> Serialize() override { ov::BitWriter bits(MIN_OPUS_SPECIFIC_CONFIG_SIZE); diff --git a/src/projects/modules/containers/bmff/bmff_packager.cpp b/src/projects/modules/containers/bmff/bmff_packager.cpp index 1ac37d20f..c86042599 100644 --- a/src/projects/modules/containers/bmff/bmff_packager.cpp +++ b/src/projects/modules/containers/bmff/bmff_packager.cpp @@ -1414,7 +1414,7 @@ namespace bmff stream.WriteBE32(0); // unsigned int(32) default_sample_size; - stream.WriteBE32(1); + stream.WriteBE32(0); // unsigned int(32) default_sample_flags; // bit(4) reserved = 0; @@ -1667,19 +1667,19 @@ namespace bmff // unsigned int(32) track_ID; stream.WriteBE32(1); - // unsigned int(64) base_data_offset; + // // unsigned int(64) base_data_offset; - // unsigned int(32) sample_description_index; - stream.WriteBE32(1); + // // unsigned int(32) sample_description_index; + // stream.WriteBE32(1); - // unsigned int(32) default_sample_duration; - stream.WriteBE32(33); + // // unsigned int(32) default_sample_duration; + // stream.WriteBE32(33); - // unsigned int(32) default_sample_size; - stream.WriteBE32(0); + // // unsigned int(32) default_sample_size; + // stream.WriteBE32(0); - // unsigned int(32) default_sample_flags; - stream.WriteBE32(0); + // // unsigned int(32) default_sample_flags; + // stream.WriteBE32(0); // tf_flags // 0x000001 base-data-offset-present: @@ -1690,7 +1690,7 @@ namespace bmff // 0x010000 duration-is-empty: // 0x020000 default‐base‐is‐moof: if base‐data‐offset‐present is 1, this flag is ignored. If base-data-offset-present is zero, this indicates that the base-data-offset for this track fragment is the position of the first byte of the enclosing Movie Fragment Box. Support for the default‐base‐is‐moof flag is required under the ‘iso5’ brand, and it shall not be used in brands or compatible brands earlier than iso5. - return WriteFullBox(container_stream, "tfhd", *stream.GetData(), 0, 0x2 | 0x8 | 0x10 | 0x20 |0x020000); + return WriteFullBox(container_stream, "tfhd", *stream.GetData(), 0, 0); //0x2 | 0x8 | 0x10 | 0x20 |0x020000); } bool Packager::WriteTfdtBox(ov::ByteStream &container_stream, const std::shared_ptr<const Samples> &samples) diff --git a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_packager.cpp b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_packager.cpp index 60208701e..7d76837d9 100644 --- a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_packager.cpp +++ b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_packager.cpp @@ -159,6 +159,15 @@ namespace bmff if (samples != nullptr && samples->GetTotalCount() > 0) { + bool next_frame_is_idr = (next_frame->GetFlag() == MediaPacketFlag::Key) || (GetMediaTrack()->GetMediaType() == cmn::MediaType::Audio); + + // Calculate duration as milliseconds + double total_sample_duration = samples->GetTotalDuration(); + double next_total_sample_duration = total_sample_duration + next_frame->GetDuration(); + + double total_sample_duration_ms = (static_cast<double>(total_sample_duration) / GetMediaTrack()->GetTimeBase().GetTimescale()) * 1000.0; + double next_total_sample_duration_ms = (static_cast<double>(next_total_sample_duration) / GetMediaTrack()->GetTimeBase().GetTimescale()) * 1000.0; + // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.3.8 // The duration of a Partial Segment MUST be less than or equal to the Part Target Duration. // The duration of each Partial Segment MUST be at least 85% of the Part Target Duration, @@ -166,7 +175,7 @@ namespace bmff // and the final Partial Segment of any Parent Segment. auto last_segment = _storage->GetLastSegment(); - bool last_partial_segment = false; + bool is_last_partial_segment = false; if (last_segment != nullptr && last_segment->IsCompleted() == false) { // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.4.9 @@ -175,35 +184,27 @@ namespace bmff // at least 85% of the Part Target Duration, with the exception of // Partial Segments with the INDEPENDENT=YES attribute and the final // Partial Segment of any Parent Segment. - if (_target_chunk_duration_ms + last_segment->GetDuration() >= _storage->GetTargetSegmentDuration()) + if (total_sample_duration_ms + last_segment->GetDuration() >= _storage->GetTargetSegmentDuration()) { // Last partial segment - last_partial_segment = true; + is_last_partial_segment = true; } } - bool next_frame_is_idr = (next_frame->GetFlag() == MediaPacketFlag::Key) || (GetMediaTrack()->GetMediaType() == cmn::MediaType::Audio); - - // Calculate duration as milliseconds - double total_duration = samples->GetTotalDuration(); - double expected_duration = total_duration + next_frame->GetDuration(); - - double total_duration_ms = (static_cast<double>(total_duration) / GetMediaTrack()->GetTimeBase().GetTimescale()) * 1000.0; - double expected_duration_ms = (static_cast<double>(expected_duration) / GetMediaTrack()->GetTimeBase().GetTimescale()) * 1000.0; - - logtd("track(%d), total_duration_ms: %lf, expected_duration_ms: %lf, target_chunk_duration_ms: %lf, next_frame_is_idr: %d, last_partial_segment: %d last_segment_duration: %lf, target_segment_duration: %lld", GetMediaTrack()->GetId(), total_duration_ms, expected_duration_ms, _target_chunk_duration_ms, next_frame_is_idr, last_partial_segment, last_segment != nullptr ? last_segment->GetDuration() : -1, _storage->GetTargetSegmentDuration()); + logtd("track(%d), total_sample_duration_ms: %lf, next_total_sample_duration_ms: %lf, target_chunk_duration_ms: %lf, next_frame_is_idr: %d, is_last_partial_segment: %d last_segment_duration: %lf, target_segment_duration: %lld", GetMediaTrack()->GetId(), total_sample_duration_ms, next_total_sample_duration_ms, _target_chunk_duration_ms, next_frame_is_idr, is_last_partial_segment, last_segment != nullptr ? last_segment->GetDuration() : -1, _storage->GetTargetSegmentDuration()); // - In the last partial segment, if the next frame is a keyframe, a segment is created immediately. This allows the segment to start with a keyframe. // - When adding samples, if the Part Target Duration is exceeded, a chunk is created immediately. // - If it exceeds 85% and the next sample is independent, a chunk is created. This makes the next chunk start independent. if ( - (last_partial_segment == true && - GetMediaTrack()->GetMediaType() == cmn::MediaType::Video && + (is_last_partial_segment == true && GetMediaTrack()->GetMediaType() == cmn::MediaType::Video && next_frame->GetFlag() == MediaPacketFlag::Key) + + || (is_last_partial_segment == true && GetMediaTrack()->GetMediaType() == cmn::MediaType::Audio) - || (total_duration_ms >= _target_chunk_duration_ms) + || (total_sample_duration_ms >= _target_chunk_duration_ms) - || ((expected_duration_ms > _target_chunk_duration_ms) && (total_duration_ms >= _target_chunk_duration_ms * 0.85)) + || ((next_total_sample_duration_ms > _target_chunk_duration_ms) && (total_sample_duration_ms >= _target_chunk_duration_ms * 0.85)) ) { double reserve_buffer_size; @@ -246,8 +247,8 @@ namespace bmff if (_storage != nullptr && _storage->AppendMediaChunk(chunk, samples->GetStartTimestamp(), - total_duration_ms, - samples->IsIndependent(), (last_partial_segment && next_frame_is_idr)) == false) + total_sample_duration_ms, + samples->IsIndependent(), (is_last_partial_segment && next_frame_is_idr)) == false) { logte("FMP4Packager::AppendSample() - Failed to store media chunk"); return false; @@ -256,7 +257,7 @@ namespace bmff _sample_buffer.Reset(); // Set the average chunk duration to config.chunk_duration_ms - // _target_chunk_duration_ms -= total_duration_ms; + // _target_chunk_duration_ms -= total_sample_duration_ms; // _target_chunk_duration_ms += _config.chunk_duration_ms; } } diff --git a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp index 9bebcd387..d633ac123 100644 --- a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp +++ b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp @@ -344,7 +344,7 @@ namespace bmff if (segment->GetDuration() >= _config.segment_duration_ms * 1.2) { - logtw("LLHLS stream (%s) / track (%d) - a longer-than-expected (%.1lf | expected : %llu) segment has created. Long or irregular intervals between keyframes might be the cause.", _stream_tag.CStr(), _track->GetId(), segment->GetDuration(), _config.segment_duration_ms); + logtw("LLHLS stream (%s) / track (%d) - a longer-than-expected (%.1lf | expected : %llu) segment has created. Long or irregular keyframe interval could be the cause.", _stream_tag.CStr(), _track->GetId(), segment->GetDuration(), _config.segment_duration_ms); } } else if (segment->GetDuration() > _config.segment_duration_ms * 2) diff --git a/src/projects/modules/containers/flv/flv_parser.cpp b/src/projects/modules/containers/flv/flv_parser.cpp index a0cbb845d..05f244994 100644 --- a/src/projects/modules/containers/flv/flv_parser.cpp +++ b/src/projects/modules/containers/flv/flv_parser.cpp @@ -1,37 +1,37 @@ #include "flv_parser.h" -#include <base/ovlibrary/ovlibrary.h> #include <base/ovlibrary/bit_reader.h> +#include <base/ovlibrary/ovlibrary.h> #define OV_LOG_TAG "FlvParser" -bool FlvVideoData::Parse(const uint8_t *data, size_t data_length, FlvVideoData &video_data) +bool FlvVideoData::Parse(const std::shared_ptr<const ov::Data>& data) { - if(data_length < MIN_FLV_VIDEO_DATA_LENGTH) + if (data->GetLength() < MIN_FLV_VIDEO_DATA_LENGTH) { - logte("The data inputted is too small for parsing (%d must be bigger than %d)", data_length, MIN_FLV_VIDEO_DATA_LENGTH); + logte("The data inputted is too small for parsing (%zu must be bigger than %d)", data->GetLength(), MIN_FLV_VIDEO_DATA_LENGTH); return false; } - BitReader parser(data, data_length); + BitReader parser(data); - video_data._frame_type = static_cast<FlvVideoFrameTypes>(parser.ReadBits<uint8_t>(4)); - video_data._codec_id = static_cast<FlvVideoCodecId>(parser.ReadBits<uint8_t>(4)); + _frame_type = static_cast<FlvVideoFrameTypes>(parser.ReadBits<uint8_t>(4)); + _codec_id = static_cast<FlvVideoCodecId>(parser.ReadBits<uint8_t>(4)); - if(video_data._codec_id != FlvVideoCodecId::AVC) + if (_codec_id != FlvVideoCodecId::AVC) { - logte("Unsupported codec : %d", static_cast<uint8_t>(video_data._codec_id)); + logte("Unsupported codec : %d", static_cast<uint8_t>(_codec_id)); return false; } - video_data._packet_type = static_cast<FlvAvcPacketType>(parser.ReadBytes<uint8_t>()); + _packet_type = static_cast<FlvAvcPacketType>(parser.ReadBytes<uint8_t>()); int32_t composition_time = static_cast<int32_t>(parser.ReadBits<uint32_t>(24)); // Need to convert UI24 to SI24 - video_data._composition_time = OV_CHECK_FLAG(composition_time, 0x800000) ? composition_time |= 0xFF000000 : composition_time; - video_data._payload = parser.CurrentPosition(); - video_data._payload_length = parser.BytesRemained(); + _composition_time = OV_CHECK_FLAG(composition_time, 0x800000) ? composition_time |= 0xFF000000 : composition_time; + _payload = parser.CurrentPosition(); + _payload_length = parser.BytesRemained(); return true; } @@ -65,24 +65,24 @@ size_t FlvVideoData::PayloadLength() return _payload_length; } -bool FlvAudioData::Parse(const uint8_t *data, size_t data_length, FlvAudioData &audio_data) +bool FlvAudioData::Parse(const std::shared_ptr<const ov::Data>& data) { - if(data_length < MIN_FLV_AUDIO_DATA_LENGTH) + if (data->GetLength() < MIN_FLV_AUDIO_DATA_LENGTH) { - logte("The data inputted is too small for parsing (%d must be bigger than %d)", data_length, MIN_FLV_AUDIO_DATA_LENGTH); + logte("The data inputted is too small for parsing (%zu must be bigger than %d)", data->GetLength(), MIN_FLV_AUDIO_DATA_LENGTH); return false; } - BitReader parser(data, data_length); + BitReader parser(data); - audio_data._format = static_cast<FlvSoundFormat>(parser.ReadBits<uint8_t>(4)); - audio_data._sample_rate = static_cast<FlvSoundRate>(parser.ReadBits<uint8_t>(2)); - audio_data._sample_size = static_cast<FlvSoundSize>(parser.ReadBit()); - audio_data._channel = static_cast<FlvSoundType>(parser.ReadBit()); - audio_data._packet_type = static_cast<FlvAACPacketType>(parser.ReadBytes<uint8_t>()); + _format = static_cast<FlvSoundFormat>(parser.ReadBits<uint8_t>(4)); + _sample_rate = static_cast<FlvSoundRate>(parser.ReadBits<uint8_t>(2)); + _sample_size = static_cast<FlvSoundSize>(parser.ReadBit()); + _channel = static_cast<FlvSoundType>(parser.ReadBit()); + _packet_type = static_cast<FlvAACPacketType>(parser.ReadBytes<uint8_t>()); - audio_data._payload = parser.CurrentPosition(); - audio_data._payload_length = parser.BytesRemained(); + _payload = parser.CurrentPosition(); + _payload_length = parser.BytesRemained(); return true; } diff --git a/src/projects/modules/containers/flv/flv_parser.h b/src/projects/modules/containers/flv/flv_parser.h index 139ca791c..a9d2966c9 100644 --- a/src/projects/modules/containers/flv/flv_parser.h +++ b/src/projects/modules/containers/flv/flv_parser.h @@ -80,7 +80,7 @@ enum class FlvAACPacketType : uint8_t class FlvVideoData { public: - static bool Parse(const uint8_t *data, size_t data_length, FlvVideoData &video_data); + bool Parse(const std::shared_ptr<const ov::Data> &data); FlvVideoFrameTypes FrameType(); FlvVideoCodecId CodecId(); @@ -112,7 +112,7 @@ class FlvVideoData class FlvAudioData { public: - static bool Parse(const uint8_t *data, size_t data_length, FlvAudioData &audio_data); + bool Parse(const std::shared_ptr<const ov::Data> &data); FlvSoundFormat Format(); FlvSoundRate SampleRate(); diff --git a/src/projects/modules/containers/mpegts/mpegts_depacketizer.cpp b/src/projects/modules/containers/mpegts/mpegts_depacketizer.cpp index 333ff0c38..8babf51ae 100644 --- a/src/projects/modules/containers/mpegts/mpegts_depacketizer.cpp +++ b/src/projects/modules/containers/mpegts/mpegts_depacketizer.cpp @@ -549,7 +549,6 @@ namespace mpegts track->SetCodecId(cmn::MediaCodecId::Aac); track->SetOriginBitstream(cmn::BitstreamFormat::AAC_ADTS); track->SetTimeBase(1, 90000); - track->SetAudioTimestampScale(90000/1000); break; default: diff --git a/src/projects/modules/rtp_rtcp/rtp_depacketizer_mpeg4_generic_audio.cpp b/src/projects/modules/rtp_rtcp/rtp_depacketizer_mpeg4_generic_audio.cpp index 6f62e6ea0..b808be4d9 100644 --- a/src/projects/modules/rtp_rtcp/rtp_depacketizer_mpeg4_generic_audio.cpp +++ b/src/projects/modules/rtp_rtcp/rtp_depacketizer_mpeg4_generic_audio.cpp @@ -55,7 +55,7 @@ std::shared_ptr<ov::Data> RtpDepacketizerMpeg4GenericAudio::ParseAndAssembleFram //Get the AACSecificConfig value from extradata; uint8_t aac_profile = static_cast<uint8_t>(_aac_config.GetAacProfile()); - uint8_t aac_sample_rate = static_cast<uint8_t>(_aac_config.SamplingFrequency()); + uint8_t aac_sample_rate = static_cast<uint8_t>(_aac_config.SamplingFrequencyIndex()); uint8_t aac_channels = static_cast<uint8_t>(_aac_config.Channel()); bitstream->Append(AacConverter::MakeAdtsHeader(aac_profile, aac_sample_rate, aac_channels, raw_aac_data_length)); diff --git a/src/projects/modules/segment_writer/writer.cpp b/src/projects/modules/segment_writer/writer.cpp index 1fb8fd94d..c26ec6085 100644 --- a/src/projects/modules/segment_writer/writer.cpp +++ b/src/projects/modules/segment_writer/writer.cpp @@ -223,7 +223,7 @@ bool Writer::FillCodecParameters(const std::shared_ptr<const Track> &track, AVCo codec_parameters->height = media_track->GetHeight(); codec_parameters->format = media_track->GetColorspace(); - std::shared_ptr<ov::Data> extra_data = nullptr; + std::shared_ptr<const ov::Data> extra_data = nullptr; if (media_track->GetCodecId() == cmn::MediaCodecId::H265) { codec_parameters->codec_tag = MKTAG('h', 'v', 'c', '1'); @@ -263,7 +263,7 @@ bool Writer::FillCodecParameters(const std::shared_ptr<const Track> &track, AVCo codec_parameters->format = static_cast<int>(media_track->GetSample().GetFormat()); codec_parameters->codec_tag = 0; - std::shared_ptr<ov::Data> extra_data = nullptr; + std::shared_ptr<const ov::Data> extra_data = nullptr; if (media_track->GetCodecId() == cmn::MediaCodecId::Aac) { codec_parameters->codec_tag = MKTAG('a', 'a', 'c', 'p'); diff --git a/src/projects/providers/rtmp/chunk/rtmp_datastructure.h b/src/projects/providers/rtmp/chunk/rtmp_datastructure.h index bbab016a6..9c837751b 100644 --- a/src/projects/providers/rtmp/chunk/rtmp_datastructure.h +++ b/src/projects/providers/rtmp/chunk/rtmp_datastructure.h @@ -83,7 +83,7 @@ struct RtmpChunkHeader int64_t timestamp = 0LL; uint32_t timestamp_delta = 0U; - RtmpMessageTypeID type_id = RtmpMessageTypeID::UNKNOWN; + RtmpMessageTypeID type_id = RtmpMessageTypeID::Unknown; uint32_t stream_id = 0U; } completed; diff --git a/src/projects/providers/rtmp/chunk/rtmp_define.h b/src/projects/providers/rtmp/chunk/rtmp_define.h index 63f17db5f..cc6aa5b8b 100644 --- a/src/projects/providers/rtmp/chunk/rtmp_define.h +++ b/src/projects/providers/rtmp/chunk/rtmp_define.h @@ -2,223 +2,154 @@ // // RtmpProvider // -// Created by Jaejong Bong +// Created by Hyunjun Jang // Copyright (c) 2018 AirenSoft. All rights reserved. // //============================================================================== #pragma once +#include <arpa/inet.h> +#include <base/ovlibrary/ovlibrary.h> #include <string.h> #include <map> #include <memory> #include <vector> -#ifdef WIN32 - -# include <winsock2.h> - -#else -# include <arpa/inet.h> -#endif - -#pragma pack(1) -#define RTMP_AVC_NAL_HEADER_SIZE (4) // 00 00 00 01 or 00 00 01 -#define RTMP_ADTS_HEADER_SIZE (7) #define RTMP_MAX_PACKET_SIZE (20 * 1024 * 1024) // 20M -//Avc Nal Header -const char g_rtmp_avc_nal_header[RTMP_AVC_NAL_HEADER_SIZE] = {0, 0, 0, 1}; - -//Rtmp - 44100(4) 22050(7) 11025(10) 사용 -const int g_rtmp_sample_rate_table[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, - 8000, 7350, 0, 0, 0}; -#define RTMP_SAMPLERATE_TABLE_SIZE (16) +#define RTMP_TIME_SCALE 1000 -#define RTMP_SESSION_KEY_MAX_SIZE (1024) -#define RTMP_TIME_SCALE (1000) +#define RTMP_HANDSHAKE_VERSION 0x03 +#define RTMP_HANDSHAKE_PACKET_SIZE 1536 -// HANDSHAKE -#define RTMP_HANDSHAKE_VERSION (0x03) -#define RTMP_HANDSHAKE_PACKET_SIZE (1536) - -// CHUNK -#define RTMP_CHUNK_BASIC_HEADER_SIZE_MAX (3) +#define RTMP_CHUNK_BASIC_HEADER_SIZE_MAX 3 #define RTMP_CHUNK_MSG_HEADER_SIZE_MAX (11 + 4) // header + extended timestamp #define RTMP_PACKET_HEADER_SIZE_MAX (RTMP_CHUNK_BASIC_HEADER_SIZE_MAX + RTMP_CHUNK_MSG_HEADER_SIZE_MAX) -#define RTMP_CHUNK_TYPE_MASK (0xc0) -#define RTMP_CHUNK_STREAM_ID_MASK (0x3f) // 00111111b +#define RTMP_EXTEND_TIMESTAMP 0x00ffffff +#define RTMP_EXTEND_TIMESTAMP_SIZE 4 -#define RTMP_EXTEND_TIMESTAMP (0x00ffffff) -#define RTMP_EXTEND_TIMESTAMP_SIZE (4) //sizeof(uint) +enum class RtmpChunkStreamId : uint32_t +{ + Urgent = 2, + Control = 3, + Media = 8, -// CHUNK STREAM ID -#define RTMP_CHUNK_STREAM_ID_URGENT (2) -#define RTMP_CHUNK_STREAM_ID_CONTROL (3) -#define RTMP_CHUNK_STREAM_ID_MEDIA (8) + Mask = 0b00111111, +}; -// MESSAGE ID -enum class RtmpMessageTypeID : uint8_t +// Overload operator to allow usage like `10 & RtmpChunkStreamId::Mask` +inline ov::UnderylingType<RtmpChunkStreamId> operator&(ov::UnderylingType<RtmpChunkStreamId> number, RtmpChunkStreamId id) { - UNKNOWN = 0, + return number & ov::ToUnderlyingType(id); +} - SET_CHUNK_SIZE = 1, - ABORT = 2, - ACKNOWLEDGEMENT = 3, - USER_CONTROL = 4, - WINDOW_ACKNOWLEDGEMENT_SIZE = 5, - SET_PEER_BANDWIDTH = 6, - AUDIO = 8, - VIDEO = 9, - AMF3_DATA = 15, - AMF3_SHARED_OBJECT = 16, - AMF3_COMMAND = 17, - AMF0_DATA = 18, - AMF0_SHARED_OBJECT = 19, - AMF0_COMMAND = 20, - AGGREGATE = 22, +enum class RtmpMessageTypeID : uint8_t +{ + Unknown = 0, + + SetChunkSize = 1, + Abort = 2, + Acknowledgement = 3, + UserControl = 4, + WindowAcknowledgementSize = 5, + SetPeerBandwidth = 6, + Audio = 8, + Video = 9, + Amf3Data = 15, + Amf3SharedObject = 16, + Amf3Command = 17, + Amf0Data = 18, + Amf0SharedObject = 19, + Amf0Command = 20, + Aggregate = 22, }; +#define _DECLARE_ENUM_TO_STRING(type_id) \ + case RtmpMessageTypeID::type_id: \ + return #type_id + inline constexpr const char *StringFromRtmpMessageTypeID(RtmpMessageTypeID type) { switch (type) { - case RtmpMessageTypeID::UNKNOWN: - return "Unknown"; - case RtmpMessageTypeID::SET_CHUNK_SIZE: - return "SetChunkSize"; - case RtmpMessageTypeID::ABORT: - return "Abort"; - case RtmpMessageTypeID::ACKNOWLEDGEMENT: - return "Acknowledgement"; - case RtmpMessageTypeID::USER_CONTROL: - return "UserControl"; - case RtmpMessageTypeID::WINDOW_ACKNOWLEDGEMENT_SIZE: - return "WindowAcknowledgementSize"; - case RtmpMessageTypeID::SET_PEER_BANDWIDTH: - return "SetPeerBandwidth"; - case RtmpMessageTypeID::AUDIO: - return "Audio"; - case RtmpMessageTypeID::VIDEO: - return "Video"; - case RtmpMessageTypeID::AMF3_DATA: - return "AMF3Data"; - case RtmpMessageTypeID::AMF3_SHARED_OBJECT: - return "AMF3SharedObject"; - case RtmpMessageTypeID::AMF3_COMMAND: - return "AMF3Command"; - case RtmpMessageTypeID::AMF0_DATA: - return "AMF0Data"; - case RtmpMessageTypeID::AMF0_SHARED_OBJECT: - return "AMF0SharedObject"; - case RtmpMessageTypeID::AMF0_COMMAND: - return "AMF0Command"; - case RtmpMessageTypeID::AGGREGATE: - return "Aggregate"; + _DECLARE_ENUM_TO_STRING(Unknown); + _DECLARE_ENUM_TO_STRING(SetChunkSize); + _DECLARE_ENUM_TO_STRING(Abort); + _DECLARE_ENUM_TO_STRING(Acknowledgement); + _DECLARE_ENUM_TO_STRING(UserControl); + _DECLARE_ENUM_TO_STRING(WindowAcknowledgementSize); + _DECLARE_ENUM_TO_STRING(SetPeerBandwidth); + _DECLARE_ENUM_TO_STRING(Audio); + _DECLARE_ENUM_TO_STRING(Video); + _DECLARE_ENUM_TO_STRING(Amf3Data); + _DECLARE_ENUM_TO_STRING(Amf3SharedObject); + _DECLARE_ENUM_TO_STRING(Amf3Command); + _DECLARE_ENUM_TO_STRING(Amf0Data); + _DECLARE_ENUM_TO_STRING(Amf0SharedObject); + _DECLARE_ENUM_TO_STRING(Amf0Command); + _DECLARE_ENUM_TO_STRING(Aggregate); } return "Invalid"; } -// USER CONTROL MESSAGE ID -#define RTMP_UCMID_STREAMBEGIN (0) -#define RTMP_UCMID_STREAMEOF (1) -#define RTMP_UCMID_STREAMDRY (2) -#define RTMP_UCMID_SETBUFFERLENGTH (3) -#define RTMP_UCMID_STREAMISRECORDED (4) -#define RTMP_UCMID_PINGREQUEST (6) -#define RTMP_UCMID_PINGRESPONSE (7) -#define RTMP_UCMID_BUFFEREMPTY (31) -#define RTMP_UCMID_BUFFERREADY (32) - -// COMMAND TRANSACTION ID -#define RTMP_CMD_TRID_NOREPONSE (0.0) -#define RTMP_CMD_TRID_CONNECT (1.0) -#define RTMP_CMD_TRID_CREATESTREAM (2.0) -#define RTMP_CMD_TRID_RELEASESTREAM (2.0) -#define RTMP_CMD_TRID_DELETESTREAM RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_CLOSESTREAM RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_PLAY RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_PLAY2 RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_SEEK RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_PAUSE RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_RECEIVEAUDIO RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_RECEIVEVIDEO RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_PUBLISH RTMP_CMD_TRID_NOREPONSE -#define RTMP_CMD_TRID_FCPUBLISH (3.0) -#define RTMP_CMD_TRID_FCSUBSCRIBE (4.0) -#define RTMP_CMD_TRID_FCUNSUBSCRIBE (5.0) -#define RTMP_CMD_TRID_FCUNPUBLISH (5.0) +enum class UserControlMessageId : uint16_t +{ + StreamBegin = 0, + StreamEof = 1, + StreamDry = 2, + SetBufferLength = 3, + StreamIsRecorded = 4, + PingRequest = 6, + PingResponse = 7, + BufferEmpty = 31, + BufferReady = 32, +}; // COMMAND NAME -#define RTMP_CMD_NAME_CONNECT ("connect") -#define RTMP_CMD_NAME_CREATESTREAM ("createStream") -#define RTMP_CMD_NAME_RELEASESTREAM ("releaseStream") -#define RTMP_CMD_NAME_DELETESTREAM ("deleteStream") -#define RTMP_CMD_NAME_CLOSESTREAM ("closeStream") -#define RTMP_CMD_NAME_PLAY ("play") -#define RTMP_CMD_NAME_ONSTATUS ("onStatus") -#define RTMP_CMD_NAME_PUBLISH ("publish") -#define RTMP_CMD_NAME_FCPUBLISH ("FCPublish") -#define RTMP_CMD_NAME_FCUNPUBLISH ("FCUnpublish") -#define RTMP_CMD_NAME_ONFCPUBLISH ("onFCPublish") -#define RTMP_CMD_NAME_ONFCUNPUBLISH ("onFCUnpublish") -#define RTMP_CMD_NAME_CLOSE ("close") -#define RTMP_CMD_NAME_SETCHALLENGE ("setChallenge") -#define RTMP_CMD_DATA_SETDATAFRAME ("@setDataFrame") -#define RTMP_CMD_NAME_ONFI ("onFI") -#define RTMP_CMD_NAME_ONCLIENTLOGIN ("onClientLogin") -#define RTMP_CMD_DATA_ONMETADATA ("onMetaData") -#define RTMP_ACK_NAME_RESULT ("_result") -#define RTMP_ACK_NAME_ERROR ("_error") -#define RTMP_ACK_NAME_BWDONE ("onBWDone") -#define RTMP_PING ("ping") - -//RtmpStream/RtmpPublish -#define RTMP_H264_I_FRAME_TYPE (0x17) -#define RTMP_H264_P_FRAME_TYPE (0x27) -#define RTMP_DEFAULT_ACKNOWNLEDGEMENT_SIZE (2500000) -#define RTMP_DEFAULT_PEER_BANDWIDTH (2500000) -#define RTMP_DEFAULT_CHUNK_SIZE (128) - -#define RTMP_SEQUENCE_INFO_TYPE (0x00) -#define RTMP_FRAME_DATA_TYPE (0x01) -#define RTMP_VIDEO_DATA_MIN_SIZE (5) // control(1) + sequence(1) + offsettime(3) - -#define RTMP_AUDIO_CONTROL_HEADER_INDEX (0) -#define RTMP_AAC_AUDIO_SEQUENCE_HEADER_INDEX (1) -#define RTMP_AAC_AUDIO_DATA_MIN_SIZE (2) // control(1) + sequence(1) -#define RTMP_AAC_AUDIO_FRAME_INDEX (2) -#define RTMP_SPEEX_AUDIO_FRAME_INDEX (1) -#define RTMP_MP3_AUDIO_FRAME_INDEX (1) -#define RTMP_SPS_PPS_MIN_DATA_SIZE (14) - -#define RTMP_VIDEO_CONTROL_HEADER_INDEX (0) -#define RTMP_VIDEO_SEQUENCE_HEADER_INDEX (1) -#define RTMP_VIDEO_COMPOSITION_OFFSET_INDEX (2) -#define RTMP_VIDEO_FRAME_SIZE_INDEX (5) -#define RTMP_VIDEO_FRAME_INDEX (9) -#define RTMP_VIDEO_FRAME_SIZE_INFO_SIZE (4) - -#define RTMP_UNKNOWN_DEVICE_TYPE_STRING ("unknown_device_type") -#define RTMP_DEFAULT_CLIENT_VERSION ("rtmp client 1.0") //"afcCli 11,0,100,1 (compatible; FMSc/1.0)" +#define RTMP_CMD_NAME_CONNECT "connect" +#define RTMP_CMD_NAME_CREATESTREAM "createStream" +#define RTMP_CMD_NAME_RELEASESTREAM "releaseStream" +#define RTMP_CMD_NAME_DELETESTREAM "deleteStream" +#define RTMP_CMD_NAME_CLOSESTREAM "closeStream" +#define RTMP_CMD_NAME_PLAY "play" +#define RTMP_CMD_NAME_ONSTATUS "onStatus" +#define RTMP_CMD_NAME_PUBLISH "publish" +#define RTMP_CMD_NAME_FCPUBLISH "FCPublish" +#define RTMP_CMD_NAME_FCUNPUBLISH "FCUnpublish" +#define RTMP_CMD_NAME_ONFCPUBLISH "onFCPublish" +#define RTMP_CMD_NAME_ONFCUNPUBLISH "onFCUnpublish" +#define RTMP_CMD_NAME_CLOSE "close" +#define RTMP_CMD_NAME_SETCHALLENGE "setChallenge" +#define RTMP_CMD_DATA_SETDATAFRAME "@setDataFrame" +#define RTMP_CMD_NAME_ONFI "onFI" +#define RTMP_CMD_NAME_ONCLIENTLOGIN "onClientLogin" +#define RTMP_CMD_DATA_ONMETADATA "onMetaData" +#define RTMP_ACK_NAME_RESULT "_result" +#define RTMP_ACK_NAME_ERROR "_error" +#define RTMP_ACK_NAME_BWDONE "onBWDone" +#define RTMP_PING "ping" + +#define RTMP_DEFAULT_ACKNOWNLEDGEMENT_SIZE 2500000 +#define RTMP_DEFAULT_PEER_BANDWIDTH 2500000 +#define RTMP_DEFAULT_CHUNK_SIZE 128 + +#define RTMP_VIDEO_DATA_MIN_SIZE (1 + 1 + 3) // control(1) + sequence(1) + offsettime(3) +#define RTMP_AAC_AUDIO_DATA_MIN_SIZE (1 + 1) // control(1) + sequence(1) + +#define RTMP_UNKNOWN_DEVICE_TYPE_STRING "unknown_device_type" +#define RTMP_DEFAULT_CLIENT_VERSION "rtmp client 1.0" //"afcCli 11,0,100,1 (compatible; FMSc/1.0)" enum class RtmpEncoderType : int32_t { - Custom = 0, // 일반 Rtmp 클라이언트(기타 구분 불가능 Client 포함) + Custom = 0, // General Rtmp client (Client that cannot be distinguished from others) Xsplit, // XSplit OBS, // OBS Lavf, // Libavformat (lavf) }; -enum class RtmpFrameType : int32_t -{ - VideoIFrame = 'I', // VIDEO I Frame - VideoPFrame = 'P', // VIDEO P Frame - AudioFrame = 'A', // AUDIO Frame -}; - enum class RtmpCodecType : int32_t { Unknown, @@ -228,27 +159,6 @@ enum class RtmpCodecType : int32_t SPEEX, // SPEEX(11) }; -struct RtmpFrameInfo -{ -public: - RtmpFrameInfo(uint32_t timestamp_, int composition_time_offset_, RtmpFrameType frame_type_, int frame_size, uint8_t *frame) - { - timestamp = timestamp_; - composition_time_offset = composition_time_offset_; - frame_type = frame_type_; - frame_data = std::make_shared<std::vector<uint8_t>>(frame, frame + frame_size); - } - -public: - uint32_t timestamp; - int composition_time_offset; - RtmpFrameType frame_type; - std::shared_ptr<std::vector<uint8_t>> frame_data; -}; - -//=============================================================================================== -// Rtmp Handshake State -//=============================================================================================== enum class RtmpHandshakeState { Uninitialized = 0, @@ -283,5 +193,3 @@ struct RtmpMediaInfo uint32_t timestamp_scale = RTMP_TIME_SCALE; RtmpEncoderType encoder_type = RtmpEncoderType::Custom; }; - -#pragma pack() \ No newline at end of file diff --git a/src/projects/providers/rtmp/chunk/rtmp_mux_util.cpp b/src/projects/providers/rtmp/chunk/rtmp_mux_util.cpp index db1967c89..460b8c34c 100644 --- a/src/projects/providers/rtmp/chunk/rtmp_mux_util.cpp +++ b/src/projects/providers/rtmp/chunk/rtmp_mux_util.cpp @@ -95,7 +95,7 @@ int RtmpMuxUtil::GetBasicHeaderSizeByRawData(uint8_t data) noexcept { int header_size = 1; - switch (data & RTMP_CHUNK_STREAM_ID_MASK) + switch (data & RtmpChunkStreamId::Mask) { case 0: header_size = 2; @@ -130,7 +130,7 @@ int RtmpMuxUtil::GetChunkHeaderSize(RtmpMessageHeaderType chunk_type, uint32_t c int message_header_size = 0; auto *raw_data_pos = (uint8_t *)raw_data; - // uint32_t chunk_stream_id = (uint32_t)(raw_data_pos[0] & RTMP_CHUNK_STREAM_ID_MASK); + // uint32_t chunk_stream_id = (uint32_t)(raw_data_pos[0] & RtmpChunkStreamId::Mask); switch (chunk_type) { diff --git a/src/projects/providers/rtmp/chunk/rtmp_mux_util.h b/src/projects/providers/rtmp/chunk/rtmp_mux_util.h index 099096b7e..90eb74165 100644 --- a/src/projects/providers/rtmp/chunk/rtmp_mux_util.h +++ b/src/projects/providers/rtmp/chunk/rtmp_mux_util.h @@ -25,16 +25,25 @@ struct RtmpMuxMessageHeader static std::shared_ptr<RtmpMuxMessageHeader> Create( uint32_t chunk_stream_id, - RtmpMessageTypeID type_id = RtmpMessageTypeID::AMF0_COMMAND, + RtmpMessageTypeID type_id = RtmpMessageTypeID::Amf0Command, uint32_t stream_id = 0, uint32_t body_size = 0) { return std::make_shared<RtmpMuxMessageHeader>(chunk_stream_id, 0, type_id, stream_id, body_size); } + static std::shared_ptr<RtmpMuxMessageHeader> Create( + RtmpChunkStreamId chunk_stream_id, + RtmpMessageTypeID type_id = RtmpMessageTypeID::Amf0Command, + uint32_t stream_id = 0, + uint32_t body_size = 0) + { + return Create(ov::ToUnderlyingType(chunk_stream_id), type_id, stream_id, body_size); + } + uint32_t chunk_stream_id = 0; uint32_t timestamp = 0; - RtmpMessageTypeID type_id = RtmpMessageTypeID::UNKNOWN; + RtmpMessageTypeID type_id = RtmpMessageTypeID::Unknown; uint32_t stream_id = 0; uint32_t body_size = 0; }; diff --git a/src/projects/providers/rtmp/rtmp_application.cpp b/src/projects/providers/rtmp/rtmp_application.cpp index d655ea3d3..2de815cd0 100644 --- a/src/projects/providers/rtmp/rtmp_application.cpp +++ b/src/projects/providers/rtmp/rtmp_application.cpp @@ -8,12 +8,11 @@ //============================================================================== #include "rtmp_application.h" -#include "rtmp_stream.h" -#include "rtmp_provider_private.h" -#include "base/provider/push_provider/application.h" #include "base/info/stream.h" - +#include "base/provider/push_provider/application.h" +#include "rtmp_provider_private.h" +#include "rtmp_stream.h" namespace pvd { @@ -32,24 +31,24 @@ namespace pvd bool RtmpApplication::JoinStream(const std::shared_ptr<PushStream> &stream) { // Check duplicated stream name - // If there is a same stream name + // If there is a same stream name auto exist_stream = GetStreamByName(stream->GetName()); - if(exist_stream != nullptr) + if (exist_stream != nullptr) { // Block - if(GetConfig().GetProviders().GetRtmpProvider().IsBlockDuplicateStreamName()) + if (GetConfig().GetProviders().GetRtmpProvider().IsBlockDuplicateStreamName()) { - logti("Reject %s/%s stream it is a stream with a duplicate name.", GetVHostAppName().CStr(), stream->GetName().CStr()); + logti("Reject %s/%s stream it is a stream with a duplicate name.", GetVHostAppName().CStr(), stream->GetName().CStr()); return false; } else { // Disconnect exist stream - logti("Remove exist %s/%s stream because the stream with the same name is connected.", GetVHostAppName().CStr(), stream->GetName().CStr()); + logti("Remove exist %s/%s stream because the stream with the same name is connected.", GetVHostAppName().CStr(), stream->GetName().CStr()); DeleteStream(exist_stream); } } return PushApplication::JoinStream(stream); } -} \ No newline at end of file +} // namespace pvd diff --git a/src/projects/providers/rtmp/rtmp_application.h b/src/projects/providers/rtmp/rtmp_application.h index 70e1ef725..749f0951b 100644 --- a/src/projects/providers/rtmp/rtmp_application.h +++ b/src/projects/providers/rtmp/rtmp_application.h @@ -13,12 +13,10 @@ #include "base/provider/push_provider/application.h" #include "base/provider/push_provider/stream.h" - namespace pvd { class RtmpApplication : public PushApplication { - public: static std::shared_ptr<RtmpApplication> Create(const std::shared_ptr<PushProvider> &provider, const info::Application &application_info); @@ -27,4 +25,4 @@ namespace pvd bool JoinStream(const std::shared_ptr<PushStream> &stream) override; }; -} \ No newline at end of file +} // namespace pvd diff --git a/src/projects/providers/rtmp/rtmp_stream.cpp b/src/projects/providers/rtmp/rtmp_stream.cpp index 1d02a67fa..2dab5b2a1 100644 --- a/src/projects/providers/rtmp/rtmp_stream.cpp +++ b/src/projects/providers/rtmp/rtmp_stream.cpp @@ -11,7 +11,6 @@ #include <base/info/media_extradata.h> #include <base/mediarouter/media_type.h> -#include <modules/bitstream/aac/audio_specific_config.h> #include <modules/bitstream/h264/h264_decoder_configuration_record.h> #include <modules/containers/flv/flv_parser.h> #include <modules/id3v2/frames/id3v2_frames.h> @@ -718,6 +717,12 @@ namespace pvd } } + _media_info->video_codec_type = video_codec_type; + _media_info->video_width = static_cast<int32_t>(video_width); + _media_info->video_height = static_cast<int32_t>(video_height); + _media_info->video_framerate = static_cast<float>(frame_rate); + _media_info->video_bitrate = static_cast<int32_t>(video_bitrate); + // Audio Codec RtmpCodecType audio_codec_type = RtmpCodecType::Unknown; bool audio_available = false; @@ -846,8 +851,15 @@ namespace pvd } } - if (((video_available == true) && (video_codec_type != RtmpCodecType::H264)) || - ((audio_available == true) && (audio_codec_type != RtmpCodecType::AAC))) + _media_info->audio_codec_type = audio_codec_type; + _media_info->audio_bitrate = static_cast<int32_t>(audio_bitrate); + _media_info->audio_channels = static_cast<int32_t>(audio_channels); + _media_info->audio_bits = static_cast<int32_t>(audio_samplesize); + _media_info->audio_samplerate = static_cast<int32_t>(audio_samplerate); + _media_info->encoder_type = encoder_type; + + if ((video_available && (video_codec_type != RtmpCodecType::H264)) || + (audio_available && (audio_codec_type != RtmpCodecType::AAC))) { logtw("AmfMeta has incompatible codec information. - stream(%s/%s) id(%u/%u) video(%s) audio(%s)", _vhost_app_name.CStr(), @@ -858,18 +870,6 @@ namespace pvd GetCodecString(audio_codec_type).CStr()); } - _media_info->video_codec_type = video_codec_type; - _media_info->video_width = static_cast<int32_t>(video_width); - _media_info->video_height = static_cast<int32_t>(video_height); - _media_info->video_framerate = static_cast<float>(frame_rate); - _media_info->video_bitrate = static_cast<int32_t>(video_bitrate); - _media_info->audio_codec_type = audio_codec_type; - _media_info->audio_bitrate = static_cast<int32_t>(audio_bitrate); - _media_info->audio_channels = static_cast<int32_t>(audio_channels); - _media_info->audio_bits = static_cast<int32_t>(audio_samplesize); - _media_info->audio_samplerate = static_cast<int32_t>(audio_samplerate); - _media_info->encoder_type = encoder_type; - return true; } @@ -1045,28 +1045,28 @@ namespace pvd switch (message->header->completed.type_id) { - case RtmpMessageTypeID::AUDIO: + case RtmpMessageTypeID::Audio: result = ReceiveAudioMessage(message); break; - case RtmpMessageTypeID::VIDEO: + case RtmpMessageTypeID::Video: result = ReceiveVideoMessage(message); break; - case RtmpMessageTypeID::SET_CHUNK_SIZE: + case RtmpMessageTypeID::SetChunkSize: result = ReceiveSetChunkSize(message); break; - case RtmpMessageTypeID::ACKNOWLEDGEMENT: + case RtmpMessageTypeID::Acknowledgement: // OME doesn't use this message break; - case RtmpMessageTypeID::AMF0_DATA: + case RtmpMessageTypeID::Amf0Data: ReceiveAmfDataMessage(message); break; - case RtmpMessageTypeID::AMF0_COMMAND: + case RtmpMessageTypeID::Amf0Command: result = ReceiveAmfCommandMessage(message); break; - case RtmpMessageTypeID::USER_CONTROL: + case RtmpMessageTypeID::UserControl: result = ReceiveUserControlMessage(message); break; - case RtmpMessageTypeID::WINDOW_ACKNOWLEDGEMENT_SIZE: + case RtmpMessageTypeID::WindowAcknowledgementSize: ReceiveWindowAcknowledgementSize(message); break; default: @@ -1121,11 +1121,11 @@ namespace pvd ov::ByteStream byte_stream(data); - auto type = byte_stream.ReadBE16(); + auto type = static_cast<UserControlMessageId>(byte_stream.ReadBE16()); switch (type) { - case RTMP_UCMID_PINGREQUEST: { + case UserControlMessageId::PingRequest: { if (data->GetLength() != 6) { logte("Invalid ping message size: %zu", data->GetLength()); @@ -1141,14 +1141,17 @@ namespace pvd // ping response == event type (16 bits) + timestamp (32 bits) auto body = std::make_shared<std::vector<uint8_t>>(2 + 4); auto write_buffer = body->data(); - auto message_header = RtmpMuxMessageHeader::Create(chunk_stream_id, RtmpMessageTypeID::USER_CONTROL, message_stream_id, 6); + auto message_header = RtmpMuxMessageHeader::Create(chunk_stream_id, RtmpMessageTypeID::UserControl, message_stream_id, 6); - *(reinterpret_cast<uint16_t *>(write_buffer)) = ov::HostToBE16(RTMP_UCMID_PINGRESPONSE); + *(reinterpret_cast<uint16_t *>(write_buffer)) = ov::HostToBE16(ov::ToUnderlyingType(UserControlMessageId::PingResponse)); write_buffer += sizeof(uint16_t); *(reinterpret_cast<uint32_t *>(write_buffer)) = ov::HostToBE32(byte_stream.ReadBE32()); return SendMessagePacket(message_header, body); } + + default: + break; } return true; @@ -1321,7 +1324,7 @@ namespace pvd } if ( - (header->completed.type_id == RtmpMessageTypeID::AMF0_DATA) && + (header->completed.type_id == RtmpMessageTypeID::Amf0Data) && (trigger_list.at(0) == "AMFDataMessage")) { auto count = trigger_list.size(); @@ -1590,7 +1593,7 @@ namespace pvd { // Parsing FLV FlvVideoData flv_video; - if (FlvVideoData::Parse(message->payload->GetDataAs<uint8_t>(), message->payload->GetLength(), flv_video) == false) + if (flv_video.Parse(message->payload) == false) { logte("Could not parse flv video (%s/%s)", _vhost_app_name.CStr(), GetName().CStr()); return false; @@ -1788,39 +1791,26 @@ namespace pvd // audio stream callback if (_media_info->audio_stream_coming) { - // Parsing FLV - FlvAudioData flv_audio; - if (FlvAudioData::Parse(message->payload->GetDataAs<uint8_t>(), message->payload->GetLength(), flv_audio) == false) - { - logte("Could not parse flv audio (%s/%s)", _vhost_app_name.CStr(), GetName().CStr()); - return false; - } - - int64_t dts = message->header->completed.timestamp; - int64_t pts = dts; - - if (_negative_cts_detected) - { - pts += ADJUST_PTS; - } - // Get audio track info auto audio_track = GetTrack(RTMP_AUDIO_TRACK_ID); if (audio_track == nullptr) { - logte("Cannot get video track (%s/%s)", _vhost_app_name.CStr(), GetName().CStr()); + logte("Cannot get audio track (%s/%s)", _vhost_app_name.CStr(), GetName().CStr()); return false; } - pts *= audio_track->GetAudioTimestampScale(); - dts *= audio_track->GetAudioTimestampScale(); - - if (_is_incoming_timestamp_used == false) + // Parsing FLV + FlvAudioData flv_audio; + if (flv_audio.Parse(message->payload) == false) { - AdjustTimestamp(pts, dts); + logte("Could not parse flv audio (%s/%s)", _vhost_app_name.CStr(), GetName().CStr()); + return false; } + auto data = std::make_shared<ov::Data>(flv_audio.Payload(), flv_audio.PayloadLength()); + cmn::PacketType packet_type = cmn::PacketType::Unknown; + if (flv_audio.PacketType() == FlvAACPacketType::SEQUENCE_HEADER) { packet_type = cmn::PacketType::SEQUENCE_HEADER; @@ -1830,7 +1820,19 @@ namespace pvd packet_type = cmn::PacketType::RAW; } - auto data = std::make_shared<ov::Data>(flv_audio.Payload(), flv_audio.PayloadLength()); + int64_t dts = message->header->completed.timestamp; + int64_t pts = dts; + + if (_negative_cts_detected) + { + pts += ADJUST_PTS; + } + + if (_is_incoming_timestamp_used == false) + { + AdjustTimestamp(pts, dts); + } + auto frame = std::make_shared<MediaPacket>(GetMsid(), cmn::MediaType::Audio, RTMP_AUDIO_TRACK_ID, @@ -1900,8 +1902,10 @@ namespace pvd return false; } - _event_generator = application->GetConfig().GetProviders().GetRtmpProvider().GetEventGenerator(); - _is_incoming_timestamp_used = application->GetConfig().GetProviders().GetRtmpProvider().IsIncomingTimestampUsed(); + const auto &rtmp_provider = application->GetConfig().GetProviders().GetRtmpProvider(); + + _event_generator = rtmp_provider.GetEventGenerator(); + _is_incoming_timestamp_used = rtmp_provider.IsIncomingTimestampUsed(); SetName(_publish_url->Stream()); @@ -1937,11 +1941,11 @@ namespace pvd // stored messages for (auto message : _stream_message_cache) { - if (message->header->completed.type_id == RtmpMessageTypeID::VIDEO && _media_info->video_stream_coming) + if ((message->header->completed.type_id == RtmpMessageTypeID::Video) && _media_info->video_stream_coming) { ReceiveVideoMessage(message); } - else if (message->header->completed.type_id == RtmpMessageTypeID::AUDIO && _media_info->audio_stream_coming) + else if ((message->header->completed.type_id == RtmpMessageTypeID::Audio) && _media_info->audio_stream_coming) { ReceiveAudioMessage(message); } @@ -1984,7 +1988,6 @@ namespace pvd new_track->SetCodecId(cmn::MediaCodecId::Aac); new_track->SetOriginBitstream(cmn::BitstreamFormat::AAC_RAW); new_track->SetTimeBase(1, 1000); - new_track->SetAudioTimestampScale(1.0); ////////////////// // Below items are not mandatory, it will be parsed again from ADTS parser @@ -2092,14 +2095,14 @@ namespace pvd return true; } - bool RtmpStream::SendUserControlMessage(uint16_t message, std::shared_ptr<std::vector<uint8_t>> &data) + bool RtmpStream::SendUserControlMessage(UserControlMessageId message, std::shared_ptr<std::vector<uint8_t>> &data) { auto message_header = RtmpMuxMessageHeader::Create( - RTMP_CHUNK_STREAM_ID_URGENT, RtmpMessageTypeID::USER_CONTROL, 0, data->size() + 2); + RtmpChunkStreamId::Urgent, RtmpMessageTypeID::UserControl, 0, data->size() + 2); data->insert(data->begin(), 0); data->insert(data->begin(), 0); - RtmpMuxUtil::WriteInt16(data->data(), message); + RtmpMuxUtil::WriteInt16(data->data(), ov::ToUnderlyingType(message)); return SendMessagePacket(message_header, data); } @@ -2108,7 +2111,7 @@ namespace pvd { auto body = std::make_shared<std::vector<uint8_t>>(sizeof(int)); auto message_header = RtmpMuxMessageHeader::Create( - RTMP_CHUNK_STREAM_ID_URGENT, RtmpMessageTypeID::WINDOW_ACKNOWLEDGEMENT_SIZE, _rtmp_stream_id, body->size()); + RtmpChunkStreamId::Urgent, RtmpMessageTypeID::WindowAcknowledgementSize, _rtmp_stream_id, body->size()); RtmpMuxUtil::WriteInt32(body->data(), size); @@ -2119,7 +2122,7 @@ namespace pvd { auto body = std::make_shared<std::vector<uint8_t>>(sizeof(int)); auto message_header = RtmpMuxMessageHeader::Create( - RTMP_CHUNK_STREAM_ID_URGENT, RtmpMessageTypeID::ACKNOWLEDGEMENT, 0, body->size()); + RtmpChunkStreamId::Urgent, RtmpMessageTypeID::Acknowledgement, 0, body->size()); RtmpMuxUtil::WriteInt32(body->data(), acknowledgement_traffic); @@ -2130,7 +2133,7 @@ namespace pvd { auto body = std::make_shared<std::vector<uint8_t>>(5); auto message_header = RtmpMuxMessageHeader::Create( - RTMP_CHUNK_STREAM_ID_URGENT, RtmpMessageTypeID::SET_PEER_BANDWIDTH, _rtmp_stream_id, body->size()); + RtmpChunkStreamId::Urgent, RtmpMessageTypeID::SetPeerBandwidth, _rtmp_stream_id, body->size()); RtmpMuxUtil::WriteInt32(body->data(), bandwidth); RtmpMuxUtil::WriteInt8(body->data() + 4, 2); @@ -2144,7 +2147,7 @@ namespace pvd RtmpMuxUtil::WriteInt32(body->data(), stream_id); - return SendUserControlMessage(RTMP_UCMID_STREAMBEGIN, body); + return SendUserControlMessage(UserControlMessageId::StreamBegin, body); } bool RtmpStream::SendStreamEnd() @@ -2153,7 +2156,7 @@ namespace pvd RtmpMuxUtil::WriteInt32(body->data(), _rtmp_stream_id); - return SendUserControlMessage(RTMP_UCMID_STREAMEOF, body); + return SendUserControlMessage(UserControlMessageId::StreamEof, body); } bool RtmpStream::SendAmfCommand(std::shared_ptr<RtmpMuxMessageHeader> &message_header, AmfDocument &document) @@ -2216,7 +2219,7 @@ namespace pvd bool RtmpStream::SendAmfOnFCPublish(uint32_t chunk_stream_id, uint32_t stream_id, double client_id) { auto message_header = RtmpMuxMessageHeader::Create( - chunk_stream_id, RtmpMessageTypeID::AMF0_COMMAND, _rtmp_stream_id); + chunk_stream_id, RtmpMessageTypeID::Amf0Command, _rtmp_stream_id); AmfDocument document; @@ -2260,7 +2263,7 @@ namespace pvd const char *description, double client_id) { - auto message_header = RtmpMuxMessageHeader::Create(chunk_stream_id, RtmpMessageTypeID::AMF0_COMMAND, stream_id); + auto message_header = RtmpMuxMessageHeader::Create(chunk_stream_id, RtmpMessageTypeID::Amf0Command, stream_id); AmfDocument document; document.AppendProperty(RTMP_CMD_NAME_ONSTATUS); diff --git a/src/projects/providers/rtmp/rtmp_stream.h b/src/projects/providers/rtmp/rtmp_stream.h index 43c704e8c..aea0f7f8d 100644 --- a/src/projects/providers/rtmp/rtmp_stream.h +++ b/src/projects/providers/rtmp/rtmp_stream.h @@ -9,23 +9,22 @@ #pragma once +#include "amf0/amf_document.h" #include "base/common_types.h" #include "base/provider/push_provider/stream.h" -#include "modules/access_control/access_controller.h" - -#include "amf0/amf_document.h" +#include "chunk/rtmp_chunk_parser.h" #include "chunk/rtmp_export_chunk.h" #include "chunk/rtmp_handshake.h" -#include "chunk/rtmp_chunk_parser.h" +#include "modules/access_control/access_controller.h" #define MAX_STREAM_MESSAGE_COUNT (500) #define BASELINE_PROFILE (66) #define MAIN_PROFILE (77) // Fix track id -#define RTMP_VIDEO_TRACK_ID 0 -#define RTMP_AUDIO_TRACK_ID 1 -#define RTMP_DATA_TRACK_ID 2 +#define RTMP_VIDEO_TRACK_ID 0 +#define RTMP_AUDIO_TRACK_ID 1 +#define RTMP_DATA_TRACK_ID 2 namespace pvd { @@ -33,7 +32,7 @@ namespace pvd { public: static std::shared_ptr<RtmpStream> Create(StreamSourceType source_type, uint32_t channel_id, const std::shared_ptr<ov::Socket> &client_socket, const std::shared_ptr<PushProvider> &provider); - + explicit RtmpStream(StreamSourceType source_type, uint32_t channel_id, std::shared_ptr<ov::Socket> client_socket, const std::shared_ptr<PushProvider> &provider); ~RtmpStream() final; @@ -66,7 +65,6 @@ namespace pvd bool OnAmfDeleteStream(const std::shared_ptr<const RtmpChunkHeader> &header, AmfDocument &document, double transaction_id); bool OnAmfMetaData(const std::shared_ptr<const RtmpChunkHeader> &header, const AmfProperty *property); - // Send messages bool SendData(const std::shared_ptr<const ov::Data> &data); bool SendData(const void *data, size_t data_size); @@ -74,7 +72,7 @@ namespace pvd bool SendMessagePacket(std::shared_ptr<RtmpMuxMessageHeader> &message_header, std::shared_ptr<std::vector<uint8_t>> &data); bool SendAcknowledgementSize(uint32_t acknowledgement_traffic); - bool SendUserControlMessage(uint16_t message, std::shared_ptr<std::vector<uint8_t>> &data); + bool SendUserControlMessage(UserControlMessageId message, std::shared_ptr<std::vector<uint8_t>> &data); bool SendWindowAcknowledgementSize(uint32_t size); bool SendSetPeerBandwidth(uint32_t bandwidth); bool SendStreamBegin(uint32_t stream_id); @@ -85,11 +83,11 @@ namespace pvd bool SendAmfOnFCPublish(uint32_t chunk_stream_id, uint32_t stream_id, double client_id); bool SendAmfCreateStreamResult(uint32_t chunk_stream_id, double transaction_id); bool SendAmfOnStatus(uint32_t chunk_stream_id, - uint32_t stream_id, - const char *level, - const char *code, - const char *description, - double client_id); + uint32_t stream_id, + const char *level, + const char *code, + const char *description, + double client_id); // Parsing handshake messages off_t ReceiveHandshakePacket(const std::shared_ptr<const ov::Data> &data); @@ -126,7 +124,7 @@ namespace pvd // RTMP related RtmpHandshakeState _handshake_state = RtmpHandshakeState::Uninitialized; - + std::shared_ptr<RtmpChunkParser> _import_chunk; std::shared_ptr<RtmpExportChunk> _export_chunk; std::shared_ptr<RtmpMediaInfo> _media_info; @@ -151,7 +149,7 @@ namespace pvd std::shared_ptr<const SignedPolicy> _signed_policy = nullptr; std::shared_ptr<const AdmissionWebhooks> _admission_webhooks = nullptr; - ov::String _full_url; // with stream_name + ov::String _full_url; // with stream_name ov::String _tc_url; ov::String _app_name; ov::String _domain_name; @@ -159,7 +157,7 @@ namespace pvd ov::String _stream_name; ov::String _device_string; - std::shared_ptr<ov::Url> _publish_url = nullptr; // AccessControl can redirect url set in RTMP to another url. + std::shared_ptr<ov::Url> _publish_url = nullptr; // AccessControl can redirect url set in RTMP to another url. // Cache (GetApplicationInfo()->GetId()) info::application_id_t _app_id = 0; @@ -168,7 +166,7 @@ namespace pvd std::shared_ptr<ov::Socket> _remote = nullptr; // Received data buffer - std::shared_ptr<ov::Data> _remained_data = nullptr; + std::shared_ptr<ov::Data> _remained_data = nullptr; // Singed Policy uint64_t _stream_expired_msec = 0; @@ -184,7 +182,7 @@ namespace pvd int64_t _last_audio_pts = 0; ov::StopWatch _last_audio_pts_clock; - // For statistics + // For statistics time_t _stream_check_time = 0; uint32_t _key_frame_interval = 0; @@ -203,4 +201,4 @@ namespace pvd bool _is_incoming_timestamp_used = false; }; -} \ No newline at end of file +} // namespace pvd diff --git a/src/projects/providers/webrtc/webrtc_stream.cpp b/src/projects/providers/webrtc/webrtc_stream.cpp index 94bb06147..3c39de6da 100644 --- a/src/projects/providers/webrtc/webrtc_stream.cpp +++ b/src/projects/providers/webrtc/webrtc_stream.cpp @@ -158,7 +158,6 @@ namespace pvd audio_track->SetId(ssrc); audio_track->SetMediaType(cmn::MediaType::Audio); audio_track->SetTimeBase(1, samplerate); - audio_track->SetAudioTimestampScale(1.0); if (channels == 1) { diff --git a/src/projects/publishers/llhls/llhls_chunklist.cpp b/src/projects/publishers/llhls/llhls_chunklist.cpp index 3514589bb..81de80dc2 100644 --- a/src/projects/publishers/llhls/llhls_chunklist.cpp +++ b/src/projects/publishers/llhls/llhls_chunklist.cpp @@ -279,7 +279,7 @@ ov::String LLHlsChunklist::MakeExtXKey() const ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool skip, bool legacy, bool rewind, bool vod, uint32_t vod_start_segment_number) const { std::shared_lock<std::shared_mutex> segment_lock(_segments_guard); - uint8_t version = 10; + uint8_t version = 6; if (_segments.size() == 0) { return ""; diff --git a/src/projects/publishers/llhls/llhls_master_playlist.cpp b/src/projects/publishers/llhls/llhls_master_playlist.cpp index 4df35a8cd..ddd7164a4 100644 --- a/src/projects/publishers/llhls/llhls_master_playlist.cpp +++ b/src/projects/publishers/llhls/llhls_master_playlist.cpp @@ -248,7 +248,7 @@ ov::String LLHlsMasterPlaylist::MakePlaylist(const ov::String &chunk_query_strin { ov::String playlist(10240); - uint8_t version = 7; + uint8_t version = 6; if (legacy == true) { version = 6; diff --git a/src/projects/publishers/llhls/llhls_stream.cpp b/src/projects/publishers/llhls/llhls_stream.cpp index 042535710..9c7586e22 100755 --- a/src/projects/publishers/llhls/llhls_stream.cpp +++ b/src/projects/publishers/llhls/llhls_stream.cpp @@ -1090,9 +1090,48 @@ bool LLHlsStream::AppendMediaPacket(const std::shared_ptr<MediaPacket> &media_pa return true; } +double LLHlsStream::ComputeOptimalPartDuration(const std::shared_ptr<const MediaTrack> &track) const +{ + auto part_target = _packager_config.chunk_duration_ms; + double optimal_part_target = part_target; + + if (track->GetMediaType() == cmn::MediaType::Audio) + { + // Duration of a frame is 1024 samples / sample rate + auto frame_duration = static_cast<double>(track->GetAudioSamplesPerFrame()) / static_cast<double>(track->GetSampleRate()); + auto frame_duration_ms = frame_duration * 1000.0; + + // Find the closest multiple of frame_duration_ms to part_target + auto optimal_frame_count = std::round(part_target / frame_duration_ms); + optimal_part_target = optimal_frame_count * frame_duration_ms; + + logti("LLHlsStream::ComputeOptimalPartDuration() - Audio track(%d) SampleRate(%d) frame_duration_ms(%f) optimal_frame_count(%f) part_target(%f) optimal_part_target(%f)", track->GetId(), track->GetSampleRate(), frame_duration_ms, optimal_frame_count, part_target, optimal_part_target); + } + else if (track->GetMediaType() == cmn::MediaType::Video) + { + // Duration of a frame is 1 / frame rate + auto frame_duration = 1.0 / track->GetFrameRate(); + auto frame_duration_ms = frame_duration * 1000.0; + + // Find the closest multiple of frame_duration_ms to part_target + auto optimal_frame_count = std::round(part_target / frame_duration_ms); + optimal_part_target = optimal_frame_count * frame_duration_ms; + + logti("LLHlsStream::ComputeOptimalPartDuration() - Video track(%d) FrameRate(%f) frame_duration_ms(%f) optimal_frame_count(%f) part_target(%f) optimal_part_target(%f)", track->GetId(), track->GetFrameRate(), frame_duration_ms, optimal_frame_count, part_target, optimal_part_target); + } + + return optimal_part_target; +} + // Create and Get fMP4 packager with track info, storage and packager_config bool LLHlsStream::AddPackager(const std::shared_ptr<const MediaTrack> &media_track, const std::shared_ptr<const MediaTrack> &data_track) { + auto packager_config = _packager_config; + + packager_config.chunk_duration_ms = std::round(ComputeOptimalPartDuration(media_track)); + + logti("LLHlsStream::AddPackager() - Track(%d) ChunkDuration(%f)", media_track->GetId(), packager_config.chunk_duration_ms); + auto cenc_property = _cenc_property; auto tag = ov::String::FormatString("%s/%s", GetApplicationInfo().GetVHostAppName().CStr(), GetName().CStr()); @@ -1120,8 +1159,8 @@ bool LLHlsStream::AddPackager(const std::shared_ptr<const MediaTrack> &media_tra auto storage = std::make_shared<bmff::FMP4Storage>(bmff::FMp4StorageObserver::GetSharedPtr(), media_track, _storage_config, tag); // Create fMP4 Packager - _packager_config.cenc_property = cenc_property; - auto packager = std::make_shared<bmff::FMP4Packager>(storage, media_track, data_track, _packager_config); + packager_config.cenc_property = cenc_property; + auto packager = std::make_shared<bmff::FMP4Packager>(storage, media_track, data_track, packager_config); // Create Initialization Segment if (packager->CreateInitializationSegment() == false) @@ -1142,7 +1181,7 @@ bool LLHlsStream::AddPackager(const std::shared_ptr<const MediaTrack> &media_tra // rounded to the nearest integer number of seconds. auto segment_duration = std::round(static_cast<float_t>(_storage_config.segment_duration_ms) / 1000.0); - auto chunk_duration = static_cast<float_t>(_packager_config.chunk_duration_ms) / 1000.0; + auto chunk_duration = static_cast<float_t>(packager_config.chunk_duration_ms) / 1000.0; auto track_id = media_track->GetId(); auto chunklist = std::make_shared<LLHlsChunklist>(GetChunklistName(track_id), diff --git a/src/projects/publishers/llhls/llhls_stream.h b/src/projects/publishers/llhls/llhls_stream.h index 47b0361f7..46a48b8d0 100755 --- a/src/projects/publishers/llhls/llhls_stream.h +++ b/src/projects/publishers/llhls/llhls_stream.h @@ -137,6 +137,8 @@ class LLHlsStream : public pub::Stream, public bmff::FMp4StorageObserver int64_t GetMinimumLastSegmentNumber() const; bool StopToSaveOldSegmentsInfo(); + double ComputeOptimalPartDuration(const std::shared_ptr<const MediaTrack> &track) const; + ////////////////////////// // Events ////////////////////////// diff --git a/src/projects/transcoder/codec/decoder/decoder_avc_xma.cpp b/src/projects/transcoder/codec/decoder/decoder_avc_xma.cpp index 5a0f7087f..1d6372d64 100644 --- a/src/projects/transcoder/codec/decoder/decoder_avc_xma.cpp +++ b/src/projects/transcoder/codec/decoder/decoder_avc_xma.cpp @@ -46,14 +46,14 @@ bool DecoderAVCxXMA::InitCodec() } // Set the SPS/PPS to extradata - std::shared_ptr<ov::Data> extra_data = nullptr; + std::shared_ptr<const ov::Data> extra_data = nullptr; extra_data = decoder_config != nullptr ? decoder_config->GetData() : nullptr; if (extra_data != nullptr) { _context->extradata_size = extra_data->GetLength(); _context->extradata = (uint8_t *)::av_malloc(_context->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); ::memset(_context->extradata, 0, _context->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - ::memcpy(_context->extradata, reinterpret_cast<const uint8_t *>(extra_data->GetData()), _context->extradata_size); + ::memcpy(_context->extradata, extra_data->GetData(), _context->extradata_size); } ::av_opt_set_int(_context->priv_data, "lxlnx_hwdev", _track->GetCodecDeviceId(), 0); diff --git a/src/projects/transcoder/codec/decoder/decoder_hevc_xma.cpp b/src/projects/transcoder/codec/decoder/decoder_hevc_xma.cpp index fe714e579..0f2de2846 100644 --- a/src/projects/transcoder/codec/decoder/decoder_hevc_xma.cpp +++ b/src/projects/transcoder/codec/decoder/decoder_hevc_xma.cpp @@ -47,14 +47,14 @@ bool DecoderHEVCxXMA::InitCodec() } // Set the SPS/PPS to extradata - std::shared_ptr<ov::Data> extra_data = nullptr; + std::shared_ptr<const ov::Data> extra_data = nullptr; extra_data = decoder_config != nullptr ? decoder_config->GetData() : nullptr; if (extra_data != nullptr) { _context->extradata_size = extra_data->GetLength(); _context->extradata = (uint8_t *)::av_malloc(_context->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); ::memset(_context->extradata, 0, _context->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - ::memcpy(_context->extradata, reinterpret_cast<const uint8_t *>(extra_data->GetData()), _context->extradata_size); + ::memcpy(_context->extradata, extra_data->GetData(), _context->extradata_size); } ::av_opt_set_int(_context->priv_data, "lxlnx_hwdev", _track->GetCodecDeviceId(), 0); diff --git a/src/projects/transcoder/transcoder_stream_internal.cpp b/src/projects/transcoder/transcoder_stream_internal.cpp index bbf0d1270..9db9f76f0 100644 --- a/src/projects/transcoder/transcoder_stream_internal.cpp +++ b/src/projects/transcoder/transcoder_stream_internal.cpp @@ -286,6 +286,7 @@ std::shared_ptr<MediaTrack> TranscoderStreamInternal::CreateOutputTrack(const st output_track->GetSample().SetFormat(input_track->GetSample().GetFormat()); output_track->SetTimeBase(input_track->GetTimeBase()); output_track->SetSampleRate(input_track->GetSampleRate()); + output_track->SetDecoderConfigurationRecord(input_track->GetDecoderConfigurationRecord()); } else {