XDecodeTask.h

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
#pragma once
#include "xtools.h"
#include "xdecode.h"
#include "xcodec.h"

class XDecodeTask : public XThread
{
public:
// 打开解码器
bool Open(AVCodecParameters *para);

// 责任链处理函数
void Do(AVPacket *pkt) override;

// 线程主函数
void Main() override;

// 线程安全,返回当前需要渲染的AVFrame,如果没有返回nullptr
// need_view_控制渲染
// 返回结果需要用 XFreeFrame释放
AVFrame *GetFrame();
private:
std::mutex mux_;
XDecode decode_;
XAVPacketList pkt_list_;
AVFrame *frame_ = nullptr; // 解码后存储
bool need_view_ = false; // 是否需要渲染,每帧只渲染一次,通过GetFrame获取
};

参数说明:

1
2
3
4
5
6
private:
std::mutex mux_;
XDecode decode_;
XAVPacketList pkt_list_;
AVFrame *frame_ = nullptr; // 解码后存储
bool need_view_ = false; // 是否需要渲染,每帧只渲染一次,通过GetFrame获取

XDecodeTask.cpp

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include "xdecodeTask.h"
#include <iostream>

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}

using namespace std;


bool XDecodeTask::Open(AVCodecParameters *para)
{
if (!para)
{
LOGERROR("para is null !!");
return false;
}
unique_lock<mutex> lock(mux_);
auto c = decode_.Create(para->codec_id, false);
if (!c)
{
LOGERROR("decode_.Create failed!");
return false;
}

avcodec_parameters_to_context(c, para);
decode_.set_c(c);
if (!decode_.Open())
{
LOGERROR("decode_.Open failed!");
return false;
}
LOGINFO("Open decode success!");

return true;
}


void XDecodeTask::Do(AVPacket *pkt)
{
cout << "#" << flush;

if (!pkt || pkt->stream_index != 0)
{
return;
}

pkt_list_.Push(pkt);
}


AVFrame *XDecodeTask::GetFrame()
{
unique_lock<mutex> lock(mux_);
if (!need_view_ || !frame_ || !frame_->buf[0]) return nullptr;

auto f = av_frame_alloc();
auto re = av_frame_ref(f, frame_); // 引用加 1
if (re != 0)
{
PrintErr(re);
av_frame_free(&f);
return nullptr;
}
need_view_ = false;
return f;
}


void XDecodeTask::Main()
{
{
unique_lock<mutex> lock(mux_);
if (!frame_)
frame_ = av_frame_alloc();
}
while (!is_exit_)
{
auto pkt = pkt_list_.Pop();
if (!pkt)
{
this_thread::sleep_for(1ms);
continue;
}

bool re = decode_.Send(pkt);
av_packet_free(&pkt);
if (!re)
{
this_thread::sleep_for(1ms);
continue;
}

{
unique_lock<mutex> lock(mux_);
if (decode_.Recv(frame_))
{
cout << "@" << flush;
need_view_ = true;
}
}

this_thread::sleep_for(1ms);
}

{
unique_lock<mutex> lock(mux_);
if (frame_)
av_frame_free(&frame_);
}
}

Open()

作用:初始化视频解码器。

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
bool XDecodeTask::Open(AVCodecParameters *para)
{
if (!para)
{
LOGERROR("para is null !!");
return false;
}
unique_lock<mutex> lock(mux_);

// Create函数在父类XCodec中
// 创建解码器
auto c = decode_.Create(para->codec_id, false);
if (!c)
{
LOGERROR("decode_.Create failed!");
return false;
}

// 使用 avcodec_parameters_to_context 函数,将 para 中的视频参数复制到解码器上下文 c 中。
avcodec_parameters_to_context(c, para);

// 设置编码器上下文
decode_.set_c(c);

// Open函数在父类XCodec中
if (!decode_.Open())
{
LOGERROR("decode_.Open failed!");
return false;
}
LOGINFO("Open decode success!");

return true;
}

Do()

责任链处理函数

作用:该方法用于接收音视频数据包,并将符合条件的数据包存入一个线程安全的列表中。

1
2
3
4
5
6
7
8
9
10
11
void XDecodeTask::Do(AVPacket *pkt)
{
cout << "#" << flush;

if (!pkt || pkt->stream_index != 0) // 判断是否是视频流
{
return;
}

pkt_list_.Push(pkt); // 如果数据包有效且属于视频流
}

GetFrame()

作用:用于获取当前解码的帧(AVFrame

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
AVFrame *XDecodeTask::GetFrame()
{
unique_lock<mutex> lock(mux_);

/*
!need_view_:检查是否需要获取视图帧。如果不需要,则返回 nullptr。
!frame_:检查当前帧是否存在。如果不存在,则返回 nullptr。
!frame_->buf[0]:检查当前帧的第一个缓冲区是否存在。如果不存在,则返回 nullptr。

这些检查确保只有在需要并且有有效帧的情况下才继续处理,否则直接返回 nullptr。
*/
if (!need_view_ || !frame_ || !frame_->buf[0]) return nullptr;

auto f = av_frame_alloc();

// 将当前帧 frame_ 的内容引用到新帧 f 中。这样可以避免数据复制,同时增加引用计数。
auto re = av_frame_ref(f, frame_); // 引用加 1
if (re != 0)
{
PrintErr(re);
av_frame_free(&f);
return nullptr;
}
need_view_ = false; // 表示当前帧已经被获取,不再需要视图帧。
return f;
}

Main()

作用:用于在一个独立的线程中持续接收音视频数据包,将其解码为帧,并在需要时标记为需要渲染。

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
void XDecodeTask::Main()
{
{
unique_lock<mutex> lock(mux_);
if (!frame_)
frame_ = av_frame_alloc();
}
while (!is_exit_)
{
auto pkt = pkt_list_.Pop();
if (!pkt)
{
this_thread::sleep_for(1ms);
continue;
}

// 发送到解码线程
bool re = decode_.Send(pkt);
av_packet_free(&pkt); // 释放数据包的内存。
if (!re)
{
this_thread::sleep_for(1ms);
continue;
}

{
unique_lock<mutex> lock(mux_);
if (decode_.Recv(frame_))
{
cout << "@" << flush;
need_view_ = true; // 需要渲染,在当前类GetFrame中用于判断
}
}

this_thread::sleep_for(1ms);
}

{
unique_lock<mutex> lock(mux_);
if (frame_)
av_frame_free(&frame_);
}
}