#include "third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h"
#include <stddef.h>
#include <string>
#include <string_view>
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "media/audio/simple_sources.h"
#include "media/base/audio_bus.h"
#include "media/base/video_color_space.h"
#include "media/base/video_frame.h"
#include "media/formats/mp4/box_definitions.h"
#include "media/media_buildflags.h"
#include "media/mojo/clients/mojo_audio_encoder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/web/web_heap.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/core/testing/scoped_mock_overlay_scrollbars.h"
#include "third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h"
#include "third_party/blink/renderer/modules/mediarecorder/fake_encoded_video_frame.h"
#include "third_party/blink/renderer/modules/mediarecorder/media_recorder.h"
#include "third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h"
#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_registry.h"
#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/heap/weak_cell.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_component_impl.h"
#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
RunOnceClosure;
_;
AtLeast;
Ge;
Gt;
InSequence;
InvokeWithoutArgs;
Lt;
Mock;
Return;
SizeIs;
TestWithParam;
ValuesIn;
#if BUILDFLAG(IS_WIN)
#include "base/test/scoped_os_info_override_win.h"
#include "media/gpu/windows/mf_audio_encoder.h"
#define HAS_AAC_ENCODER …
#endif
#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)) && \
BUILDFLAG(USE_PROPRIETARY_CODECS)
#define HAS_AAC_ENCODER …
#endif
namespace blink {
static String TestVideoTrackId() { … }
static String TestAudioTrackId() { … }
static const int kTestAudioChannels = …;
static const int kTestAudioSampleRate = …;
static const int kTestAudioBufferDurationMs = …;
static const int kRatioOpusToTestAudioBuffers = …;
struct MediaRecorderTestParams { … };
static const MediaRecorderTestParams kMediaRecorderTestParams[] = …;
MediaStream* CreateMediaStream(V8TestingScope& scope) { … }
class MockMediaRecorder : public MediaRecorder { … };
class MediaRecorderHandlerFixture : public ScopedMockOverlayScrollbars { … };
class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams>,
public MediaRecorderHandlerFixture { … };
TEST_P(MediaRecorderHandlerTest, CanSupportMimeType) { … }
TEST_P(MediaRecorderHandlerTest, SupportsBitrateMode) { … }
TEST_P(MediaRecorderHandlerTest, InitializeFailedWhenMP4MuxerFeatureDisabled) { … }
TEST_P(MediaRecorderHandlerTest, EncodeVideoFrames) { … }
TEST_P(MediaRecorderHandlerTest, OpusEncodeAudioFrames) { … }
TEST_P(MediaRecorderHandlerTest, WebmMuxerErrorWhileEncoding) { … }
TEST_P(MediaRecorderHandlerTest, ActualMimeType) { … }
TEST_P(MediaRecorderHandlerTest, PauseRecorderForVideo) { … }
TEST_P(MediaRecorderHandlerTest, StartStopStartRecorderForVideo) { … }
INSTANTIATE_TEST_SUITE_P(…);
class MediaRecorderHandlerTestForMp4
: public TestWithParam<MediaRecorderTestParams>,
public MediaRecorderHandlerFixture { … };
static const MediaRecorderTestParams kMediaRecorderTestParamsForMp4[] = …;
TEST_P(MediaRecorderHandlerTestForMp4,
InitializeFailedWhenMP4MuxerFeatureDisabled) { … }
INSTANTIATE_TEST_SUITE_P(…);
class MediaRecorderHandlerIsSupportedTypeTestForMp4
: public TestWithParam<bool>,
public MediaRecorderHandlerFixture { … };
TEST_P(MediaRecorderHandlerIsSupportedTypeTestForMp4,
CanSupportMimeTypeForMp4) { … }
#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_PROPRIETARY_CODECS)
TEST_P(MediaRecorderHandlerIsSupportedTypeTestForMp4,
CanSupportAacCodecForWinNSku) {
if (!GetParam()) {
GTEST_SKIP();
}
if (!IsTargetAudioCodecSupported("mp4a.40.2")) {
return;
}
{
base::test::ScopedOSInfoOverride scoped_os_info_override(
base::test::ScopedOSInfoOverride::Type::kWin11Home);
EXPECT_TRUE(
media_recorder_handler_->CanSupportMimeType("audio/mp4", "mp4a.40.2"));
}
{
base::test::ScopedOSInfoOverride scoped_os_info_override(
base::test::ScopedOSInfoOverride::Type::kWin11HomeN);
EXPECT_FALSE(
media_recorder_handler_->CanSupportMimeType("audio/mp4", "mp4a.40.2"));
}
}
#endif
INSTANTIATE_TEST_SUITE_P(…);
class MediaRecorderHandlerAudioVideoTest : public testing::Test,
public MediaRecorderHandlerFixture { … };
TEST_F(MediaRecorderHandlerAudioVideoTest, IgnoresStaleEncodedMediaOnRestart) { … }
TEST_F(MediaRecorderHandlerAudioVideoTest, EmitsCachedAudioDataOnStop) { … }
TEST_F(MediaRecorderHandlerAudioVideoTest, EmitsCachedVideoDataOnStop) { … }
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
TEST_F(MediaRecorderHandlerAudioVideoTest, CorrectH264LevelOnWrite) {
AddTracks();
V8TestingScope scope;
auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), "video/webm", "avc1.640022,opus",
AudioTrackRecorder::BitrateMode::kVariable);
EXPECT_EQ(media_recorder_handler_->ActualMimeType(),
"video/x-matroska;codecs=avc1.640022,opus");
media_recorder_handler_->Start(std::numeric_limits<int>::max(), "video/webm",
0, 0);
FeedAudio();
OnEncodedH264VideoForTesting(base::TimeTicks::Now());
EXPECT_CALL(*recorder, WriteData).Times(AtLeast(1));
media_recorder_handler_->Stop();
EXPECT_EQ(media_recorder_handler_->ActualMimeType(),
"video/x-matroska;codecs=avc1.64000d,opus");
media_recorder_handler_ = nullptr;
Mock::VerifyAndClearExpectations(recorder);
}
#endif
TEST_F(MediaRecorderHandlerAudioVideoTest,
EmitsCachedAudioDataAfterVideoTrackEnded) { … }
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
struct H264ProfileTestParams {
const bool has_audio;
const char* const mime_type;
const char* const codecs;
};
static const H264ProfileTestParams kH264ProfileTestParams[] = {
{false, "video/x-matroska", "avc1.42000c"},
{false, "video/x-matroska", "avc1.4d000c"},
{false, "video/x-matroska", "avc1.64000c"},
{false, "video/x-matroska", "avc1.640029"},
{false, "video/x-matroska", "avc1.640034"},
{true, "video/x-matroska", "avc1.64000c,pcm"},
{false, "video/mp4", "avc1.42000c"},
{false, "video/mp4", "avc1.4d000c"},
{false, "video/mp4", "avc1.64000c"},
{false, "video/mp4", "avc1.640029"},
{false, "video/mp4", "avc1.640034"},
};
class MediaRecorderHandlerH264ProfileTest
: public TestWithParam<H264ProfileTestParams>,
public MediaRecorderHandlerFixture {
public:
MediaRecorderHandlerH264ProfileTest()
: MediaRecorderHandlerFixture(true, GetParam().has_audio) {
scoped_feature_list_.InitAndEnableFeature(kMediaRecorderEnableMp4Muxer);
}
MediaRecorderHandlerH264ProfileTest(
const MediaRecorderHandlerH264ProfileTest&) = delete;
MediaRecorderHandlerH264ProfileTest& operator=(
const MediaRecorderHandlerH264ProfileTest&) = delete;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_P(MediaRecorderHandlerH264ProfileTest, ActualMimeType) {
AddTracks();
V8TestingScope scope;
auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs,
AudioTrackRecorder::BitrateMode::kVariable));
String actual_mime_type =
String(GetParam().mime_type) + ";codecs=" + GetParam().codecs;
EXPECT_EQ(media_recorder_handler_->ActualMimeType(), actual_mime_type);
media_recorder_handler_ = nullptr;
}
INSTANTIATE_TEST_SUITE_P(All,
MediaRecorderHandlerH264ProfileTest,
ValuesIn(kH264ProfileTestParams));
#if BUILDFLAG(IS_WIN)
class MediaRecorderHandlerWinAacCodecTest : public TestWithParam<unsigned int>,
public MediaRecorderHandlerFixture {
public:
MediaRecorderHandlerWinAacCodecTest()
: MediaRecorderHandlerFixture(false, true) {
scoped_feature_list_.InitAndEnableFeature(kMediaRecorderEnableMp4Muxer);
}
MediaRecorderHandlerWinAacCodecTest(
const MediaRecorderHandlerWinAacCodecTest&) = delete;
MediaRecorderHandlerWinAacCodecTest& operator=(
const MediaRecorderHandlerWinAacCodecTest&) = delete;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_P(MediaRecorderHandlerWinAacCodecTest, AudioBitsPerSeconds) {
const String codecs("mp4a.40.2");
if (!IsTargetAudioCodecSupported(codecs)) {
return;
}
AddTracks();
V8TestingScope scope;
auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
const String mime_type("audio/mp4");
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs,
AudioTrackRecorder::BitrateMode::kVariable));
media_recorder_handler_->Start(0, mime_type, GetParam(), 0);
EXPECT_EQ(media::MFAudioEncoder::ClampAccCodecBitrate(GetParam()),
recorder->audioBitsPerSecond());
media_recorder_handler_->Stop();
media_recorder_handler_ = nullptr;
}
INSTANTIATE_TEST_SUITE_P(All,
MediaRecorderHandlerWinAacCodecTest,
ValuesIn({5000u, 96000u, 128000u, 160000u, 192000u,
256000u, 300000u}));
#endif
#endif
struct MediaRecorderPassthroughTestParams { … };
static const MediaRecorderPassthroughTestParams
kMediaRecorderPassthroughTestParams[] = …;
class MediaRecorderHandlerPassthroughTest
: public TestWithParam<MediaRecorderPassthroughTestParams>,
public ScopedMockOverlayScrollbars { … };
TEST_P(MediaRecorderHandlerPassthroughTest, PassesThrough) { … }
TEST_F(MediaRecorderHandlerPassthroughTest, ErrorsOutOnCodecSwitch) { … }
INSTANTIATE_TEST_SUITE_P(…);
}