Commit 4aeffa3b authored by Hirokazu Honda's avatar Hirokazu Honda Committed by Commit Bot

Reland "media/gpu/test: Group ivf frames whose timestamps are same"

This is a reland of ed3b55f6

Original change's description:
> media/gpu/test: Group ivf frames whose timestamps are same
> 
> The served stream in webrtc can be dumped by the chrome
> option [1]. When the stream structures spatial-layers, one
> spatial layer is separately saved in one ivf in the dumped
> ivf file.
> 
> On the other hand, to play the ivf file properly, it is needed
> to group the spatial layers, which have the same timestamp in
> ivf frame header. IvfFileReader [2] in webrtc does so in fact.
> 
> This applies the group logic to our own ivf parser used in
> VDA test. This enables us to test SVC stream in VDA test.
> 
> [1] --force-fieldtrials=WebRTC-DecoderDataDumpDirectory/;tmp;
> [2] https://source.chromium.org/chromium/chromium/src/+/master:third_party/webrtc/modules/video_coding/utility/ivf_file_reader.h
> 
> Bug: 1044816
> Test: video.DecodeAccel.vp9_ksvc
> Change-Id: I9a0bcfa9083dc71333ad3c727d6cbb6d902202bc
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2032453
> Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
> Reviewed-by: David Staessens <dstaessens@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#740575}

Bug: 1044816
Change-Id: Ic617db810b7d46e2065b6363d27ac1326b35abbc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2072341Reviewed-by: default avatarDavid Staessens <dstaessens@chromium.org>
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746729}
parent 0b8e31e5
......@@ -88,6 +88,13 @@ void VideoFrameFileWriter::ProcessVideoFrame(
if (num_frames_writes_requested_ >= output_limit_)
return;
if (video_frame->visible_rect().IsEmpty()) {
// This occurs in bitstream buffer in webrtc scenario.
DLOG(WARNING) << "Skipping writing, frame_index=" << frame_index
<< " because visible_rect is empty";
return;
}
num_frames_writes_requested_++;
base::AutoLock auto_lock(frame_writer_lock_);
......
......@@ -146,6 +146,13 @@ void VideoFrameValidator::ProcessVideoFrame(
ASSERT_TRUE(model_frame);
}
if (video_frame->visible_rect().IsEmpty()) {
// This occurs in bitstream buffer in webrtc scenario.
DLOG(WARNING) << "Skipping validation, frame_index=" << frame_index
<< " because visible_rect is empty";
return;
}
base::AutoLock auto_lock(frame_validator_lock_);
num_frames_validating_++;
......
......@@ -7,6 +7,7 @@
#include <limits>
#include "base/stl_util.h"
#include "base/sys_byteorder.h"
#include "media/base/video_decoder_config.h"
#include "media/video/h264_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -14,6 +15,20 @@
namespace media {
namespace test {
// The structure for IVF frame header. IVF is a video file format.
// The helpful description is in https://wiki.multimedia.cx/index.php/IVF.
struct EncodedDataHelper::IVFHeader {
uint32_t frame_size;
uint64_t timestamp;
};
// The structure for IVF frame data and header.
// The data to be read is |header.frame_size| bytes from |data|.
struct EncodedDataHelper::IVFFrame {
const char* data;
IVFHeader header;
};
EncodedDataHelper::EncodedDataHelper(const std::vector<uint8_t>& stream,
VideoCodecProfile profile)
: data_(std::string(reinterpret_cast<const char*>(stream.data()),
......@@ -94,39 +109,119 @@ bool EncodedDataHelper::LookForSPS(size_t* skipped_fragments_count) {
scoped_refptr<DecoderBuffer> EncodedDataHelper::GetNextFrame() {
// Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
constexpr size_t kIVFHeaderSize = 32;
constexpr size_t kIVFFrameHeaderSize = 12;
size_t pos = next_pos_to_decode_;
// Only IVF video files are supported. The first 4bytes of an IVF video file's
// header should be "DKIF".
if (pos == 0) {
if (next_pos_to_decode_ == 0) {
if ((data_.size() < kIVFHeaderSize) || strncmp(&data_[0], "DKIF", 4) != 0) {
LOG(ERROR) << "Unexpected data encountered while parsing IVF header";
return nullptr;
}
pos = kIVFHeaderSize; // Skip IVF header.
next_pos_to_decode_ = kIVFHeaderSize; // Skip IVF header.
}
// Group IVF data whose timestamps are the same. Spatial layers in a
// spatial-SVC stream may separately be stored in IVF data, where the
// timestamps of the IVF frame headers are the same. However, it is necessary
// for VD(A) to feed the spatial layers by a single DecoderBuffer. So this
// grouping is required.
std::vector<IVFFrame> ivf_frames;
while (!ReachEndOfStream()) {
auto frame_header = GetNextIVFFrameHeader();
if (!frame_header)
return nullptr;
// Timestamp is different from the current one. The next IVF data must be
// grouped in the next group.
if (!ivf_frames.empty() &&
frame_header->timestamp != ivf_frames[0].header.timestamp) {
break;
}
auto frame_data = ReadNextIVFFrame();
if (!frame_data)
return nullptr;
ivf_frames.push_back(*frame_data);
}
if (ivf_frames.empty()) {
LOG(ERROR) << "No IVF frame is available";
return nullptr;
}
// Standard stream case.
if (ivf_frames.size() == 1) {
return DecoderBuffer::CopyFrom(
reinterpret_cast<const uint8_t*>(ivf_frames[0].data),
ivf_frames[0].header.frame_size);
}
if (ivf_frames.size() > 3) {
LOG(ERROR) << "Number of IVF frames with same timestamps exceeds maximum of"
<< "3: ivf_frames.size()=" << ivf_frames.size();
return nullptr;
}
std::string data;
std::vector<uint32_t> frame_sizes;
frame_sizes.reserve(ivf_frames.size());
for (const IVFFrame& ivf : ivf_frames) {
data.append(ivf.data, ivf.header.frame_size);
frame_sizes.push_back(ivf.header.frame_size);
}
// Copy frame_sizes information to DecoderBuffer's side data. Since side_data
// is uint8_t*, we need to copy as uint8_t from uint32_t. The copied data is
// recognized as uint32_t in VD(A).
const uint8_t* side_data =
reinterpret_cast<const uint8_t*>(frame_sizes.data());
size_t side_data_size =
frame_sizes.size() * sizeof(uint32_t) / sizeof(uint8_t);
return DecoderBuffer::CopyFrom(reinterpret_cast<const uint8_t*>(data.data()),
data.size(), side_data, side_data_size);
}
base::Optional<EncodedDataHelper::IVFHeader>
EncodedDataHelper::GetNextIVFFrameHeader() const {
constexpr size_t kIVFFrameHeaderSize = 12;
const size_t pos = next_pos_to_decode_;
// Read VP8/9 frame size from IVF header.
if (pos + kIVFFrameHeaderSize > data_.size()) {
LOG(ERROR) << "Unexpected data encountered while parsing IVF frame header";
return nullptr;
return base::nullopt;
}
const uint32_t frame_size = *reinterpret_cast<uint32_t*>(&data_[pos]);
pos += kIVFFrameHeaderSize; // Skip IVF frame header.
uint32_t frame_size = 0;
uint64_t timestamp = 0;
memcpy(&frame_size, &data_[pos], 4);
memcpy(&timestamp, &data_[pos + 4], 8);
return IVFHeader{frame_size, timestamp};
}
base::Optional<EncodedDataHelper::IVFFrame>
EncodedDataHelper::ReadNextIVFFrame() {
constexpr size_t kIVFFrameHeaderSize = 12;
auto frame_header = GetNextIVFFrameHeader();
if (!frame_header)
return base::nullopt;
// Skip IVF frame header.
const size_t pos = next_pos_to_decode_ + kIVFFrameHeaderSize;
// Make sure we are not reading out of bounds.
if (pos + frame_size > data_.size()) {
if (pos + frame_header->frame_size > data_.size()) {
LOG(ERROR) << "Unexpected data encountered while parsing IVF frame header";
next_pos_to_decode_ = data_.size();
return nullptr;
return base::nullopt;
}
// Update next_pos_to_decode_.
next_pos_to_decode_ = pos + frame_size;
return DecoderBuffer::CopyFrom(reinterpret_cast<const uint8_t*>(&data_[pos]),
frame_size);
next_pos_to_decode_ = pos + frame_header->frame_size;
return IVFFrame{&data_[pos], *frame_header};
}
// static
......
......@@ -87,10 +87,15 @@ class EncodedDataHelper {
size_t num_skipped_fragments() { return num_skipped_fragments_; }
private:
struct IVFHeader;
struct IVFFrame;
// For h.264.
scoped_refptr<DecoderBuffer> GetNextFragment();
// For VP8/9.
scoped_refptr<DecoderBuffer> GetNextFrame();
base::Optional<IVFHeader> GetNextIVFFrameHeader() const;
base::Optional<IVFFrame> ReadNextIVFFrame();
// Helpers for GetBytesForNextFragment above.
size_t GetBytesForNextNALU(size_t pos);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment