blob: 0c2ab3c443b4b85ea00e1e749fb8375fdadde72e [file] [log] [blame]
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "RTPFile.h"
#include <stdlib.h>
#include <limits>
#include "absl/strings/string_view.h"
#ifdef WIN32
#include <Winsock2.h>
#else
#include <arpa/inet.h>
#endif
// TODO(tlegrand): Consider removing usage of gtest.
#include "test/gtest.h"
namespace webrtc {
void RTPStream::ParseRTPHeader(RTPHeader* rtp_header,
const uint8_t* rtpHeader) {
rtp_header->payloadType = rtpHeader[1];
rtp_header->sequenceNumber =
(static_cast<uint16_t>(rtpHeader[2]) << 8) | rtpHeader[3];
rtp_header->timestamp = (static_cast<uint32_t>(rtpHeader[4]) << 24) |
(static_cast<uint32_t>(rtpHeader[5]) << 16) |
(static_cast<uint32_t>(rtpHeader[6]) << 8) |
rtpHeader[7];
rtp_header->ssrc = (static_cast<uint32_t>(rtpHeader[8]) << 24) |
(static_cast<uint32_t>(rtpHeader[9]) << 16) |
(static_cast<uint32_t>(rtpHeader[10]) << 8) |
rtpHeader[11];
}
void RTPStream::MakeRTPheader(uint8_t* rtpHeader,
uint8_t payloadType,
int16_t seqNo,
uint32_t timeStamp,
uint32_t ssrc) {
rtpHeader[0] = 0x80;
rtpHeader[1] = payloadType;
rtpHeader[2] = (seqNo >> 8) & 0xFF;
rtpHeader[3] = seqNo & 0xFF;
rtpHeader[4] = timeStamp >> 24;
rtpHeader[5] = (timeStamp >> 16) & 0xFF;
rtpHeader[6] = (timeStamp >> 8) & 0xFF;
rtpHeader[7] = timeStamp & 0xFF;
rtpHeader[8] = ssrc >> 24;
rtpHeader[9] = (ssrc >> 16) & 0xFF;
rtpHeader[10] = (ssrc >> 8) & 0xFF;
rtpHeader[11] = ssrc & 0xFF;
}
RTPPacket::RTPPacket(uint8_t payloadType,
uint32_t timeStamp,
int16_t seqNo,
const uint8_t* payloadData,
size_t payloadSize,
uint32_t frequency)
: payloadType(payloadType),
timeStamp(timeStamp),
seqNo(seqNo),
payloadSize(payloadSize),
frequency(frequency) {
if (payloadSize > 0) {
this->payloadData = new uint8_t[payloadSize];
memcpy(this->payloadData, payloadData, payloadSize);
}
}
RTPPacket::~RTPPacket() {
delete[] payloadData;
}
void RTPBuffer::Write(const uint8_t payloadType,
const uint32_t timeStamp,
const int16_t seqNo,
const uint8_t* payloadData,
const size_t payloadSize,
uint32_t frequency) {
RTPPacket* packet = new RTPPacket(payloadType, timeStamp, seqNo, payloadData,
payloadSize, frequency);
MutexLock lock(&mutex_);
_rtpQueue.push(packet);
}
size_t RTPBuffer::Read(RTPHeader* rtp_header,
uint8_t* payloadData,
size_t payloadSize,
uint32_t* offset) {
RTPPacket* packet;
{
MutexLock lock(&mutex_);
packet = _rtpQueue.front();
_rtpQueue.pop();
}
rtp_header->markerBit = 1;
rtp_header->payloadType = packet->payloadType;
rtp_header->sequenceNumber = packet->seqNo;
rtp_header->ssrc = 0;
rtp_header->timestamp = packet->timeStamp;
if (packet->payloadSize > 0 && payloadSize >= packet->payloadSize) {
memcpy(payloadData, packet->payloadData, packet->payloadSize);
} else {
return 0;
}
*offset = (packet->timeStamp / (packet->frequency / 1000));
return packet->payloadSize;
}
bool RTPBuffer::EndOfFile() const {
MutexLock lock(&mutex_);
return _rtpQueue.empty();
}
void RTPFile::Open(absl::string_view filename, absl::string_view mode) {
std::string filename_str = std::string(filename);
if ((_rtpFile = fopen(filename_str.c_str(), std::string(mode).c_str())) ==
NULL) {
printf("Cannot write file %s.\n", filename_str.c_str());
ADD_FAILURE() << "Unable to write file";
exit(1);
}
}
void RTPFile::Close() {
if (_rtpFile != NULL) {
fclose(_rtpFile);
_rtpFile = NULL;
}
}
void RTPFile::WriteHeader() {
// Write data in a format that NetEQ and RTP Play can parse
fprintf(_rtpFile, "#!RTPencode%s\n", "1.0");
uint32_t dummy_variable = 0;
// should be converted to network endian format, but does not matter when 0
EXPECT_EQ(1u, fwrite(&dummy_variable, 4, 1, _rtpFile));
EXPECT_EQ(1u, fwrite(&dummy_variable, 4, 1, _rtpFile));
EXPECT_EQ(1u, fwrite(&dummy_variable, 4, 1, _rtpFile));
EXPECT_EQ(1u, fwrite(&dummy_variable, 2, 1, _rtpFile));
EXPECT_EQ(1u, fwrite(&dummy_variable, 2, 1, _rtpFile));
fflush(_rtpFile);
}
void RTPFile::ReadHeader() {
uint32_t start_sec, start_usec, source;
uint16_t port, padding;
char fileHeader[40];
EXPECT_TRUE(fgets(fileHeader, 40, _rtpFile) != 0);
EXPECT_EQ(1u, fread(&start_sec, 4, 1, _rtpFile));
start_sec = ntohl(start_sec);
EXPECT_EQ(1u, fread(&start_usec, 4, 1, _rtpFile));
start_usec = ntohl(start_usec);
EXPECT_EQ(1u, fread(&source, 4, 1, _rtpFile));
source = ntohl(source);
EXPECT_EQ(1u, fread(&port, 2, 1, _rtpFile));
port = ntohs(port);
EXPECT_EQ(1u, fread(&padding, 2, 1, _rtpFile));
padding = ntohs(padding);
}
void RTPFile::Write(const uint8_t payloadType,
const uint32_t timeStamp,
const int16_t seqNo,
const uint8_t* payloadData,
const size_t payloadSize,
uint32_t frequency) {
/* write RTP packet to file */
uint8_t rtpHeader[12];
MakeRTPheader(rtpHeader, payloadType, seqNo, timeStamp, 0);
ASSERT_LE(12 + payloadSize + 8, std::numeric_limits<u_short>::max());
uint16_t lengthBytes = htons(static_cast<u_short>(12 + payloadSize + 8));
uint16_t plen = htons(static_cast<u_short>(12 + payloadSize));
uint32_t offsetMs;
offsetMs = (timeStamp / (frequency / 1000));
offsetMs = htonl(offsetMs);
EXPECT_EQ(1u, fwrite(&lengthBytes, 2, 1, _rtpFile));
EXPECT_EQ(1u, fwrite(&plen, 2, 1, _rtpFile));
EXPECT_EQ(1u, fwrite(&offsetMs, 4, 1, _rtpFile));
EXPECT_EQ(1u, fwrite(&rtpHeader, 12, 1, _rtpFile));
EXPECT_EQ(payloadSize, fwrite(payloadData, 1, payloadSize, _rtpFile));
}
size_t RTPFile::Read(RTPHeader* rtp_header,
uint8_t* payloadData,
size_t payloadSize,
uint32_t* offset) {
uint16_t lengthBytes;
uint16_t plen;
uint8_t rtpHeader[12];
size_t read_len = fread(&lengthBytes, 2, 1, _rtpFile);
/* Check if we have reached end of file. */
if ((read_len == 0) && feof(_rtpFile)) {
_rtpEOF = true;
return 0;
}
EXPECT_EQ(1u, fread(&plen, 2, 1, _rtpFile));
EXPECT_EQ(1u, fread(offset, 4, 1, _rtpFile));
lengthBytes = ntohs(lengthBytes);
plen = ntohs(plen);
*offset = ntohl(*offset);
EXPECT_GT(plen, 11);
EXPECT_EQ(1u, fread(rtpHeader, 12, 1, _rtpFile));
ParseRTPHeader(rtp_header, rtpHeader);
EXPECT_EQ(lengthBytes, plen + 8);
if (plen == 0) {
return 0;
}
if (lengthBytes < 20) {
return 0;
}
if (payloadSize < static_cast<size_t>((lengthBytes - 20))) {
return 0;
}
lengthBytes -= 20;
EXPECT_EQ(lengthBytes, fread(payloadData, 1, lengthBytes, _rtpFile));
return lengthBytes;
}
} // namespace webrtc