富贵山庄

恒者行远,思者常新

在 Android 平台上,MediaCodec 提供了一套 C API(通常通过 NDK 使用)来进行视频和音频的编解码。这个 API 允许开发者直接在 C/C++ 层面操作编解码器,而不是通过 Java 层的 MediaCodec 类。使用 C API 可以更好地控制性能,特别是在需要处理高效率、低延迟的多媒体应用时。

MediaCodec 编解码流程

解码

创建解码器

使用 AMediaCodec_createDecoderByType 创建解码器实例。

获取解码器名称

1
2
3
4
5
6
7
8
9
10
11
12
std::string mime_type;
switch (src) {
case CodecType::CODEC_TYPE_H264:
mime_type = "video/avc";
break;
case CodecType::CODEC_TYPE_H265:
mime_type = "video/hevc";
break;
default:
break;
}
return mime_type;

设置解码格式参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
AMediaFormat* MediaCodecDecoderImpl::CreateMediaFormat(
const VideoInfo& video_info, const ExtraData& extra_data) {
std::string mime_type = CodecTypeConvert<CodecType, std::string>(video_info.codec_type);

AMediaFormat* format = AMediaFormat_new();
// 设置宽高,解码名称
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, video_info.width);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, video_info.height);
AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime_type.c_str());
// 设置输出的颜色格式
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, pix_fmt);
// 通过csd-0 csd-1 csd-2来设置vps sps pps数据
switch (video_info.codec_type) {
case CodecType::CODEC_TYPE_H264: {
auto* avc_ps_ = new avc::ParamSets();
himawari::avc::ParseExtraDataToParamSet(extra_data, *avc_ps_);
if (avc_ps_->sps_list.empty() || avc_ps_->pps_list.empty()) {
return nullptr;
}
AMediaFormat_setBuffer(format, "csd-0", avc_ps_->sps_list[0].data,
avc_ps_->sps_list[0].data_size);
AMediaFormat_setBuffer(format, "csd-1", avc_ps_->pps_list[0].data,
avc_ps_->pps_list[0].data_size);
delete avc_ps_;
} break;
case CodecType::CODEC_TYPE_H265: {
auto* hevc_ps = new hevc::ParamSets();
himawari::hevc::ParseExtraDataToParamSet(extra_data, *hevc_ps);
if (hevc_ps->vps_list.empty() || hevc_ps->sps_list.empty() ||
hevc_ps->pps_list.empty()) {
return nullptr;
}
AMediaFormat_setBuffer(format, "csd-0", hevc_ps->vps_list[0].data,
hevc_ps->vps_list[0].data_size);
AMediaFormat_setBuffer(format, "csd-1", hevc_ps->sps_list[0].data,
hevc_ps->sps_list[0].data_size);
AMediaFormat_setBuffer(format, "csd-2", hevc_ps->pps_list[0].data,
hevc_ps->pps_list[0].data_size);
delete hevc_ps;
} break;
default:
return nullptr;
}
return format;
}
阅读全文 »

在 iOS 平台上,VideoToolbox 是 Apple 提供的一个强大的框架,用于执行硬件加速的视频编解码。它支持广泛的视频编码格式,包括但不限于 H.264 和 HEVC。使用 VideoToolbox 可以提高编解码的性能,同时降低 CPU 使用率。

VideoToolbox 编解码流程

解码

创建解码器

设置视频描述信息

将vps sps pps信息读取出来设置到CMVideoFormatDescriptionRef中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
CMVideoFormatDescriptionRef VideoToolBoxDecoderImpl::CreateVideoFormatDesc(
const VideoInfo &video_info, const ExtraData &extra_data) {
OSStatus status;
CMFormatDescriptionRef format_desc;
switch (video_info.codec_type) {
case CodecType::CODEC_TYPE_H264: {
auto *avc_ps_ = new avc::ParamSets();
...
// 准备好sps pps数据
const uint8_t *const parameterSetPointers[] = {avc_ps_->sps_list[0].data,
avc_ps_->pps_list[0].data};
const size_t parameterSetSizes[] = {
(size_t)avc_ps_->sps_list[0].data_size,
(size_t)avc_ps_->pps_list[0].data_size};
status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
kCFAllocatorDefault, 2, parameterSetPointers, parameterSetSizes, 4,
&format_desc);
delete avc_ps_;
} break;
case CodecType::CODEC_TYPE_H265: {
auto *hevc_ps = new hevc::ParamSets();
...
// 准备好vps sps pps数据
if (hevc_ps->vps_list.empty() || hevc_ps->sps_list.empty() ||
hevc_ps->pps_list.empty()) {
return nullptr;
}
const uint8_t *const parameterSetPointers[] = {hevc_ps->vps_list[0].data,
hevc_ps->sps_list[0].data,
hevc_ps->pps_list[0].data};
const size_t parameterSetSizes[] = {
(size_t)hevc_ps->vps_list[0].data_size,
(size_t)hevc_ps->sps_list[0].data_size,
(size_t)hevc_ps->pps_list[0].data_size};
status = CMVideoFormatDescriptionCreateFromHEVCParameterSets(
kCFAllocatorDefault, 3, parameterSetPointers, parameterSetSizes, 4,
nil, &format_desc);
delete hevc_ps;
} break;
default:
return nullptr;
}

if (status) {
return nullptr;
}
return format_desc;
}
阅读全文 »

Lookahead

Lookahead是预处理模块,负责对输入的待编码图像进行下采样、复杂度预计算、帧类型决策等编码前期处理。

在Lookahead中维护了两个队列,外部输入的待编码图像按照输入的顺序放入到m_inputQueue队列中等待预处理,当m_inputQueue中数量超过指定阈值时,就会开始预处理,预处理完毕的就会根据DTS顺序放入到m_outputQueue中等待取出编码。

1
2
3
4
5
6
7
8
class Lookahead : public JobProvider
{
public:
// 输入队列,PTS顺序
PicList m_inputQueue; // input pictures in order received
// 输出队列,DTS顺序
PicList m_outputQueue; // pictures to be encoded, in encode order
}
阅读全文 »

std::allocator

默认的内存分配器,负责内存的分配和销毁

函数

allocate

分配内存

1
2
3
4
5
6
7
8
9
10
11
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
const _Tp* allocate(size_t __n) {
if (__n > allocator_traits<allocator>::max_size(*this))
__throw_length_error("allocator<const T>::allocate(size_t n)"
" 'n' exceeds maximum supported size");
if (__libcpp_is_constant_evaluated()) {
return static_cast<const _Tp*>(::operator new(__n * sizeof(_Tp)));
} else {
return static_cast<const _Tp*>(_VSTD::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp)));
}
}
阅读全文 »

std::vector

数据结构

__vector_base

vector的基类,定义了vector的数据结构,在vector内部是使用对象数组来实现的,end - begin表示size,end_cap - begin表示capacity。

1
2
3
4
5
6
7
8
class __vector_base
: protected __vector_base_common<true>
{
public:
pointer __begin_; // 数组第一个对象的首地址
pointer __end_; // 数组下一个待存储的对象的首地址
__compressed_pair<pointer, allocator_type> __end_cap_; // 数组内存尾指针
}
阅读全文 »

compressed_pair

与pair相比,compressed_pair提供了空类优化,节省了空类的内存

pair

看pair的实现可以看到,将first和second都作为属性进行保存。

1
2
3
4
5
6
7
8
9
template <class _T1, class _T2>
struct _LIBCPP_TEMPLATE_VIS pair : private __non_trivially_copyable_base<_T1, _T2>
{
typedef _T1 first_type;
typedef _T2 second_type;

_T1 first;
_T2 second;
}
阅读全文 »

std::string

1
typedef basic_string<char, char_traits<char>, allocator<char> > string;

数据结构

在basic_string实现中,对短字符串进行了优化,长短字符长存储的数据结构是不一样的。

1
2
3
4
5
6
7
8
9
10
11
struct __rep
{
union
{
__long __l;
__short __s;
__raw __r;
};
};

__compressed_pair<__rep, allocator_type> __r_;
阅读全文 »

码率控制

视频编码的目标是尽可能多的节省比特的同时尽量保持视频质量,码率控制是平衡码率和质量的重要工具。

码率控制并不是视频编码框架中具体的一个模块,而是在整个视频编码过程中扮演控制着的角色,一方面接收来自编码器外部的要求(编码缓冲区大小、人为要求的目标输出码率),另一方面分析并处理这些要求,为了完成要求对编码参数做出选择(量化参数、编码模式、运动矢量)。

在整个码率控制过程中,最为重要的三个编码参数是量化参数、编码模式、运动矢量。

在工业实现中一般都是选择量化参数建立率失真模型,将量化参数和编码模式、运动矢量分开,先有码率控制模块确定量化参数,再由RDO(率失真优化)确定编码模式和运动矢量。

所以码率控制就是负责量化参数的确定。

阅读全文 »

定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
template<class T>
class shared_ptr
{
public:
typedef T element_type;
typedef weak_ptr<T> weak_type; // C++17

// constructors:
constexpr shared_ptr() noexcept;
template<class Y> explicit shared_ptr(Y* p);
template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);
template<class Y> shared_ptr(const shared_ptr<Y>& r, T *p) noexcept;
shared_ptr(const shared_ptr& r) noexcept;
template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;
shared_ptr(shared_ptr&& r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y>&& r) noexcept;
template<class Y> explicit shared_ptr(const weak_ptr<Y>& r);
template<class Y> shared_ptr(auto_ptr<Y>&& r); // removed in C++17
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
shared_ptr(nullptr_t) : shared_ptr() { }

// destructor:
~shared_ptr();

// assignment:
shared_ptr& operator=(const shared_ptr& r) noexcept;
template<class Y> shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;
shared_ptr& operator=(shared_ptr&& r) noexcept;
template<class Y> shared_ptr& operator=(shared_ptr<Y>&& r);
template<class Y> shared_ptr& operator=(auto_ptr<Y>&& r); // removed in C++17
template <class Y, class D> shared_ptr& operator=(unique_ptr<Y, D>&& r);

// modifiers:
void swap(shared_ptr& r) noexcept;
void reset() noexcept;
template<class Y> void reset(Y* p);
template<class Y, class D> void reset(Y* p, D d);
template<class Y, class D, class A> void reset(Y* p, D d, A a);

// observers:
T* get() const noexcept;
T& operator*() const noexcept;
T* operator->() const noexcept;
long use_count() const noexcept;
bool unique() const noexcept;
explicit operator bool() const noexcept;
template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
};
阅读全文 »

定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 定义类模板,T为要删除的指针类型
template <class T>
struct default_delete
{
constexpr default_delete() noexcept = default;
template <class U> default_delete(const default_delete<U>&) noexcept;
// 重写()操作符,外部调用该操作符将T类型的指针传递进来进行删除
void operator()(T*) const noexcept;
};

// 定义类模板,T为管理的指针类型,D为负责删除指针的结构体类型,默认实现为default_delete
template <class T, class D = default_delete<T>>
class unique_ptr
{
public:
typedef see below pointer; // 默认为T*,除非default_delete里定义了pointer类型
typedef T element_type; // 管理的指针类型
typedef D deleter_type; // 删除指针的结构体类型

// constructors
constexpr unique_ptr() noexcept;
explicit unique_ptr(pointer p) noexcept;
unique_ptr(pointer p, see below d1) noexcept;
unique_ptr(pointer p, see below d2) noexcept;
unique_ptr(unique_ptr&& u) noexcept;
unique_ptr(nullptr_t) noexcept : unique_ptr() { }
template <class U, class E>
unique_ptr(unique_ptr<U, E>&& u) noexcept;
template <class U>
unique_ptr(auto_ptr<U>&& u) noexcept; // removed in C++17

// destructor
~unique_ptr();

// assignment
unique_ptr& operator=(unique_ptr&& u) noexcept;
template <class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
unique_ptr& operator=(nullptr_t) noexcept;

// observers
typename add_lvalue_reference<T>::type operator*() const;
pointer operator->() const noexcept;
pointer get() const noexcept;
deleter_type& get_deleter() noexcept;
const deleter_type& get_deleter() const noexcept;
explicit operator bool() const noexcept;

// modifiers
pointer release() noexcept;
void reset(pointer p = pointer()) noexcept;
void swap(unique_ptr& u) noexcept;
};
阅读全文 »
0%