blob: d5cb5431163c3ec0a8a2597849f430a4c1d61c82 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/media_gallery_util/media_parser_android.h"
#include <memory>
#include <vector>
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
#include "media/base/test_data_util.h"
#include "media/media_buildflags.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/service_manager/public/cpp/service_keepalive.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
struct ExtractVideoFrameResult {
bool success = false;
chrome::mojom::VideoFrameDataPtr video_frame_data;
base::Optional<media::VideoDecoderConfig> config;
};
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
// Returns if the first 3 or 4 bytes of H264 encoded |data| is the start code,
// 0x000001 or 0x00000001.
bool HasH264StartCode(const std::vector<uint8_t>& data) {
if (data.size() < 4 || (data[0] != 0u || data[1] != 0u))
return false;
return data[2] == 0x01 || (data[2] == 0u && data[3] == 0x01);
}
#endif
// Returns if the first few bytes in the YUV frame are not all zero. This is a
// rough method to verify the frame is not empty.
bool HasValidYUVData(const scoped_refptr<media::VideoFrame>& frame) {
bool valid = false;
for (size_t i = 0; i < 8; ++i) {
valid |= *(frame->data(media::VideoFrame::kYPlane) + i);
if (valid)
break;
}
return valid;
}
// Used in test that do blocking reads from a local file.
class TestMediaDataSource : public chrome::mojom::MediaDataSource {
public:
TestMediaDataSource(chrome::mojom::MediaDataSourcePtr* interface,
const base::FilePath& file_path)
: file_path_(file_path), binding_(this, mojo::MakeRequest(interface)) {}
~TestMediaDataSource() override {}
private:
// chrome::mojom::MediaDataSource implementation.
void Read(int64_t position,
int64_t length,
chrome::mojom::MediaDataSource::ReadCallback callback) override {
base::File file(file_path_, base::File::Flags::FLAG_OPEN |
base::File::Flags::FLAG_READ);
auto buffer = std::vector<uint8_t>(length);
int bytes_read = file.Read(position, (char*)(buffer.data()), length);
if (bytes_read < length)
buffer.resize(bytes_read);
std::move(callback).Run(std::vector<uint8_t>(std::move(buffer)));
}
base::FilePath file_path_;
mojo::Binding<chrome::mojom::MediaDataSource> binding_;
DISALLOW_COPY_AND_ASSIGN(TestMediaDataSource);
};
class MediaParserAndroidTest : public testing::Test {
public:
MediaParserAndroidTest() : keepalive_(nullptr, base::nullopt) {}
~MediaParserAndroidTest() override = default;
void SetUp() override {
parser_ = std::make_unique<MediaParserAndroid>(keepalive_.CreateRef());
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
void TearDown() override { parser_.reset(); }
protected:
MediaParserAndroid* parser() { return parser_.get(); }
const base::FilePath& temp_dir() const { return temp_dir_.GetPath(); }
ExtractVideoFrameResult ExtractFrame(const base::FilePath& file_path,
const std::string& mime_type) {
int64_t size = 0;
EXPECT_TRUE(base::GetFileSize(file_path, &size));
chrome::mojom::MediaDataSourcePtr data_source_ptr;
TestMediaDataSource test_data_source(&data_source_ptr, file_path);
ExtractVideoFrameResult result;
base::RunLoop run_loop;
parser()->ExtractVideoFrame(
mime_type, size, std::move(data_source_ptr),
base::BindLambdaForTesting(
[&](bool success, chrome::mojom::VideoFrameDataPtr video_frame_data,
const base::Optional<media::VideoDecoderConfig>& config) {
result.success = success;
result.video_frame_data = std::move(video_frame_data);
result.config = config;
run_loop.Quit();
}));
run_loop.Run();
return result;
}
private:
std::unique_ptr<MediaParserAndroid> parser_;
base::ScopedTempDir temp_dir_;
service_manager::ServiceKeepalive keepalive_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(MediaParserAndroidTest);
};
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
// Test to verify an encoded video frame can be extracted for h264 codec video
// file. Decoding needs to happen in other process.
TEST_F(MediaParserAndroidTest, VideoFrameExtractionH264) {
auto result =
ExtractFrame(media::GetTestDataFilePath("bear.mp4"), "video/mp4");
EXPECT_TRUE(result.success);
EXPECT_EQ(result.video_frame_data->which(),
chrome::mojom::VideoFrameData::Tag::ENCODED_DATA);
EXPECT_FALSE(result.video_frame_data->get_encoded_data().empty());
EXPECT_TRUE(HasH264StartCode(result.video_frame_data->get_encoded_data()));
}
#endif
// Test to verify a decoded video frame can be extracted for vp8 codec video
// file with YUV420 color format.
TEST_F(MediaParserAndroidTest, VideoFrameExtractionVp8) {
auto result = ExtractFrame(media::GetTestDataFilePath("bear-vp8-webvtt.webm"),
"video/webm");
EXPECT_TRUE(result.success);
EXPECT_EQ(result.video_frame_data->which(),
chrome::mojom::VideoFrameData::Tag::DECODED_FRAME);
const auto& frame = result.video_frame_data->get_decoded_frame();
EXPECT_TRUE(frame);
EXPECT_TRUE(HasValidYUVData(frame));
EXPECT_TRUE(frame->IsMappable());
EXPECT_FALSE(frame->HasTextures());
EXPECT_EQ(frame->storage_type(),
media::VideoFrame::StorageType::STORAGE_MOJO_SHARED_BUFFER);
}
// Test to verify a decoded video frame can be extracted for vp8 codec with
// alpha plane.
TEST_F(MediaParserAndroidTest, VideoFrameExtractionVp8WithAlphaPlane) {
auto result =
ExtractFrame(media::GetTestDataFilePath("bear-vp8a.webm"), "video/webm");
EXPECT_TRUE(result.success);
EXPECT_EQ(result.video_frame_data->which(),
chrome::mojom::VideoFrameData::Tag::DECODED_FRAME);
const auto& frame = result.video_frame_data->get_decoded_frame();
EXPECT_TRUE(frame);
EXPECT_TRUE(HasValidYUVData(frame));
EXPECT_TRUE(frame->IsMappable());
EXPECT_FALSE(frame->HasTextures());
EXPECT_EQ(frame->storage_type(),
media::VideoFrame::StorageType::STORAGE_MOJO_SHARED_BUFFER);
}
// Test to verify frame extraction will fail on invalid video file.
TEST_F(MediaParserAndroidTest, VideoFrameExtractionInvalidFile) {
base::FilePath dummy_file = temp_dir().AppendASCII("test.txt");
EXPECT_GT(base::WriteFile(dummy_file, "123", sizeof("123")), 0);
auto result = ExtractFrame(dummy_file, "video/webm");
EXPECT_FALSE(result.success);
}
} // namespace