XDemux.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma once
#include "xformat.h"
class XDemux : public XFormat
{
public:
// 打开解封装 解封装地址支持rtsp 失败返回nullptr
static AVFormatContext *Open(const char *url);

// 读取一帧数据
// pkt: 输出数据
// return 是否成功
bool Read(AVPacket *pkt);

bool Seek(long long pts, int stream_index);
};


XDemux.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
#include "xdemux.h"
#include <iostream>
#include <thread>
#include "xtools.h"

using namespace std;

extern "C" {
#include <libavformat\avformat.h>
}

static void PrintErr(int err)
{
char buf[1024] = { 0 };
av_strerror(err, buf, sizeof(buf) - 1);
cerr << buf << endl;
}
#define BERR(err) if(err != 0) {PrintErr(err); return 0;}


AVFormatContext *XDemux::Open(const char *url)
{
AVFormatContext *c = nullptr;
AVDictionary *opts = nullptr;

av_dict_set(&opts, "stimeout", "1000000", 0);

auto re = avformat_open_input(&c, url, nullptr, &opts);

if (opts)
av_dict_free(&opts);

BERR(re);

re = avformat_find_stream_info(c, nullptr);
BERR(re);

av_dump_format(c, 0, url, 0);

return c;
}


bool XDemux::Read(AVPacket *pkt)
{
unique_lock<mutex> lock(mux_);
if (!c_) return false;

auto re = av_read_frame(c_, pkt);
BERR(re);

last_time_ = NowMs();

return true;
}


bool XDemux::Seek(long long pts, int stream_index)
{
unique_lock<mutex> lock(mux_);
if (!c_) return false;

auto re = av_seek_frame(c_, stream_index, pts,
AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
BERR(re);

return true;
}

Open()

作用:通过 Open 方法,XDemux 类能够打开指定 URL 的多路复用器上下文,设置连接选项,获取媒体信息,并打印封装信息。该方法封装了多路复用器上下文的打开、选项设置和错误处理步骤,确保操作的可靠性和可调试性。

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
AVFormatContext *XDemux::Open(const char *url)
{
// 打开封装上下文
AVFormatContext *c = nullptr; // 用于存储多路复用器上下文。
AVDictionary *opts = nullptr; // 用于存储打开输入时的选项字典。

//av_dict_set(&opts, "rtsp_transport", "tcp", 0); // 传输媒体流为tcp协议,默认udp
av_dict_set(&opts, "stimeout", "1000000", 0); // 给字典opts设置连接超时1秒

// 尝试打开指定 URL 的输入上下文,并将选项字典 opts 传递给它。
// 如果成功,c 将指向打开的多路复用器上下文。
auto re = avformat_open_input(&c, url, nullptr, &opts);

if (opts)
av_dict_free(&opts); // 调用 av_dict_free 函数释放选项字典。

BERR(re);

// 获取媒体信息
re = avformat_find_stream_info(c, nullptr);
BERR(re);

// 打印输入封装信息
av_dump_format(c, 0, url, 0);

return c;
}

Read()

作用:通过 Read 方法,XDemux 类能够从多路复用器中读取一帧数据并存储在提供的 AVPacket 对象中。该方法通过线程安全的资源访问、错误处理和时间戳记录,确保读取操作的可靠性和可调试性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool XDemux::Read(AVPacket *pkt)
{
unique_lock<mutex> lock(mux_);
if (!c_) return false;

/*
调用 av_read_frame 函数从多路复用器上下文 c_ 中读取一帧数据,
并将其存储在 pkt 中。
av_read_frame 函数返回一个整数值 re,表示函数调用的结果。
使用宏 BERR(re) 检查返回值 re 是否为 0(成功)。
*/
auto re = av_read_frame(c_, pkt);
BERR(re);

// 计时 用于超时判断
last_time_ = NowMs();

return true;
}

Seek()

作用:这个方法通常用于在打开的多路复用器中进行时间跳转,例如在视频播放器中用户拖动进度条时调用。在实际应用中,可以在一个循环中调用 Seek 方法,定位到指定位置并读取相应的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool XDemux::Seek(long long pts, int stream_index)
{
unique_lock<mutex> lock(mux_);
if (!c_) return false;

/*
调用 av_seek_frame 函数,将多路复用器上下文 c_ 定位到指定的时间戳 pts 和流索引 stream_index。
使用的标志:
AVSEEK_FLAG_FRAME:表示按帧进行定位。
AVSEEK_FLAG_BACKWARD:表示允许向后搜索关键帧。
av_seek_frame 函数返回一个整数值 re,表示函数调用的结果。
使用宏 BERR(re) 检查返回值 re 是否为 0(成功)。
*/
auto re = av_seek_frame(c_,
stream_index,
pts,
AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
BERR(re);

return true;
}