chromium/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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 =;
// Opus works with 60ms buffers, so 6 MediaStreamAudioTrack Buffers are needed
// to encode one output buffer.
static const int kRatioOpusToTestAudioBuffers =;

struct MediaRecorderTestParams {};

// Array of valid combinations of video/audio/codecs and expected collected
// encoded sizes to use for parameterizing MediaRecorderHandlerTest.
static const MediaRecorderTestParams kMediaRecorderTestParams[] =;

MediaStream* CreateMediaStream(V8TestingScope& scope) {}

class MockMediaRecorder : public MediaRecorder {};

class MediaRecorderHandlerFixture : public ScopedMockOverlayScrollbars {};

class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams>,
                                 public MediaRecorderHandlerFixture {};

// Checks that canSupportMimeType() works as expected, by sending supported
// combinations and unsupported ones.
TEST_P(MediaRecorderHandlerTest, CanSupportMimeType) {}

// Checks that it uses the specified bitrate mode.
TEST_P(MediaRecorderHandlerTest, SupportsBitrateMode) {}

// Checks that the initialization-destruction sequence works fine.
TEST_P(MediaRecorderHandlerTest, InitializeFailedWhenMP4MuxerFeatureDisabled) {}

// Sends 2 opaque frames and 1 transparent frame and expects them as WebM
// contained encoded data in writeData().
TEST_P(MediaRecorderHandlerTest, EncodeVideoFrames) {}

// Sends 2 frames and expect them as WebM (or MKV) contained encoded audio data
// in writeData().
TEST_P(MediaRecorderHandlerTest, OpusEncodeAudioFrames) {}

// Starts up recording and forces a WebmMuxer's libwebm error.
TEST_P(MediaRecorderHandlerTest, WebmMuxerErrorWhileEncoding) {}

// Checks the ActualMimeType() versus the expected.
TEST_P(MediaRecorderHandlerTest, ActualMimeType) {}

TEST_P(MediaRecorderHandlerTest, PauseRecorderForVideo) {}

TEST_P(MediaRecorderHandlerTest, StartStopStartRecorderForVideo) {}

INSTANTIATE_TEST_SUITE_P();
class MediaRecorderHandlerTestForMp4
    : public TestWithParam<MediaRecorderTestParams>,
      public MediaRecorderHandlerFixture {};

// Array of valid combinations of video/audio/codecs for mp4.
static const MediaRecorderTestParams kMediaRecorderTestParamsForMp4[] =;

TEST_P(MediaRecorderHandlerTestForMp4,
       InitializeFailedWhenMP4MuxerFeatureDisabled) {}

INSTANTIATE_TEST_SUITE_P();

class MediaRecorderHandlerIsSupportedTypeTestForMp4
    : public TestWithParam<bool>,
      public MediaRecorderHandlerFixture {};

// Checks that canSupportMimeType() works as expected, by sending supported
// combinations and unsupported ones.
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  // BUILDFLAG(IS_WIN) && BUILDFLAG(USE_PROPRIETARY_CODECS)

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);

  // Feed some encoded data into the recorder. Expect that data cached by the
  // muxer is emitted on the call to Stop.
  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"},  // H264PROFILE_BASELINE
    {false, "video/x-matroska", "avc1.4d000c"},  // H264PROFILE_MAIN
    {false, "video/x-matroska", "avc1.64000c"},  // H264PROFILE_HIGH
    {false, "video/x-matroska", "avc1.640029"},
    {false, "video/x-matroska", "avc1.640034"},
    {true, "video/x-matroska", "avc1.64000c,pcm"},
    {false, "video/mp4", "avc1.42000c"},  // H264PROFILE_BASELINE
    {false, "video/mp4", "avc1.4d000c"},  // H264PROFILE_MAIN
    {false, "video/mp4", "avc1.64000c"},  // H264PROFILE_HIGH
    {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  // BUILDFLAG(IS_WIN)
#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)

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();

}  // namespace blink