Add end-to-end H.264 packetization test.
Also correctly wires up H.264 packetization in the new Call api. R=pbos@webrtc.org Review URL: https://webrtc-codereview.appspot.com/20079004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6835 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
e415864a32
commit
79c3359e67
@ -57,5 +57,31 @@ int32_t FakeDecoder::Reset() {
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int32_t FakeH264Decoder::Decode(const EncodedImage& input,
|
||||
bool missing_frames,
|
||||
const RTPFragmentationHeader* fragmentation,
|
||||
const CodecSpecificInfo* codec_specific_info,
|
||||
int64_t render_time_ms) {
|
||||
uint8_t value = 0;
|
||||
for (size_t i = 0; i < input._length; ++i) {
|
||||
uint8_t kStartCode[] = {0, 0, 0, 1};
|
||||
if (i < input._length - sizeof(kStartCode) &&
|
||||
!memcmp(&input._buffer[i], kStartCode, sizeof(kStartCode))) {
|
||||
i += sizeof(kStartCode) + 1; // Skip start code and NAL header.
|
||||
}
|
||||
if (input._buffer[i] != value) {
|
||||
EXPECT_EQ(value, input._buffer[i])
|
||||
<< "Bitstream mismatch between sender and receiver.";
|
||||
return -1;
|
||||
}
|
||||
++value;
|
||||
}
|
||||
return FakeDecoder::Decode(input,
|
||||
missing_frames,
|
||||
fragmentation,
|
||||
codec_specific_info,
|
||||
render_time_ms);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
@ -22,6 +22,7 @@ namespace test {
|
||||
class FakeDecoder : public VideoDecoder {
|
||||
public:
|
||||
FakeDecoder();
|
||||
virtual ~FakeDecoder() {}
|
||||
|
||||
virtual int32_t InitDecode(const VideoCodec* config,
|
||||
int32_t number_of_cores) OVERRIDE;
|
||||
@ -43,6 +44,17 @@ class FakeDecoder : public VideoDecoder {
|
||||
I420VideoFrame frame_;
|
||||
DecodedImageCallback* callback_;
|
||||
};
|
||||
|
||||
class FakeH264Decoder : public FakeDecoder {
|
||||
public:
|
||||
virtual ~FakeH264Decoder() {}
|
||||
|
||||
virtual int32_t Decode(const EncodedImage& input,
|
||||
bool missing_frames,
|
||||
const RTPFragmentationHeader* fragmentation,
|
||||
const CodecSpecificInfo* codec_specific_info,
|
||||
int64_t render_time_ms) OVERRIDE;
|
||||
};
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -117,5 +117,60 @@ int32_t FakeEncoder::SetRates(uint32_t new_target_bitrate, uint32_t framerate) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FakeH264Encoder::FakeH264Encoder(Clock* clock)
|
||||
: FakeEncoder(clock), callback_(NULL), idr_counter_(0) {
|
||||
FakeEncoder::RegisterEncodeCompleteCallback(this);
|
||||
}
|
||||
|
||||
int32_t FakeH264Encoder::RegisterEncodeCompleteCallback(
|
||||
EncodedImageCallback* callback) {
|
||||
callback_ = callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t FakeH264Encoder::Encoded(EncodedImage& encoded_image,
|
||||
const CodecSpecificInfo* codec_specific_info,
|
||||
const RTPFragmentationHeader* fragments) {
|
||||
const size_t kSpsSize = 8;
|
||||
const size_t kPpsSize = 11;
|
||||
const int kIdrFrequency = 10;
|
||||
RTPFragmentationHeader fragmentation;
|
||||
if (idr_counter_++ % kIdrFrequency == 0 &&
|
||||
encoded_image._length > kSpsSize + kPpsSize + 1) {
|
||||
const size_t kNumSlices = 3;
|
||||
fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices);
|
||||
fragmentation.fragmentationOffset[0] = 0;
|
||||
fragmentation.fragmentationLength[0] = kSpsSize;
|
||||
fragmentation.fragmentationOffset[1] = kSpsSize;
|
||||
fragmentation.fragmentationLength[1] = kPpsSize;
|
||||
fragmentation.fragmentationOffset[2] = kSpsSize + kPpsSize;
|
||||
fragmentation.fragmentationLength[2] =
|
||||
encoded_image._length - (kSpsSize + kPpsSize);
|
||||
const uint8_t kSpsNalHeader = 0x37;
|
||||
const uint8_t kPpsNalHeader = 0x38;
|
||||
const uint8_t kIdrNalHeader = 0x15;
|
||||
encoded_image._buffer[fragmentation.fragmentationOffset[0]] = kSpsNalHeader;
|
||||
encoded_image._buffer[fragmentation.fragmentationOffset[1]] = kPpsNalHeader;
|
||||
encoded_image._buffer[fragmentation.fragmentationOffset[2]] = kIdrNalHeader;
|
||||
} else {
|
||||
const size_t kNumSlices = 1;
|
||||
fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices);
|
||||
fragmentation.fragmentationOffset[0] = 0;
|
||||
fragmentation.fragmentationLength[0] = encoded_image._length;
|
||||
const uint8_t kNalHeader = 0x11;
|
||||
encoded_image._buffer[fragmentation.fragmentationOffset[0]] = kNalHeader;
|
||||
}
|
||||
uint8_t value = 0;
|
||||
int fragment_counter = 0;
|
||||
for (size_t i = 0; i < encoded_image._length; ++i) {
|
||||
if (fragment_counter == fragmentation.fragmentationVectorSize ||
|
||||
i != fragmentation.fragmentationOffset[fragment_counter]) {
|
||||
encoded_image._buffer[i] = value++;
|
||||
} else {
|
||||
++fragment_counter;
|
||||
}
|
||||
}
|
||||
return callback_->Encoded(encoded_image, NULL, &fragmentation);
|
||||
}
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
@ -50,6 +50,24 @@ class FakeEncoder : public VideoEncoder {
|
||||
int64_t last_encode_time_ms_;
|
||||
uint8_t encoded_buffer_[100000];
|
||||
};
|
||||
|
||||
class FakeH264Encoder : public FakeEncoder, public EncodedImageCallback {
|
||||
public:
|
||||
explicit FakeH264Encoder(Clock* clock);
|
||||
virtual ~FakeH264Encoder() {}
|
||||
|
||||
virtual int32_t RegisterEncodeCompleteCallback(
|
||||
EncodedImageCallback* callback) OVERRIDE;
|
||||
|
||||
virtual int32_t Encoded(
|
||||
EncodedImage& encodedImage,
|
||||
const CodecSpecificInfo* codecSpecificInfo = NULL,
|
||||
const RTPFragmentationHeader* fragments = NULL) OVERRIDE;
|
||||
|
||||
private:
|
||||
EncodedImageCallback* callback_;
|
||||
int idr_counter_;
|
||||
};
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -209,6 +209,57 @@ TEST_F(EndToEndTest, TransmitsFirstFrame) {
|
||||
DestroyStreams();
|
||||
}
|
||||
|
||||
TEST_F(EndToEndTest, SendsAndReceivesH264) {
|
||||
class H264Observer : public test::EndToEndTest, public VideoRenderer {
|
||||
public:
|
||||
H264Observer()
|
||||
: EndToEndTest(2 * kDefaultTimeoutMs),
|
||||
fake_encoder_(Clock::GetRealTimeClock()),
|
||||
frame_counter_(0) {}
|
||||
|
||||
virtual void PerformTest() OVERRIDE {
|
||||
EXPECT_EQ(kEventSignaled, Wait())
|
||||
<< "Timed out while waiting for enough frames to be decoded.";
|
||||
}
|
||||
|
||||
virtual void ModifyConfigs(
|
||||
VideoSendStream::Config* send_config,
|
||||
std::vector<VideoReceiveStream::Config>* receive_configs,
|
||||
std::vector<VideoStream>* video_streams) {
|
||||
send_config->encoder_settings.encoder = &fake_encoder_;
|
||||
send_config->encoder_settings.payload_name = "H264";
|
||||
send_config->encoder_settings.payload_type = kFakeSendPayloadType;
|
||||
(*video_streams)[0].min_bitrate_bps = 50000;
|
||||
(*video_streams)[0].target_bitrate_bps =
|
||||
(*video_streams)[0].max_bitrate_bps = 2000000;
|
||||
|
||||
(*receive_configs)[0].renderer = this;
|
||||
VideoCodec codec =
|
||||
test::CreateDecoderVideoCodec(send_config->encoder_settings);
|
||||
(*receive_configs)[0].codecs.resize(1);
|
||||
(*receive_configs)[0].codecs[0] = codec;
|
||||
(*receive_configs)[0].external_decoders.resize(1);
|
||||
(*receive_configs)[0].external_decoders[0].payload_type =
|
||||
send_config->encoder_settings.payload_type;
|
||||
(*receive_configs)[0].external_decoders[0].decoder = &fake_decoder_;
|
||||
}
|
||||
|
||||
virtual void RenderFrame(const I420VideoFrame& video_frame,
|
||||
int time_to_render_ms) OVERRIDE {
|
||||
const int kRequiredFrames = 500;
|
||||
if (++frame_counter_ == kRequiredFrames)
|
||||
observation_complete_->Set();
|
||||
}
|
||||
|
||||
private:
|
||||
test::FakeH264Decoder fake_decoder_;
|
||||
test::FakeH264Encoder fake_encoder_;
|
||||
int frame_counter_;
|
||||
} test;
|
||||
|
||||
RunBaseTest(&test);
|
||||
}
|
||||
|
||||
TEST_F(EndToEndTest, ReceiverUsesLocalSsrc) {
|
||||
class SyncRtcpObserver : public test::EndToEndTest {
|
||||
public:
|
||||
|
@ -304,9 +304,13 @@ bool VideoSendStream::ReconfigureVideoEncoder(
|
||||
|
||||
VideoCodec video_codec;
|
||||
memset(&video_codec, 0, sizeof(video_codec));
|
||||
video_codec.codecType =
|
||||
(config_.encoder_settings.payload_name == "VP8" ? kVideoCodecVP8
|
||||
: kVideoCodecGeneric);
|
||||
if (config_.encoder_settings.payload_name == "VP8") {
|
||||
video_codec.codecType = kVideoCodecVP8;
|
||||
} else if (config_.encoder_settings.payload_name == "H264") {
|
||||
video_codec.codecType = kVideoCodecH264;
|
||||
} else {
|
||||
video_codec.codecType = kVideoCodecGeneric;
|
||||
}
|
||||
|
||||
if (video_codec.codecType == kVideoCodecVP8) {
|
||||
video_codec.codecSpecific.VP8.resilience = kResilientStream;
|
||||
@ -316,6 +320,10 @@ bool VideoSendStream::ReconfigureVideoEncoder(
|
||||
video_codec.codecSpecific.VP8.automaticResizeOn = false;
|
||||
video_codec.codecSpecific.VP8.frameDroppingOn = true;
|
||||
video_codec.codecSpecific.VP8.keyFrameInterval = 3000;
|
||||
} else if (video_codec.codecType == kVideoCodecH264) {
|
||||
video_codec.codecSpecific.H264.profile = kProfileBase;
|
||||
video_codec.codecSpecific.H264.frameDroppingOn = true;
|
||||
video_codec.codecSpecific.H264.keyFrameInterval = 3000;
|
||||
}
|
||||
|
||||
if (video_codec.codecType == kVideoCodecVP8) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user