This approach passes packetization mode to the encoder as part of
a cricket::VideoCodec structure, rather than as part of struct VideoCodecH264 inside webrtc::VideoCodec.
BUG=600254
Review-Url: https://codereview.webrtc.org/2528343002
Cr-Commit-Position: refs/heads/master@{#15437}
diff --git a/webrtc/media/base/codec.cc b/webrtc/media/base/codec.cc
index 835cf77..75e5a7b 100644
--- a/webrtc/media/base/codec.cc
+++ b/webrtc/media/base/codec.cc
@@ -210,10 +210,13 @@
}
VideoCodec::VideoCodec(int id, const std::string& name)
- : Codec(id, name, kVideoCodecClockrate) {}
+ : Codec(id, name, kVideoCodecClockrate) {
+ SetDefaultParameters();
+}
-VideoCodec::VideoCodec(const std::string& name)
- : VideoCodec(0 /* id */, name) {}
+VideoCodec::VideoCodec(const std::string& name) : VideoCodec(0 /* id */, name) {
+ SetDefaultParameters();
+}
VideoCodec::VideoCodec() : Codec() {
clockrate = kVideoCodecClockrate;
@@ -224,6 +227,16 @@
VideoCodec& VideoCodec::operator=(const VideoCodec& c) = default;
VideoCodec& VideoCodec::operator=(VideoCodec&& c) = default;
+void VideoCodec::SetDefaultParameters() {
+ if (_stricmp(kH264CodecName, name.c_str()) == 0) {
+ // This default is set for all H.264 codecs created because
+ // that was the default before packetization mode support was added.
+ // TODO(hta): Move this to the places that create VideoCodecs from
+ // SDP or from knowledge of implementation capabilities.
+ SetParam(kH264FmtpPacketizationMode, "1");
+ }
+}
+
bool VideoCodec::operator==(const VideoCodec& c) const {
return Codec::operator==(c);
}
diff --git a/webrtc/media/base/codec.h b/webrtc/media/base/codec.h
index 85b4327..2280082 100644
--- a/webrtc/media/base/codec.h
+++ b/webrtc/media/base/codec.h
@@ -184,6 +184,9 @@
// don't make sense (such as max < min bitrate), and error is logged and
// ValidateCodecFormat returns false.
bool ValidateCodecFormat() const;
+
+ private:
+ void SetDefaultParameters();
};
struct DataCodec : public Codec {
diff --git a/webrtc/media/engine/internalencoderfactory.cc b/webrtc/media/engine/internalencoderfactory.cc
index e714e5a..ec98375 100644
--- a/webrtc/media/engine/internalencoderfactory.cc
+++ b/webrtc/media/engine/internalencoderfactory.cc
@@ -37,11 +37,9 @@
cricket::VideoCodec codec(kH264CodecName);
// TODO(magjed): Move setting these parameters into webrtc::H264Encoder
// instead.
- // TODO(hta): Set FMTP parameters for all codecs of type H264.
codec.SetParam(kH264FmtpProfileLevelId,
kH264ProfileLevelConstrainedBaseline);
codec.SetParam(kH264FmtpLevelAsymmetryAllowed, "1");
- codec.SetParam(kH264FmtpPacketizationMode, "1");
supported_codecs_.push_back(std::move(codec));
}
diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn
index e853ea4..a5d11bb 100644
--- a/webrtc/modules/BUILD.gn
+++ b/webrtc/modules/BUILD.gn
@@ -544,6 +544,10 @@
[ "video_coding/codecs/vp9/vp9_screenshare_layers_unittest.cc" ]
}
+ if (rtc_use_h264) {
+ sources += [ "video_coding/codecs/h264/h264_encoder_impl_unittest.cc" ]
+ }
+
if (rtc_desktop_capture_supported || is_android) {
deps += [ "desktop_capture" ]
sources += [
diff --git a/webrtc/modules/include/module_common_types.h b/webrtc/modules/include/module_common_types.h
index 5de5eb7..3df93b6 100644
--- a/webrtc/modules/include/module_common_types.h
+++ b/webrtc/modules/include/module_common_types.h
@@ -262,6 +262,15 @@
// that was too large to fit into a single packet.
};
+// Packetization modes are defined in RFC 6184 section 6
+// Due to the structure containing this being initialized with zeroes
+// in some places, and mode 1 being default, mode 1 needs to have the value
+// zero. https://crbug.com/webrtc/6803
+enum class H264PacketizationMode {
+ NonInterleaved = 0, // Mode 1 - STAP-A, FU-A is allowed
+ SingleNalUnit // Mode 0 - only single NALU allowed
+};
+
struct NaluInfo {
uint8_t type;
int sps_id;
@@ -275,14 +284,19 @@
const size_t kMaxNalusPerPacket = 10;
struct RTPVideoHeaderH264 {
- uint8_t nalu_type; // The NAL unit type. If this is a header for a
- // fragmented packet, it's the NAL unit type of
- // the original data. If this is the header for an
- // aggregated packet, it's the NAL unit type of
- // the first NAL unit in the packet.
+ // The NAL unit type. If this is a header for a
+ // fragmented packet, it's the NAL unit type of
+ // the original data. If this is the header for an
+ // aggregated packet, it's the NAL unit type of
+ // the first NAL unit in the packet.
+ uint8_t nalu_type;
+ // The packetization type of this buffer - single, aggregated or fragmented.
H264PacketizationTypes packetization_type;
NaluInfo nalus[kMaxNalusPerPacket];
size_t nalus_length;
+ // The packetization mode of this transport. Packetization mode
+ // determines which packetization types are allowed when packetizing.
+ H264PacketizationMode packetization_mode;
};
union RTPVideoTypeHeader {
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format.cc b/webrtc/modules/rtp_rtcp/source/rtp_format.cc
index cdb9c49..753fc2e 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_format.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format.cc
@@ -10,6 +10,8 @@
#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
+#include <utility>
+
#include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h"
@@ -22,17 +24,19 @@
FrameType frame_type) {
switch (type) {
case kRtpVideoH264:
- return new RtpPacketizerH264(frame_type, max_payload_len);
+ RTC_CHECK(rtp_type_header);
+ return new RtpPacketizerH264(max_payload_len,
+ rtp_type_header->H264.packetization_mode);
case kRtpVideoVp8:
- assert(rtp_type_header != NULL);
+ RTC_CHECK(rtp_type_header);
return new RtpPacketizerVp8(rtp_type_header->VP8, max_payload_len);
case kRtpVideoVp9:
- assert(rtp_type_header != NULL);
+ RTC_CHECK(rtp_type_header);
return new RtpPacketizerVp9(rtp_type_header->VP9, max_payload_len);
case kRtpVideoGeneric:
return new RtpPacketizerGeneric(frame_type, max_payload_len);
case kRtpVideoNone:
- assert(false);
+ RTC_NOTREACHED();
}
return NULL;
}
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
index b82b66f..9d71803 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
@@ -78,9 +78,10 @@
} // namespace
-RtpPacketizerH264::RtpPacketizerH264(FrameType frame_type,
- size_t max_payload_len)
- : max_payload_len_(max_payload_len) {}
+RtpPacketizerH264::RtpPacketizerH264(size_t max_payload_len,
+ H264PacketizationMode packetization_mode)
+ : max_payload_len_(max_payload_len),
+ packetization_mode_(packetization_mode) {}
RtpPacketizerH264::~RtpPacketizerH264() {
}
@@ -163,11 +164,19 @@
void RtpPacketizerH264::GeneratePackets() {
for (size_t i = 0; i < input_fragments_.size();) {
- if (input_fragments_[i].length > max_payload_len_) {
- PacketizeFuA(i);
- ++i;
- } else {
- i = PacketizeStapA(i);
+ switch (packetization_mode_) {
+ case H264PacketizationMode::SingleNalUnit:
+ PacketizeSingleNalu(i);
+ ++i;
+ break;
+ case H264PacketizationMode::NonInterleaved:
+ if (input_fragments_[i].length > max_payload_len_) {
+ PacketizeFuA(i);
+ ++i;
+ } else {
+ i = PacketizeStapA(i);
+ }
+ break;
}
}
}
@@ -230,6 +239,21 @@
return fragment_index;
}
+void RtpPacketizerH264::PacketizeSingleNalu(size_t fragment_index) {
+ // Add a single NALU to the queue, no aggregation.
+ size_t payload_size_left = max_payload_len_;
+ const Fragment* fragment = &input_fragments_[fragment_index];
+ RTC_CHECK_GE(payload_size_left, fragment->length)
+ << "Payload size left " << payload_size_left << ", fragment length "
+ << fragment->length << ", packetization mode "
+ << (packetization_mode_ == H264PacketizationMode::SingleNalUnit
+ ? "SingleNalUnit"
+ : "NonInterleaved");
+ RTC_CHECK_GT(fragment->length, 0u);
+ packets_.push(PacketUnit(*fragment, true /* first */, true /* last */,
+ false /* aggregated */, fragment->buffer[0]));
+}
+
bool RtpPacketizerH264::NextPacket(RtpPacketToSend* rtp_packet,
bool* last_packet) {
RTC_DCHECK(rtp_packet);
@@ -248,8 +272,10 @@
packets_.pop();
input_fragments_.pop_front();
} else if (packet.aggregated) {
+ RTC_CHECK(packetization_mode_ == H264PacketizationMode::NonInterleaved);
NextAggregatePacket(rtp_packet);
} else {
+ RTC_CHECK(packetization_mode_ == H264PacketizationMode::NonInterleaved);
NextFragmentPacket(rtp_packet);
}
RTC_DCHECK_LE(rtp_packet->payload_size(), max_payload_len_);
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h
index bc32401..fe7b378 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h
@@ -26,7 +26,8 @@
public:
// Initialize with payload from encoder.
// The payload_data must be exactly one encoded H264 frame.
- RtpPacketizerH264(FrameType frame_type, size_t max_payload_len);
+ RtpPacketizerH264(size_t max_payload_len,
+ H264PacketizationMode packetization_mode);
virtual ~RtpPacketizerH264();
@@ -86,10 +87,12 @@
void GeneratePackets();
void PacketizeFuA(size_t fragment_index);
size_t PacketizeStapA(size_t fragment_index);
+ void PacketizeSingleNalu(size_t fragment_index);
void NextAggregatePacket(RtpPacketToSend* rtp_packet);
void NextFragmentPacket(RtpPacketToSend* rtp_packet);
const size_t max_payload_len_;
+ const H264PacketizationMode packetization_mode_;
std::deque<Fragment> input_fragments_;
std::queue<PacketUnit> packets_;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
index 03082a4..ccaef1a 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
@@ -49,6 +49,27 @@
// Bit masks for FU (A and B) headers.
enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 };
+void CreateThreeFragments(RTPFragmentationHeader* fragmentation,
+ size_t frameSize,
+ size_t payloadOffset) {
+ fragmentation->VerifyAndAllocateFragmentationHeader(3);
+ fragmentation->fragmentationOffset[0] = 0;
+ fragmentation->fragmentationLength[0] = 2;
+ fragmentation->fragmentationOffset[1] = 2;
+ fragmentation->fragmentationLength[1] = 2;
+ fragmentation->fragmentationOffset[2] = 4;
+ fragmentation->fragmentationLength[2] =
+ kNalHeaderSize + frameSize - payloadOffset;
+}
+
+RtpPacketizer* CreateH264Packetizer(H264PacketizationMode mode,
+ size_t max_payload_size) {
+ RTPVideoTypeHeader type_header;
+ type_header.H264.packetization_mode = mode;
+ return RtpPacketizer::Create(kRtpVideoH264, max_payload_size, &type_header,
+ kEmptyFrame);
+}
+
void VerifyFua(size_t fua_index,
const uint8_t* expected_payload,
int offset,
@@ -88,8 +109,8 @@
fragmentation.VerifyAndAllocateFragmentationHeader(1);
fragmentation.fragmentationOffset[0] = 0;
fragmentation.fragmentationLength[0] = frame_size;
- std::unique_ptr<RtpPacketizer> packetizer(RtpPacketizer::Create(
- kRtpVideoH264, max_payload_size, NULL, kEmptyFrame));
+ std::unique_ptr<RtpPacketizer> packetizer(CreateH264Packetizer(
+ H264PacketizationMode::NonInterleaved, max_payload_size));
packetizer->SetPayloadData(frame.get(), frame_size, &fragmentation);
RtpPacketToSend packet(kNoExtensions);
@@ -149,14 +170,19 @@
}
} // namespace
-TEST(RtpPacketizerH264Test, TestSingleNalu) {
+// Tests that should work with both packetization mode 0 and
+// packetization mode 1.
+class RtpPacketizerH264ModeTest
+ : public ::testing::TestWithParam<H264PacketizationMode> {};
+
+TEST_P(RtpPacketizerH264ModeTest, TestSingleNalu) {
const uint8_t frame[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5.
RTPFragmentationHeader fragmentation;
fragmentation.VerifyAndAllocateFragmentationHeader(1);
fragmentation.fragmentationOffset[0] = 0;
fragmentation.fragmentationLength[0] = sizeof(frame);
std::unique_ptr<RtpPacketizer> packetizer(
- RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kEmptyFrame));
+ CreateH264Packetizer(GetParam(), kMaxPayloadSize));
packetizer->SetPayloadData(frame, sizeof(frame), &fragmentation);
RtpPacketToSend packet(kNoExtensions);
ASSERT_LE(kMaxPayloadSize, packet.FreeCapacity());
@@ -168,7 +194,7 @@
EXPECT_FALSE(packetizer->NextPacket(&packet, &last));
}
-TEST(RtpPacketizerH264Test, TestSingleNaluTwoPackets) {
+TEST_P(RtpPacketizerH264ModeTest, TestSingleNaluTwoPackets) {
const size_t kFrameSize = kMaxPayloadSize + 100;
uint8_t frame[kFrameSize] = {0};
for (size_t i = 0; i < kFrameSize; ++i)
@@ -184,7 +210,7 @@
frame[fragmentation.fragmentationOffset[1]] = 0x01;
std::unique_ptr<RtpPacketizer> packetizer(
- RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kEmptyFrame));
+ CreateH264Packetizer(GetParam(), kMaxPayloadSize));
packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
RtpPacketToSend packet(kNoExtensions);
@@ -201,6 +227,12 @@
EXPECT_FALSE(packetizer->NextPacket(&packet, &last));
}
+INSTANTIATE_TEST_CASE_P(
+ PacketMode,
+ RtpPacketizerH264ModeTest,
+ ::testing::Values(H264PacketizationMode::SingleNalUnit,
+ H264PacketizationMode::NonInterleaved));
+
TEST(RtpPacketizerH264Test, TestStapA) {
const size_t kFrameSize =
kMaxPayloadSize - 3 * kLengthFieldLength - kNalHeaderSize;
@@ -211,16 +243,9 @@
for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i)
frame[i + kPayloadOffset] = i;
RTPFragmentationHeader fragmentation;
- fragmentation.VerifyAndAllocateFragmentationHeader(3);
- fragmentation.fragmentationOffset[0] = 0;
- fragmentation.fragmentationLength[0] = 2;
- fragmentation.fragmentationOffset[1] = 2;
- fragmentation.fragmentationLength[1] = 2;
- fragmentation.fragmentationOffset[2] = 4;
- fragmentation.fragmentationLength[2] =
- kNalHeaderSize + kFrameSize - kPayloadOffset;
- std::unique_ptr<RtpPacketizer> packetizer(
- RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kEmptyFrame));
+ CreateThreeFragments(&fragmentation, kFrameSize, kPayloadOffset);
+ std::unique_ptr<RtpPacketizer> packetizer(CreateH264Packetizer(
+ H264PacketizationMode::NonInterleaved, kMaxPayloadSize));
packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
RtpPacketToSend packet(kNoExtensions);
@@ -237,6 +262,31 @@
EXPECT_FALSE(packetizer->NextPacket(&packet, &last));
}
+TEST(RtpPacketizerH264Test, TestSingleNalUnitModeHasNoStapA) {
+ // This is the same setup as for the TestStapA test.
+ const size_t kFrameSize =
+ kMaxPayloadSize - 3 * kLengthFieldLength - kNalHeaderSize;
+ uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7 (SPS).
+ 0x08, 0xFF, // F=0, NRI=0, Type=8 (PPS).
+ 0x05}; // F=0, NRI=0, Type=5 (IDR).
+ const size_t kPayloadOffset = 5;
+ for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i)
+ frame[i + kPayloadOffset] = i;
+ RTPFragmentationHeader fragmentation;
+ CreateThreeFragments(&fragmentation, kFrameSize, kPayloadOffset);
+ std::unique_ptr<RtpPacketizer> packetizer(CreateH264Packetizer(
+ H264PacketizationMode::SingleNalUnit, kMaxPayloadSize));
+ packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
+
+ RtpPacketToSend packet(kNoExtensions);
+ bool last = false;
+ // The three fragments should be returned as three packets.
+ ASSERT_TRUE(packetizer->NextPacket(&packet, &last));
+ ASSERT_TRUE(packetizer->NextPacket(&packet, &last));
+ ASSERT_TRUE(packetizer->NextPacket(&packet, &last));
+ EXPECT_FALSE(packetizer->NextPacket(&packet, &last));
+}
+
TEST(RtpPacketizerH264Test, TestTooSmallForStapAHeaders) {
const size_t kFrameSize = kMaxPayloadSize - 1;
uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7.
@@ -254,8 +304,8 @@
fragmentation.fragmentationOffset[2] = 4;
fragmentation.fragmentationLength[2] =
kNalHeaderSize + kFrameSize - kPayloadOffset;
- std::unique_ptr<RtpPacketizer> packetizer(
- RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kEmptyFrame));
+ std::unique_ptr<RtpPacketizer> packetizer(CreateH264Packetizer(
+ H264PacketizationMode::NonInterleaved, kMaxPayloadSize));
packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
RtpPacketToSend packet(kNoExtensions);
@@ -302,8 +352,8 @@
frame[nalu_offset + j] = i + j;
}
}
- std::unique_ptr<RtpPacketizer> packetizer(
- RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize, NULL, kEmptyFrame));
+ std::unique_ptr<RtpPacketizer> packetizer(CreateH264Packetizer(
+ H264PacketizationMode::NonInterleaved, kMaxPayloadSize));
packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
// First expecting two FU-A packets.
@@ -376,6 +426,28 @@
sizeof(kExpectedPayloadSizes) / sizeof(size_t)));
}
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
+TEST(RtpPacketizerH264DeathTest, SendOverlongDataInPacketizationMode0) {
+ const size_t kFrameSize = kMaxPayloadSize + 1;
+ uint8_t frame[kFrameSize] = {0};
+ for (size_t i = 0; i < kFrameSize; ++i)
+ frame[i] = i;
+ RTPFragmentationHeader fragmentation;
+ fragmentation.VerifyAndAllocateFragmentationHeader(1);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = kFrameSize;
+ // Set NAL headers.
+ frame[fragmentation.fragmentationOffset[0]] = 0x01;
+
+ std::unique_ptr<RtpPacketizer> packetizer(CreateH264Packetizer(
+ H264PacketizationMode::SingleNalUnit, kMaxPayloadSize));
+ EXPECT_DEATH(packetizer->SetPayloadData(frame, kFrameSize, &fragmentation),
+ "payload_size");
+}
+
+#endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
namespace {
const uint8_t kStartSequence[] = {0x00, 0x00, 0x00, 0x01};
const uint8_t kOriginalSps[] = {kSps, 0x00, 0x00, 0x03, 0x03,
@@ -416,9 +488,9 @@
const size_t kHeaderOverhead = kFuAHeaderSize + 1;
// Set size to fragment SPS into two FU-A packets.
- packetizer_.reset(RtpPacketizer::Create(
- kRtpVideoH264, sizeof(kOriginalSps) - 2 + kHeaderOverhead, nullptr,
- kEmptyFrame));
+ packetizer_.reset(
+ CreateH264Packetizer(H264PacketizationMode::NonInterleaved,
+ sizeof(kOriginalSps) - 2 + kHeaderOverhead));
packetizer_->SetPayloadData(in_buffer_.data(), in_buffer_.size(),
&fragmentation_header_);
@@ -450,9 +522,8 @@
sizeof(kIdrTwo) + (kLengthFieldLength * 3);
// Set size to include SPS and the rest of the packets in a Stap-A package.
- packetizer_.reset(RtpPacketizer::Create(kRtpVideoH264,
- kExpectedTotalSize + kHeaderOverhead,
- nullptr, kEmptyFrame));
+ packetizer_.reset(CreateH264Packetizer(H264PacketizationMode::NonInterleaved,
+ kExpectedTotalSize + kHeaderOverhead));
packetizer_->SetPayloadData(in_buffer_.data(), in_buffer_.size(),
&fragmentation_header_);
diff --git a/webrtc/modules/video_coding/BUILD.gn b/webrtc/modules/video_coding/BUILD.gn
index c8f6404..cd43b83 100644
--- a/webrtc/modules/video_coding/BUILD.gn
+++ b/webrtc/modules/video_coding/BUILD.gn
@@ -158,6 +158,7 @@
]
deps += [
"../../common_video",
+ "../../media:rtc_media_base",
"//third_party/ffmpeg:ffmpeg",
"//third_party/openh264:encoder",
]
diff --git a/webrtc/modules/video_coding/codecs/h264/h264.cc b/webrtc/modules/video_coding/codecs/h264/h264.cc
index f03a83d..1e3a580 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264.cc
+++ b/webrtc/modules/video_coding/codecs/h264/h264.cc
@@ -49,7 +49,7 @@
#if defined(WEBRTC_USE_H264)
RTC_CHECK(g_rtc_use_h264);
LOG(LS_INFO) << "Creating H264EncoderImpl.";
- return new H264EncoderImpl();
+ return new H264EncoderImpl(codec);
#else
RTC_NOTREACHED();
return nullptr;
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc
index d7df122..6150aa8 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc
+++ b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc
@@ -12,6 +12,7 @@
#include "webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h"
#include <limits>
+#include <string>
#include "third_party/openh264/src/codec/api/svc/codec_api.h"
#include "third_party/openh264/src/codec/api/svc/codec_app_def.h"
@@ -21,6 +22,7 @@
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/media/base/mediaconstants.h"
#include "webrtc/system_wrappers/include/metrics.h"
namespace webrtc {
@@ -150,7 +152,7 @@
}
}
-H264EncoderImpl::H264EncoderImpl()
+H264EncoderImpl::H264EncoderImpl(const cricket::VideoCodec& codec)
: openh264_encoder_(nullptr),
width_(0),
height_(0),
@@ -160,10 +162,20 @@
mode_(kRealtimeVideo),
frame_dropping_on_(false),
key_frame_interval_(0),
+ packetization_mode_(H264PacketizationMode::SingleNalUnit),
+ max_payload_size_(0),
number_of_cores_(0),
encoded_image_callback_(nullptr),
has_reported_init_(false),
- has_reported_error_(false) {}
+ has_reported_error_(false) {
+ RTC_CHECK(cricket::CodecNamesEq(codec.name, cricket::kH264CodecName));
+ std::string packetization_mode_string;
+ if (codec.GetParam(cricket::kH264FmtpPacketizationMode,
+ &packetization_mode_string) &&
+ packetization_mode_string == "1") {
+ packetization_mode_ = H264PacketizationMode::NonInterleaved;
+ }
+}
H264EncoderImpl::~H264EncoderImpl() {
Release();
@@ -171,7 +183,7 @@
int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings,
int32_t number_of_cores,
- size_t /*max_payload_size*/) {
+ size_t max_payload_size) {
ReportInit();
if (!codec_settings ||
codec_settings->codecType != kVideoCodecH264) {
@@ -218,6 +230,7 @@
mode_ = codec_settings->mode;
frame_dropping_on_ = codec_settings->H264().frameDroppingOn;
key_frame_interval_ = codec_settings->H264().keyFrameInterval;
+ max_payload_size_ = max_payload_size;
// Codec_settings uses kbits/second; encoder uses bits/second.
max_bps_ = codec_settings->maxBitrate * 1000;
@@ -227,6 +240,7 @@
target_bps_ = codec_settings->targetBitrate * 1000;
SEncParamExt encoder_params = CreateEncoderParams();
+
// Initialize.
if (openh264_encoder_->InitializeExt(&encoder_params) != 0) {
LOG(LS_ERROR) << "Failed to initialize OpenH264 encoder";
@@ -370,6 +384,7 @@
// Deliver encoded image.
CodecSpecificInfo codec_specific;
codec_specific.codecType = kVideoCodecH264;
+ codec_specific.codecSpecific.H264.packetization_mode = packetization_mode_;
encoded_image_callback_->OnEncodedImage(encoded_image_, &codec_specific,
&frag_header);
@@ -434,19 +449,46 @@
encoder_params.iTargetBitrate;
encoder_params.sSpatialLayers[0].iMaxSpatialBitrate =
encoder_params.iMaxBitrate;
+ LOG(INFO) << "OpenH264 version is " << OPENH264_MAJOR << "."
+ << OPENH264_MINOR;
+ switch (packetization_mode_) {
+ case H264PacketizationMode::SingleNalUnit:
+// Limit the size of the packets produced.
#if (OPENH264_MAJOR == 1) && (OPENH264_MINOR <= 5)
- // Slice num according to number of threads.
- encoder_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE;
+ encoder_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_DYN_SLICE;
+ // The slice size is max payload size - room for a NAL header.
+ // The constant 50 is NAL_HEADER_ADD_0X30BYTES in openh264 source,
+ // but is not exported.
+ const kNalHeaderSizeAllocation = 50;
+ encoder_params.sSpatialLayers[0]
+ .sSliceCfg.sSliceArgument.uiSliceSizeConstraint =
+ static_cast<unsigned int>(max_payload_size_ -
+ kNalHeaderSizeAllocation);
+ encoder_params.uiMaxNalSize =
+ static_cast<unsigned int>(max_payload_size_);
#else
- // When uiSliceMode = SM_FIXEDSLCNUM_SLICE, uiSliceNum = 0 means auto design
- // it with cpu core number.
- // TODO(sprang): Set to 0 when we understand why the rate controller borks
- // when uiSliceNum > 1.
- encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceNum = 1;
- encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceMode =
- SM_FIXEDSLCNUM_SLICE;
+ encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceNum = 1;
+ encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceMode =
+ SM_SIZELIMITED_SLICE;
+ encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint =
+ static_cast<unsigned int>(max_payload_size_);
#endif
-
+ break;
+ case H264PacketizationMode::NonInterleaved:
+#if (OPENH264_MAJOR == 1) && (OPENH264_MINOR <= 5)
+ // Slice num according to number of threads.
+ encoder_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE;
+#else
+ // When uiSliceMode = SM_FIXEDSLCNUM_SLICE, uiSliceNum = 0 means auto
+ // design it with cpu core number.
+ // TODO(sprang): Set to 0 when we understand why the rate controller borks
+ // when uiSliceNum > 1.
+ encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceNum = 1;
+ encoder_params.sSpatialLayers[0].sSliceArgument.uiSliceMode =
+ SM_FIXEDSLCNUM_SLICE;
+#endif
+ break;
+ }
return encoder_params;
}
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h
index aab16ac..a455259 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h
+++ b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h
@@ -27,7 +27,7 @@
class H264EncoderImpl : public H264Encoder {
public:
- H264EncoderImpl();
+ explicit H264EncoderImpl(const cricket::VideoCodec& codec);
~H264EncoderImpl() override;
// |max_payload_size| is ignored.
@@ -39,7 +39,7 @@
// - height
int32_t InitEncode(const VideoCodec* codec_settings,
int32_t number_of_cores,
- size_t /*max_payload_size*/) override;
+ size_t max_payload_size) override;
int32_t Release() override;
int32_t RegisterEncodeCompleteCallback(
@@ -61,6 +61,11 @@
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
int32_t SetPeriodicKeyFrames(bool enable) override;
+ // Exposed for testing.
+ H264PacketizationMode PacketizationModeForTesting() const {
+ return packetization_mode_;
+ }
+
private:
bool IsInitialized() const;
SEncParamExt CreateEncoderParams() const;
@@ -81,7 +86,9 @@
// H.264 specifc parameters
bool frame_dropping_on_;
int key_frame_interval_;
+ H264PacketizationMode packetization_mode_;
+ size_t max_payload_size_;
int32_t number_of_cores_;
EncodedImage encoded_image_;
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc
new file mode 100644
index 0000000..2d236cf
--- /dev/null
+++ b/webrtc/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015 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 "webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h"
+
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+const int kMaxPayloadSize = 1024;
+const int kNumCores = 1;
+
+void SetDefaultSettings(VideoCodec* codec_settings) {
+ codec_settings->codecType = kVideoCodecH264;
+ codec_settings->maxFramerate = 60;
+ codec_settings->width = 640;
+ codec_settings->height = 480;
+ // If frame dropping is false, we get a warning that bitrate can't
+ // be controlled for RC_QUALITY_MODE; RC_BITRATE_MODE and RC_TIMESTAMP_MODE
+ codec_settings->H264()->frameDroppingOn = true;
+ codec_settings->targetBitrate = 2000;
+ codec_settings->maxBitrate = 4000;
+}
+
+TEST(H264EncoderImplTest, CanInitializeWithDefaultParameters) {
+ H264EncoderImpl encoder(cricket::VideoCodec("H264"));
+ VideoCodec codec_settings;
+ SetDefaultSettings(&codec_settings);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ encoder.InitEncode(&codec_settings, kNumCores, kMaxPayloadSize));
+ EXPECT_EQ(H264PacketizationMode::NonInterleaved,
+ encoder.PacketizationModeForTesting());
+}
+
+TEST(H264EncoderImplTest, CanInitializeWithNonInterleavedModeExplicitly) {
+ cricket::VideoCodec codec("H264");
+ codec.SetParam(cricket::kH264FmtpPacketizationMode, "1");
+ H264EncoderImpl encoder(codec);
+ VideoCodec codec_settings;
+ SetDefaultSettings(&codec_settings);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ encoder.InitEncode(&codec_settings, kNumCores, kMaxPayloadSize));
+ EXPECT_EQ(H264PacketizationMode::NonInterleaved,
+ encoder.PacketizationModeForTesting());
+}
+
+TEST(H264EncoderImplTest, CanInitializeWithSingleNalUnitModeExplicitly) {
+ cricket::VideoCodec codec("H264");
+ codec.SetParam(cricket::kH264FmtpPacketizationMode, "0");
+ H264EncoderImpl encoder(codec);
+ VideoCodec codec_settings;
+ SetDefaultSettings(&codec_settings);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ encoder.InitEncode(&codec_settings, kNumCores, kMaxPayloadSize));
+ EXPECT_EQ(H264PacketizationMode::SingleNalUnit,
+ encoder.PacketizationModeForTesting());
+}
+
+TEST(H264EncoderImplTest, CanInitializeWithRemovedParameter) {
+ cricket::VideoCodec codec("H264");
+ codec.RemoveParam(cricket::kH264FmtpPacketizationMode);
+ H264EncoderImpl encoder(codec);
+ VideoCodec codec_settings;
+ SetDefaultSettings(&codec_settings);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ encoder.InitEncode(&codec_settings, kNumCores, kMaxPayloadSize));
+ EXPECT_EQ(H264PacketizationMode::SingleNalUnit,
+ encoder.PacketizationModeForTesting());
+}
+
+} // anonymous namespace
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_coding/include/video_codec_interface.h b/webrtc/modules/video_coding/include/video_codec_interface.h
index f0a11e7..5dcfaff 100644
--- a/webrtc/modules/video_coding/include/video_codec_interface.h
+++ b/webrtc/modules/video_coding/include/video_codec_interface.h
@@ -77,7 +77,9 @@
uint8_t simulcast_idx;
};
-struct CodecSpecificInfoH264 {};
+struct CodecSpecificInfoH264 {
+ H264PacketizationMode packetization_mode;
+};
union CodecSpecificInfoUnion {
CodecSpecificInfoGeneric generic;
diff --git a/webrtc/test/fake_encoder.cc b/webrtc/test/fake_encoder.cc
index 26ce0c6..ae1788b 100644
--- a/webrtc/test/fake_encoder.cc
+++ b/webrtc/test/fake_encoder.cc
@@ -200,6 +200,8 @@
CodecSpecificInfo specifics;
memset(&specifics, 0, sizeof(specifics));
specifics.codecType = kVideoCodecH264;
+ specifics.codecSpecific.H264.packetization_mode =
+ H264PacketizationMode::NonInterleaved;
return callback_->OnEncodedImage(encoded_image, &specifics, &fragmentation);
}
diff --git a/webrtc/video/BUILD.gn b/webrtc/video/BUILD.gn
index 66191ef..9709e45 100644
--- a/webrtc/video/BUILD.gn
+++ b/webrtc/video/BUILD.gn
@@ -144,6 +144,7 @@
# TODO(pbos): Rename test suite.
rtc_source_set("video_tests") {
testonly = true
+ defines = []
sources = [
"call_stats_unittest.cc",
"encoder_rtcp_feedback_unittest.cc",
@@ -171,7 +172,7 @@
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
if (rtc_use_h264) {
- defines = [ "WEBRTC_USE_H264" ]
+ defines += [ "WEBRTC_USE_H264" ]
}
}
}
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index 4de6db4..4931daf 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -404,6 +404,23 @@
H264Decoder::Create());
RunBaseTest(&test);
}
+
+TEST_P(EndToEndTest, SendsAndReceivesH264PacketizationMode0) {
+ cricket::VideoCodec codec = cricket::VideoCodec("H264");
+ codec.SetParam(cricket::kH264FmtpPacketizationMode, "0");
+ CodecObserver test(500, kVideoRotation_0, "H264", H264Encoder::Create(codec),
+ H264Decoder::Create());
+ RunBaseTest(&test);
+}
+
+TEST_P(EndToEndTest, SendsAndReceivesH264PacketizationMode1) {
+ cricket::VideoCodec codec = cricket::VideoCodec("H264");
+ codec.SetParam(cricket::kH264FmtpPacketizationMode, "1");
+ CodecObserver test(500, kVideoRotation_0, "H264", H264Encoder::Create(codec),
+ H264Decoder::Create());
+ RunBaseTest(&test);
+}
+
#endif // defined(WEBRTC_USE_H264)
TEST_P(EndToEndTest, ReceiverUsesLocalSsrc) {
diff --git a/webrtc/video/payload_router.cc b/webrtc/video/payload_router.cc
index 6c8e36f..8b29e81 100644
--- a/webrtc/video/payload_router.cc
+++ b/webrtc/video/payload_router.cc
@@ -75,6 +75,8 @@
}
case kVideoCodecH264:
rtp->codec = kRtpVideoH264;
+ rtp->codecHeader.H264.packetization_mode =
+ info->codecSpecific.H264.packetization_mode;
return;
case kVideoCodecGeneric:
rtp->codec = kRtpVideoGeneric;