In generic encoder ensure capture timestamp is preserved.
Because some hardware encoders may not preserve capture timestamp, use rtp timestamp as a key to capture time storage and restore corrupted capture timestamps whenever possible. Bug: webrtc:8497 Change-Id: Ib3449313c65e864a89cd645a3f5dec99aa9cf8e4 Reviewed-on: https://webrtc-review.googlesource.com/23620 Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Magnus Jedvert <magjed@webrtc.org> Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20732}
This commit is contained in:

committed by
Commit Bot

parent
2707fb2782
commit
76f2a85027
@ -28,13 +28,16 @@ inline size_t FrameSize(const size_t& min_frame_size,
|
||||
class FakeEncodedImageCallback : public EncodedImageCallback {
|
||||
public:
|
||||
FakeEncodedImageCallback()
|
||||
: last_frame_was_timing_(false), num_frames_dropped_(0) {}
|
||||
: last_frame_was_timing_(false),
|
||||
num_frames_dropped_(0),
|
||||
last_capture_timestamp_(-1) {}
|
||||
Result OnEncodedImage(const EncodedImage& encoded_image,
|
||||
const CodecSpecificInfo* codec_specific_info,
|
||||
const RTPFragmentationHeader* fragmentation) override {
|
||||
last_frame_was_timing_ =
|
||||
encoded_image.timing_.flags != TimingFrameFlags::kInvalid &&
|
||||
encoded_image.timing_.flags != TimingFrameFlags::kNotTriggered;
|
||||
last_capture_timestamp_ = encoded_image.capture_time_ms_;
|
||||
return Result(Result::OK);
|
||||
};
|
||||
|
||||
@ -44,9 +47,12 @@ class FakeEncodedImageCallback : public EncodedImageCallback {
|
||||
|
||||
size_t GetNumFramesDropped() { return num_frames_dropped_; }
|
||||
|
||||
int64_t GetLastCaptureTimestamp() { return last_capture_timestamp_; }
|
||||
|
||||
private:
|
||||
bool last_frame_was_timing_;
|
||||
size_t num_frames_dropped_;
|
||||
int64_t last_capture_timestamp_;
|
||||
};
|
||||
|
||||
enum class FrameType {
|
||||
@ -87,9 +93,11 @@ std::vector<std::vector<FrameType>> GetTimingFrames(
|
||||
CodecSpecificInfo codec_specific;
|
||||
image._length = FrameSize(min_frame_size, max_frame_size, s, i);
|
||||
image.capture_time_ms_ = current_timestamp;
|
||||
image._timeStamp = static_cast<uint32_t>(current_timestamp * 90);
|
||||
codec_specific.codecType = kVideoCodecGeneric;
|
||||
codec_specific.codecSpecific.generic.simulcast_idx = s;
|
||||
callback.OnEncodeStarted(current_timestamp, s);
|
||||
callback.OnEncodeStarted(static_cast<uint32_t>(current_timestamp * 90),
|
||||
current_timestamp, s);
|
||||
if (dropped) {
|
||||
result[s].push_back(FrameType::kDropped);
|
||||
continue;
|
||||
@ -178,6 +186,7 @@ TEST(TestVCMEncodedFrameCallback, NoTimingFrameIfNoEncodeStartTime) {
|
||||
int64_t timestamp = 1;
|
||||
image._length = 500;
|
||||
image.capture_time_ms_ = timestamp;
|
||||
image._timeStamp = static_cast<uint32_t>(timestamp * 90);
|
||||
codec_specific.codecType = kVideoCodecGeneric;
|
||||
codec_specific.codecSpecific.generic.simulcast_idx = 0;
|
||||
FakeEncodedImageCallback sink;
|
||||
@ -188,12 +197,13 @@ TEST(TestVCMEncodedFrameCallback, NoTimingFrameIfNoEncodeStartTime) {
|
||||
callback.OnTargetBitrateChanged(500, 0);
|
||||
|
||||
// Verify a single frame works with encode start time set.
|
||||
callback.OnEncodeStarted(timestamp, 0);
|
||||
callback.OnEncodeStarted(static_cast<uint32_t>(timestamp * 90), timestamp, 0);
|
||||
callback.OnEncodedImage(image, &codec_specific, nullptr);
|
||||
EXPECT_TRUE(sink.WasTimingFrame());
|
||||
|
||||
// New frame, now skip OnEncodeStarted. Should not result in timing frame.
|
||||
image.capture_time_ms_ = ++timestamp;
|
||||
image._timeStamp = static_cast<uint32_t>(timestamp * 90);
|
||||
callback.OnEncodedImage(image, &codec_specific, nullptr);
|
||||
EXPECT_FALSE(sink.WasTimingFrame());
|
||||
}
|
||||
@ -201,33 +211,59 @@ TEST(TestVCMEncodedFrameCallback, NoTimingFrameIfNoEncodeStartTime) {
|
||||
TEST(TestVCMEncodedFrameCallback, NotifiesAboutDroppedFrames) {
|
||||
EncodedImage image;
|
||||
CodecSpecificInfo codec_specific;
|
||||
const int64_t timestamp1 = 100;
|
||||
const int64_t timestamp2 = 110;
|
||||
const int64_t timestamp3 = 120;
|
||||
const int64_t timestamp4 = 130;
|
||||
const int64_t kTimestampMs1 = 47721840;
|
||||
const int64_t kTimestampMs2 = 47721850;
|
||||
const int64_t kTimestampMs3 = 47721860;
|
||||
const int64_t kTimestampMs4 = 47721870;
|
||||
codec_specific.codecType = kVideoCodecGeneric;
|
||||
codec_specific.codecSpecific.generic.simulcast_idx = 0;
|
||||
FakeEncodedImageCallback sink;
|
||||
VCMEncodedFrameCallback callback(&sink, nullptr);
|
||||
// Any non-zero bitrate needed to be set before the first frame.
|
||||
callback.OnTargetBitrateChanged(500, 0);
|
||||
callback.OnEncodeStarted(timestamp1, 0);
|
||||
image.capture_time_ms_ = kTimestampMs1;
|
||||
image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
|
||||
callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
|
||||
EXPECT_EQ(0u, sink.GetNumFramesDropped());
|
||||
image.capture_time_ms_ = timestamp1;
|
||||
callback.OnEncodedImage(image, &codec_specific, nullptr);
|
||||
callback.OnEncodeStarted(timestamp2, 0);
|
||||
|
||||
image.capture_time_ms_ = kTimestampMs2;
|
||||
image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
|
||||
callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
|
||||
// No OnEncodedImageCall for timestamp2. Yet, at this moment it's not known
|
||||
// that frame with timestamp2 was dropped.
|
||||
EXPECT_EQ(0u, sink.GetNumFramesDropped());
|
||||
callback.OnEncodeStarted(timestamp3, 0);
|
||||
image.capture_time_ms_ = timestamp3;
|
||||
|
||||
image.capture_time_ms_ = kTimestampMs3;
|
||||
image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
|
||||
callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
|
||||
callback.OnEncodedImage(image, &codec_specific, nullptr);
|
||||
EXPECT_EQ(1u, sink.GetNumFramesDropped());
|
||||
callback.OnEncodeStarted(timestamp4, 0);
|
||||
image.capture_time_ms_ = timestamp4;
|
||||
|
||||
image.capture_time_ms_ = kTimestampMs4;
|
||||
image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
|
||||
callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
|
||||
callback.OnEncodedImage(image, &codec_specific, nullptr);
|
||||
EXPECT_EQ(1u, sink.GetNumFramesDropped());
|
||||
}
|
||||
|
||||
TEST(TestVCMEncodedFrameCallback, RestoresCaptureTimestamps) {
|
||||
EncodedImage image;
|
||||
CodecSpecificInfo codec_specific;
|
||||
const int64_t kTimestampMs = 123456;
|
||||
codec_specific.codecType = kVideoCodecGeneric;
|
||||
codec_specific.codecSpecific.generic.simulcast_idx = 0;
|
||||
FakeEncodedImageCallback sink;
|
||||
VCMEncodedFrameCallback callback(&sink, nullptr);
|
||||
// Any non-zero bitrate needed to be set before the first frame.
|
||||
callback.OnTargetBitrateChanged(500, 0);
|
||||
image.capture_time_ms_ = kTimestampMs; // Incorrect timesetamp.
|
||||
image._timeStamp = static_cast<uint32_t>(image.capture_time_ms_ * 90);
|
||||
callback.OnEncodeStarted(image._timeStamp, image.capture_time_ms_, 0);
|
||||
image.capture_time_ms_ = 0; // Incorrect timesetamp.
|
||||
callback.OnEncodedImage(image, &codec_specific, nullptr);
|
||||
EXPECT_EQ(kTimestampMs, sink.GetLastCaptureTimestamp());
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
Reference in New Issue
Block a user