video_replay: add pcapng support
as documented on https://pcapng.com/ Nanosec resolution for timestamps is assumed as described in https://gitlab.com/wireshark/wireshark/-/blob/master/writecap/pcapio.c BUG=webrtc:351327754 Change-Id: Ieec601a33c131908e30e7f7e41ddc89ddc1c36b7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/356461 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42699}
This commit is contained in:
parent
96c1b9c5ea
commit
4ddd931023
@ -305,6 +305,7 @@ rtc_library("rtp_test_utils") {
|
|||||||
"../rtc_base:criticalsection",
|
"../rtc_base:criticalsection",
|
||||||
"../rtc_base:logging",
|
"../rtc_base:logging",
|
||||||
"../rtc_base:macromagic",
|
"../rtc_base:macromagic",
|
||||||
|
"../rtc_base:timeutils",
|
||||||
"../rtc_base/synchronization:mutex",
|
"../rtc_base/synchronization:mutex",
|
||||||
"../rtc_base/system:arch",
|
"../rtc_base/system:arch",
|
||||||
"//third_party/abseil-cpp/absl/strings:string_view",
|
"//third_party/abseil-cpp/absl/strings:string_view",
|
||||||
|
|||||||
@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include "test/rtp_file_reader.h"
|
#include "test/rtp_file_reader.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -21,12 +19,44 @@
|
|||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
#include "rtc_base/system/arch.h"
|
#include "rtc_base/system/arch.h"
|
||||||
|
#include "rtc_base/time_utils.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace {
|
||||||
namespace test {
|
constexpr size_t kRtpDumpFirstLineLength = 80;
|
||||||
|
constexpr uint16_t kRtpDumpPacketHeaderSize = 8;
|
||||||
|
|
||||||
static const size_t kFirstLineLength = 80;
|
enum {
|
||||||
static uint16_t kPacketHeaderSize = 8;
|
kResultFail = -1,
|
||||||
|
kResultSuccess = 0,
|
||||||
|
kResultSkip = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kPcapVersionMajor = 2,
|
||||||
|
kPcapVersionMinor = 4,
|
||||||
|
kLinktypeNull = 0,
|
||||||
|
kLinktypeEthernet = 1,
|
||||||
|
kBsdNullLoopback1 = 0x00000002,
|
||||||
|
kBsdNullLoopback2 = 0x02000000,
|
||||||
|
kEthernetIIHeaderMacSkip = 12,
|
||||||
|
kEthertypeIp = 0x0800,
|
||||||
|
kIpVersion4 = 4,
|
||||||
|
kMinIpHeaderLength = 20,
|
||||||
|
kFragmentOffsetClear = 0x0000,
|
||||||
|
kFragmentOffsetDoNotFragment = 0x4000,
|
||||||
|
kProtocolTcp = 0x06,
|
||||||
|
kProtocolUdp = 0x11,
|
||||||
|
kUdpHeaderLength = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t kMaxReadBufferSize = 4096;
|
||||||
|
constexpr uint32_t kPcapBOMSwapOrder = 0xd4c3b2a1UL;
|
||||||
|
constexpr uint32_t kPcapBOMNoSwapOrder = 0xa1b2c3d4UL;
|
||||||
|
constexpr uint32_t kPcapNgBOMLittleEndian = 0x4d3c2b1aUL;
|
||||||
|
|
||||||
|
constexpr uint32_t kPcapNgSectionHeaderBlock = 0x0a0d0d0aUL;
|
||||||
|
constexpr uint32_t kPcapNgInterfaceDescriptionBlock = 0x00000001LU;
|
||||||
|
constexpr uint32_t kPcapNgPacketBlock = 0x00000006LU;
|
||||||
|
|
||||||
#define TRY(expr) \
|
#define TRY(expr) \
|
||||||
do { \
|
do { \
|
||||||
@ -36,6 +66,17 @@ static uint16_t kPacketHeaderSize = 8;
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define TRY_PCAP(expr) \
|
||||||
|
do { \
|
||||||
|
int r = (expr); \
|
||||||
|
if (r == kResultFail) { \
|
||||||
|
RTC_LOG(LS_INFO) << "FAIL at " << __FILE__ << ":" << __LINE__; \
|
||||||
|
return kResultFail; \
|
||||||
|
} else if (r == kResultSkip) { \
|
||||||
|
return kResultSkip; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
bool ReadUint32(uint32_t* out, FILE* file) {
|
bool ReadUint32(uint32_t* out, FILE* file) {
|
||||||
*out = 0;
|
*out = 0;
|
||||||
for (size_t i = 0; i < 4; ++i) {
|
for (size_t i = 0; i < 4; ++i) {
|
||||||
@ -60,6 +101,11 @@ bool ReadUint16(uint16_t* out, FILE* file) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
class RtpFileReaderImpl : public RtpFileReader {
|
class RtpFileReaderImpl : public RtpFileReader {
|
||||||
public:
|
public:
|
||||||
virtual bool Init(FILE* file, const std::set<uint32_t>& ssrc_filter) = 0;
|
virtual bool Init(FILE* file, const std::set<uint32_t>& ssrc_filter) = 0;
|
||||||
@ -123,8 +169,8 @@ class RtpDumpReader : public RtpFileReaderImpl {
|
|||||||
bool Init(FILE* file, const std::set<uint32_t>& ssrc_filter) override {
|
bool Init(FILE* file, const std::set<uint32_t>& ssrc_filter) override {
|
||||||
file_ = file;
|
file_ = file;
|
||||||
|
|
||||||
char firstline[kFirstLineLength + 1] = {0};
|
char firstline[kRtpDumpFirstLineLength + 1] = {0};
|
||||||
if (fgets(firstline, kFirstLineLength, file_) == nullptr) {
|
if (fgets(firstline, kRtpDumpFirstLineLength, file_) == nullptr) {
|
||||||
RTC_LOG(LS_INFO) << "Can't read from file";
|
RTC_LOG(LS_INFO) << "Can't read from file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -139,7 +185,8 @@ class RtpDumpReader : public RtpFileReaderImpl {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RTC_LOG(LS_INFO) << "Wrong file format of input file";
|
RTC_LOG(LS_INFO)
|
||||||
|
<< "Input file is neither in rtpplay nor RTPencode format";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +216,7 @@ class RtpDumpReader : public RtpFileReaderImpl {
|
|||||||
TRY(ReadUint32(&offset, file_));
|
TRY(ReadUint32(&offset, file_));
|
||||||
|
|
||||||
// Use 'len' here because a 'plen' of 0 specifies rtcp.
|
// Use 'len' here because a 'plen' of 0 specifies rtcp.
|
||||||
len -= kPacketHeaderSize;
|
len -= kRtpDumpPacketHeaderSize;
|
||||||
if (packet->length < len) {
|
if (packet->length < len) {
|
||||||
RTC_LOG(LS_ERROR) << "Packet is too large to fit: " << len << " bytes vs "
|
RTC_LOG(LS_ERROR) << "Packet is too large to fit: " << len << " bytes vs "
|
||||||
<< packet->length
|
<< packet->length
|
||||||
@ -191,45 +238,10 @@ class RtpDumpReader : public RtpFileReaderImpl {
|
|||||||
FILE* file_;
|
FILE* file_;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
kResultFail = -1,
|
|
||||||
kResultSuccess = 0,
|
|
||||||
kResultSkip = 1,
|
|
||||||
|
|
||||||
kPcapVersionMajor = 2,
|
|
||||||
kPcapVersionMinor = 4,
|
|
||||||
kLinktypeNull = 0,
|
|
||||||
kLinktypeEthernet = 1,
|
|
||||||
kBsdNullLoopback1 = 0x00000002,
|
|
||||||
kBsdNullLoopback2 = 0x02000000,
|
|
||||||
kEthernetIIHeaderMacSkip = 12,
|
|
||||||
kEthertypeIp = 0x0800,
|
|
||||||
kIpVersion4 = 4,
|
|
||||||
kMinIpHeaderLength = 20,
|
|
||||||
kFragmentOffsetClear = 0x0000,
|
|
||||||
kFragmentOffsetDoNotFragment = 0x4000,
|
|
||||||
kProtocolTcp = 0x06,
|
|
||||||
kProtocolUdp = 0x11,
|
|
||||||
kUdpHeaderLength = 8,
|
|
||||||
kMaxReadBufferSize = 4096
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint32_t kPcapBOMSwapOrder = 0xd4c3b2a1UL;
|
|
||||||
const uint32_t kPcapBOMNoSwapOrder = 0xa1b2c3d4UL;
|
|
||||||
|
|
||||||
#define TRY_PCAP(expr) \
|
|
||||||
do { \
|
|
||||||
int r = (expr); \
|
|
||||||
if (r == kResultFail) { \
|
|
||||||
RTC_LOG(LS_INFO) << "FAIL at " << __FILE__ << ":" << __LINE__; \
|
|
||||||
return kResultFail; \
|
|
||||||
} else if (r == kResultSkip) { \
|
|
||||||
return kResultSkip; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// Read RTP packets from file in tcpdump/libpcap format, as documented at:
|
// Read RTP packets from file in tcpdump/libpcap format, as documented at:
|
||||||
// http://wiki.wireshark.org/Development/LibpcapFileFormat
|
// http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||||
|
// Transparently supports PCAPNG as described at
|
||||||
|
// https://pcapng.com/
|
||||||
class PcapReader : public RtpFileReaderImpl {
|
class PcapReader : public RtpFileReaderImpl {
|
||||||
public:
|
public:
|
||||||
PcapReader()
|
PcapReader()
|
||||||
@ -240,6 +252,7 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
#else
|
#else
|
||||||
swap_network_byte_order_(true),
|
swap_network_byte_order_(true),
|
||||||
#endif
|
#endif
|
||||||
|
pcapng_(false),
|
||||||
read_buffer_(),
|
read_buffer_(),
|
||||||
packets_by_ssrc_(),
|
packets_by_ssrc_(),
|
||||||
packets_(),
|
packets_(),
|
||||||
@ -263,42 +276,30 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
int Initialize(FILE* file, const std::set<uint32_t>& ssrc_filter) {
|
int Initialize(FILE* file, const std::set<uint32_t>& ssrc_filter) {
|
||||||
file_ = file;
|
file_ = file;
|
||||||
|
|
||||||
|
size_t total_packet_count = 0;
|
||||||
if (ReadGlobalHeader() < 0) {
|
if (ReadGlobalHeader() < 0) {
|
||||||
return kResultFail;
|
return kResultFail;
|
||||||
}
|
}
|
||||||
|
int result;
|
||||||
int total_packet_count = 0;
|
if (!pcapng_) {
|
||||||
uint32_t stream_start_ms = 0;
|
result = ReadPcap(ssrc_filter, total_packet_count);
|
||||||
int32_t next_packet_pos = ftell(file_);
|
} else {
|
||||||
for (;;) {
|
result = ReadPcapNg(ssrc_filter, total_packet_count);
|
||||||
TRY_PCAP(fseek(file_, next_packet_pos, SEEK_SET));
|
|
||||||
int result = ReadPacket(&next_packet_pos, stream_start_ms,
|
|
||||||
++total_packet_count, ssrc_filter);
|
|
||||||
if (result == kResultFail) {
|
|
||||||
break;
|
|
||||||
} else if (result == kResultSuccess && packets_.size() == 1) {
|
|
||||||
RTC_DCHECK_EQ(stream_start_ms, 0);
|
|
||||||
PacketIterator it = packets_.begin();
|
|
||||||
stream_start_ms = it->time_offset_ms;
|
|
||||||
it->time_offset_ms = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (result == kResultFail) {
|
||||||
if (feof(file_) == 0) {
|
|
||||||
printf("Failed reading file!\n");
|
|
||||||
return kResultFail;
|
return kResultFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Total packets in file: %d\n", total_packet_count);
|
RTC_LOG(LS_INFO) << "Total packets in file: " << total_packet_count;
|
||||||
printf("Total RTP/RTCP packets: %zu\n", packets_.size());
|
RTC_LOG(LS_INFO) << "Total RTP/RTCP packets: " << packets_.size();
|
||||||
|
|
||||||
for (SsrcMapIterator mit = packets_by_ssrc_.begin();
|
for (SsrcMapIterator mit = packets_by_ssrc_.begin();
|
||||||
mit != packets_by_ssrc_.end(); ++mit) {
|
mit != packets_by_ssrc_.end(); ++mit) {
|
||||||
uint32_t ssrc = mit->first;
|
uint32_t ssrc = mit->first;
|
||||||
const std::vector<uint32_t>& packet_indices = mit->second;
|
const std::vector<uint32_t>& packet_indices = mit->second;
|
||||||
int pt = packets_[packet_indices[0]].payload_type;
|
int pt = packets_[packet_indices[0]].payload_type;
|
||||||
printf("SSRC: %08x, %zu packets, pt=%d\n", ssrc, packet_indices.size(),
|
RTC_LOG(LS_INFO) << "SSRC: " << ssrc << ", " << packet_indices.size()
|
||||||
pt);
|
<< " packets, pt=" << pt << ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(solenberg): Better validation of identified SSRC streams.
|
// TODO(solenberg): Better validation of identified SSRC streams.
|
||||||
@ -321,6 +322,56 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
return kResultSuccess;
|
return kResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ReadPcap(const std::set<uint32_t>& ssrc_filter,
|
||||||
|
size_t& total_packet_count) {
|
||||||
|
uint32_t stream_start_ms = 0;
|
||||||
|
int32_t next_packet_pos = ftell(file_);
|
||||||
|
for (;;) {
|
||||||
|
TRY_PCAP(fseek(file_, next_packet_pos, SEEK_SET));
|
||||||
|
int result = ReadPacket(&next_packet_pos, stream_start_ms, ssrc_filter);
|
||||||
|
if (result == kResultFail) {
|
||||||
|
break;
|
||||||
|
} else if (result == kResultSuccess && packets_.size() == 1) {
|
||||||
|
RTC_DCHECK_EQ(stream_start_ms, 0);
|
||||||
|
PacketIterator it = packets_.begin();
|
||||||
|
stream_start_ms = it->time_offset_ms;
|
||||||
|
it->time_offset_ms = 0;
|
||||||
|
}
|
||||||
|
total_packet_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feof(file_) == 0) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed reading file!";
|
||||||
|
return kResultFail;
|
||||||
|
}
|
||||||
|
return kResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadPcapNg(const std::set<uint32_t>& ssrc_filter,
|
||||||
|
size_t& total_packet_count) {
|
||||||
|
uint32_t stream_start_ms = 0;
|
||||||
|
int next_packet_pos = 0;
|
||||||
|
for (;;) {
|
||||||
|
TRY_PCAP(fseek(file_, next_packet_pos, SEEK_SET));
|
||||||
|
int result = ReadPacketNg(&next_packet_pos, stream_start_ms, ssrc_filter);
|
||||||
|
|
||||||
|
if (result == kResultFail) {
|
||||||
|
break;
|
||||||
|
} else if (result == kResultSuccess && packets_.size() == 1) {
|
||||||
|
RTC_DCHECK_EQ(stream_start_ms, 0);
|
||||||
|
PacketIterator it = packets_.begin();
|
||||||
|
stream_start_ms = it->time_offset_ms;
|
||||||
|
it->time_offset_ms = 0;
|
||||||
|
}
|
||||||
|
total_packet_count++;
|
||||||
|
}
|
||||||
|
if (feof(file_) == 0) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed reading file!";
|
||||||
|
return kResultFail;
|
||||||
|
}
|
||||||
|
return kResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
bool NextPacket(RtpPacket* packet) override {
|
bool NextPacket(RtpPacket* packet) override {
|
||||||
uint32_t length = RtpPacket::kMaxPacketBufferSize;
|
uint32_t length = RtpPacket::kMaxPacketBufferSize;
|
||||||
if (NextPcap(packet->data, &length, &packet->time_ms) != kResultSuccess)
|
if (NextPcap(packet->data, &length, &packet->time_ms) != kResultSuccess)
|
||||||
@ -353,7 +404,6 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
private:
|
private:
|
||||||
// A marker of an RTP packet within the file.
|
// A marker of an RTP packet within the file.
|
||||||
struct RtpPacketMarker {
|
struct RtpPacketMarker {
|
||||||
uint32_t packet_number; // One-based index (like in WireShark)
|
|
||||||
uint32_t time_offset_ms;
|
uint32_t time_offset_ms;
|
||||||
uint32_t source_ip;
|
uint32_t source_ip;
|
||||||
uint32_t dest_ip;
|
uint32_t dest_ip;
|
||||||
@ -377,6 +427,10 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
swap_pcap_byte_order_ = true;
|
swap_pcap_byte_order_ = true;
|
||||||
} else if (magic == kPcapBOMNoSwapOrder) {
|
} else if (magic == kPcapBOMNoSwapOrder) {
|
||||||
swap_pcap_byte_order_ = false;
|
swap_pcap_byte_order_ = false;
|
||||||
|
} else if (magic == kPcapNgSectionHeaderBlock) {
|
||||||
|
pcapng_ = true;
|
||||||
|
RTC_LOG(LS_INFO) << "PCAPNG detected, support is experimental";
|
||||||
|
return kResultSuccess;
|
||||||
} else {
|
} else {
|
||||||
return kResultFail;
|
return kResultFail;
|
||||||
}
|
}
|
||||||
@ -408,36 +462,9 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
return kResultSuccess;
|
return kResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadPacket(int32_t* next_packet_pos,
|
int ProcessPacket(RtpPacketMarker& marker,
|
||||||
uint32_t stream_start_ms,
|
const std::set<uint32_t>& ssrc_filter,
|
||||||
uint32_t number,
|
rtc::ArrayView<const uint8_t> packet) {
|
||||||
const std::set<uint32_t>& ssrc_filter) {
|
|
||||||
RTC_DCHECK(next_packet_pos);
|
|
||||||
|
|
||||||
uint32_t ts_sec; // Timestamp seconds.
|
|
||||||
uint32_t ts_usec; // Timestamp microseconds.
|
|
||||||
uint32_t incl_len; // Number of octets of packet saved in file.
|
|
||||||
uint32_t orig_len; // Actual length of packet.
|
|
||||||
TRY_PCAP(Read(&ts_sec, false));
|
|
||||||
TRY_PCAP(Read(&ts_usec, false));
|
|
||||||
TRY_PCAP(Read(&incl_len, false));
|
|
||||||
TRY_PCAP(Read(&orig_len, false));
|
|
||||||
|
|
||||||
*next_packet_pos = ftell(file_) + incl_len;
|
|
||||||
|
|
||||||
RtpPacketMarker marker = {0};
|
|
||||||
marker.packet_number = number;
|
|
||||||
marker.time_offset_ms = CalcTimeDelta(ts_sec, ts_usec, stream_start_ms);
|
|
||||||
TRY_PCAP(ReadPacketHeader(&marker));
|
|
||||||
marker.pos_in_file = ftell(file_);
|
|
||||||
|
|
||||||
if (marker.payload_length > sizeof(read_buffer_)) {
|
|
||||||
printf("Packet too large!\n");
|
|
||||||
return kResultFail;
|
|
||||||
}
|
|
||||||
TRY_PCAP(Read(read_buffer_, marker.payload_length));
|
|
||||||
|
|
||||||
rtc::ArrayView<const uint8_t> packet(read_buffer_, marker.payload_length);
|
|
||||||
if (IsRtcpPacket(packet)) {
|
if (IsRtcpPacket(packet)) {
|
||||||
marker.payload_type = packet[1];
|
marker.payload_type = packet[1];
|
||||||
packets_.push_back(marker);
|
packets_.push_back(marker);
|
||||||
@ -459,6 +486,98 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
return kResultSuccess;
|
return kResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ReadPacket(int32_t* next_packet_pos,
|
||||||
|
uint32_t stream_start_ms,
|
||||||
|
const std::set<uint32_t>& ssrc_filter) {
|
||||||
|
RTC_DCHECK(next_packet_pos);
|
||||||
|
|
||||||
|
uint32_t ts_sec; // Timestamp seconds.
|
||||||
|
uint32_t ts_usec; // Timestamp microseconds.
|
||||||
|
uint32_t incl_len; // Number of octets of packet saved in file.
|
||||||
|
uint32_t orig_len; // Actual length of packet.
|
||||||
|
TRY_PCAP(Read(&ts_sec, false));
|
||||||
|
TRY_PCAP(Read(&ts_usec, false));
|
||||||
|
TRY_PCAP(Read(&incl_len, false));
|
||||||
|
TRY_PCAP(Read(&orig_len, false));
|
||||||
|
|
||||||
|
*next_packet_pos = ftell(file_) + incl_len;
|
||||||
|
|
||||||
|
RtpPacketMarker marker = {0};
|
||||||
|
marker.time_offset_ms = CalcTimeDelta(ts_sec, ts_usec, stream_start_ms);
|
||||||
|
TRY_PCAP(ReadPacketHeader(&marker));
|
||||||
|
marker.pos_in_file = ftell(file_);
|
||||||
|
|
||||||
|
if (marker.payload_length > sizeof(read_buffer_)) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Packet too large!";
|
||||||
|
return kResultFail;
|
||||||
|
}
|
||||||
|
TRY_PCAP(Read(read_buffer_, marker.payload_length));
|
||||||
|
return ProcessPacket(marker, ssrc_filter,
|
||||||
|
{read_buffer_, marker.payload_length});
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadPacketNg(int32_t* next_packet_pos,
|
||||||
|
uint32_t stream_start_ms,
|
||||||
|
const std::set<uint32_t>& ssrc_filter) {
|
||||||
|
uint32_t block_type;
|
||||||
|
uint32_t block_length;
|
||||||
|
TRY_PCAP(Read(&block_type, false));
|
||||||
|
TRY_PCAP(Read(&block_length, false));
|
||||||
|
if (block_length == 0) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Empty PCAPNG block";
|
||||||
|
return kResultFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
*next_packet_pos += block_length;
|
||||||
|
switch (block_type) {
|
||||||
|
case kPcapNgSectionHeaderBlock: {
|
||||||
|
// TODO: https://issues.webrtc.org/issues/351327754 - interpret more of
|
||||||
|
// this block, in particular the if_tsresol option.
|
||||||
|
uint32_t byte_order_magic;
|
||||||
|
TRY_PCAP(Read(&byte_order_magic, false));
|
||||||
|
swap_pcap_byte_order_ = (byte_order_magic == kPcapNgBOMLittleEndian);
|
||||||
|
} break;
|
||||||
|
case kPcapNgInterfaceDescriptionBlock:
|
||||||
|
break;
|
||||||
|
case kPcapNgPacketBlock: {
|
||||||
|
uint32_t interface; // Interface ID. Unused.
|
||||||
|
uint32_t ts_upper; // Upper 32 bits of timestamp.
|
||||||
|
uint32_t ts_lower; // Lower 32 bits of timestamp.
|
||||||
|
uint32_t incl_len; // Number of octets of packet saved in file.
|
||||||
|
uint32_t orig_len; // Actual length of packet.
|
||||||
|
TRY_PCAP(Read(&interface, false));
|
||||||
|
TRY_PCAP(Read(&ts_upper, false));
|
||||||
|
TRY_PCAP(Read(&ts_lower, false));
|
||||||
|
TRY_PCAP(Read(&incl_len, false));
|
||||||
|
TRY_PCAP(Read(&orig_len, false));
|
||||||
|
|
||||||
|
RtpPacketMarker marker = {0};
|
||||||
|
// Note: Wireshark writes nanoseconds most of the time, see comments in
|
||||||
|
// it's pcapio.c. We are only interesting in the time difference so
|
||||||
|
// truncating to uint32_t is ok.
|
||||||
|
uint64_t timestamp_ms =
|
||||||
|
((static_cast<uint64_t>(ts_upper) << 32) | ts_lower) /
|
||||||
|
rtc::kNumMicrosecsPerSec;
|
||||||
|
marker.time_offset_ms =
|
||||||
|
static_cast<uint32_t>(timestamp_ms) - stream_start_ms;
|
||||||
|
TRY_PCAP(ReadPacketHeader(&marker));
|
||||||
|
marker.pos_in_file = ftell(file_);
|
||||||
|
if (marker.payload_length > sizeof(read_buffer_)) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Packet too large!";
|
||||||
|
return kResultFail;
|
||||||
|
}
|
||||||
|
TRY_PCAP(Read(read_buffer_, marker.payload_length));
|
||||||
|
if (ProcessPacket(marker, ssrc_filter,
|
||||||
|
{read_buffer_, marker.payload_length}) !=
|
||||||
|
kResultSuccess) {
|
||||||
|
return kResultFail;
|
||||||
|
}
|
||||||
|
return kResultSuccess;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return kResultSkip;
|
||||||
|
}
|
||||||
|
|
||||||
int ReadPacketHeader(RtpPacketMarker* marker) {
|
int ReadPacketHeader(RtpPacketMarker* marker) {
|
||||||
int32_t file_pos = ftell(file_);
|
int32_t file_pos = ftell(file_);
|
||||||
|
|
||||||
@ -470,7 +589,6 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
TRY_PCAP(Read(&protocol, true));
|
TRY_PCAP(Read(&protocol, true));
|
||||||
if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) {
|
if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) {
|
||||||
int result = ReadXxpIpHeader(marker);
|
int result = ReadXxpIpHeader(marker);
|
||||||
RTC_LOG(LS_INFO) << "Recognized loopback frame";
|
|
||||||
if (result != kResultSkip) {
|
if (result != kResultSkip) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -484,7 +602,6 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
TRY_PCAP(Read(&type, true));
|
TRY_PCAP(Read(&type, true));
|
||||||
if (type == kEthertypeIp) {
|
if (type == kEthertypeIp) {
|
||||||
int result = ReadXxpIpHeader(marker);
|
int result = ReadXxpIpHeader(marker);
|
||||||
RTC_LOG(LS_INFO) << "Recognized ethernet 2 frame";
|
|
||||||
if (result != kResultSkip) {
|
if (result != kResultSkip) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -617,6 +734,7 @@ class PcapReader : public RtpFileReaderImpl {
|
|||||||
FILE* file_;
|
FILE* file_;
|
||||||
bool swap_pcap_byte_order_;
|
bool swap_pcap_byte_order_;
|
||||||
const bool swap_network_byte_order_;
|
const bool swap_network_byte_order_;
|
||||||
|
bool pcapng_;
|
||||||
uint8_t read_buffer_[kMaxReadBufferSize];
|
uint8_t read_buffer_[kMaxReadBufferSize];
|
||||||
|
|
||||||
SsrcMap packets_by_ssrc_;
|
SsrcMap packets_by_ssrc_;
|
||||||
@ -648,7 +766,7 @@ RtpFileReader* RtpFileReader::Create(FileFormat format,
|
|||||||
|
|
||||||
FILE* file = tmpfile();
|
FILE* file = tmpfile();
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
printf("ERROR: Can't open file from memory buffer\n");
|
RTC_LOG(LS_ERROR) << "ERROR: Can't open file from memory buffer.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -671,7 +789,7 @@ RtpFileReader* RtpFileReader::Create(FileFormat format,
|
|||||||
std::string filename_str = std::string(filename);
|
std::string filename_str = std::string(filename);
|
||||||
FILE* file = fopen(filename_str.c_str(), "rb");
|
FILE* file = fopen(filename_str.c_str(), "rb");
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
printf("ERROR: Can't open file: %s\n", filename_str.c_str());
|
RTC_LOG(LS_ERROR) << "ERROR: Can't open file: '" << filename_str << "'.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user