Skip to content

Commit 2f8da4d

Browse files
voluntasmelpon
andauthored
fake-capture-device で nv12 を出力するように変更する (#422)
* fake-capture-device で nv12 を出力するように変更する * formatter * コメントを修正 * blend2d 0.20.0 追従 --------- Co-authored-by: melpon <[email protected]>
1 parent a9c1fb8 commit 2f8da4d

File tree

6 files changed

+128
-60
lines changed

6 files changed

+128
-60
lines changed

src/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ int main(int argc, char* argv[]) {
153153
video_config.width = size.width;
154154
video_config.height = size.height;
155155
video_config.fps = args.framerate;
156+
video_config.force_nv12 = args.force_nv12;
156157
return FakeVideoCapturer::Create(video_config);
157158
}
158159
#endif

src/rtc/fake_video_capturer.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <sstream>
1010

1111
// WebRTC
12+
#include <api/video/nv12_buffer.h>
1213
#include <rtc_base/logging.h>
1314
#include <third_party/libyuv/include/libyuv.h>
1415

@@ -70,7 +71,7 @@ void FakeVideoCapturer::CaptureThread() {
7071
// 画像を更新
7172
UpdateImage(now);
7273

73-
// Blend2D イメージから I420 バッファへ変換
74+
// Blend2D イメージから VideoFrameBuffer へ変換
7475
BLImageData data;
7576
BLResult result = image_.get_data(&data);
7677
if (result != BL_SUCCESS) {
@@ -79,14 +80,25 @@ void FakeVideoCapturer::CaptureThread() {
7980
break;
8081
}
8182

82-
webrtc::scoped_refptr<webrtc::I420Buffer> buffer =
83-
webrtc::I420Buffer::Create(config_.width, config_.height);
84-
85-
libyuv::ABGRToI420((const uint8_t*)data.pixel_data, data.stride,
86-
buffer->MutableDataY(), buffer->StrideY(),
87-
buffer->MutableDataU(), buffer->StrideU(),
88-
buffer->MutableDataV(), buffer->StrideV(), config_.width,
89-
config_.height);
83+
// 出力フォーマットを選択
84+
webrtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer;
85+
if (config_.force_nv12) {
86+
// NV12 へ変換
87+
auto nv12 = webrtc::NV12Buffer::Create(config_.width, config_.height);
88+
libyuv::ABGRToNV12((const uint8_t*)data.pixel_data, data.stride,
89+
nv12->MutableDataY(), nv12->StrideY(),
90+
nv12->MutableDataUV(), nv12->StrideUV(), config_.width,
91+
config_.height);
92+
buffer = nv12;
93+
} else {
94+
// 既定は I420
95+
auto i420 = webrtc::I420Buffer::Create(config_.width, config_.height);
96+
libyuv::ABGRToI420(
97+
(const uint8_t*)data.pixel_data, data.stride, i420->MutableDataY(),
98+
i420->StrideY(), i420->MutableDataU(), i420->StrideU(),
99+
i420->MutableDataV(), i420->StrideV(), config_.width, config_.height);
100+
buffer = i420;
101+
}
90102

91103
// タイムスタンプを計算
92104
int64_t timestamp_us =
@@ -150,7 +162,7 @@ void FakeVideoCapturer::DrawAnimations(
150162
ctx.set_fill_style(BLRgba32(160, 160, 160));
151163
uint32_t current_frame = frame_counter_;
152164
ctx.fill_pie(0, 0, width * 0.3, 0,
153-
(current_frame % fps) / static_cast<float>(fps) * 2 * M_PI);
165+
(current_frame % fps) / static_cast<float>(fps) * 2 * M_PI);
154166

155167
// 円が一周したときにビープ音を鳴らす
156168
auto fake_audio_capturer = GetAudioCapturer();
@@ -260,7 +272,7 @@ void FakeVideoCapturer::DrawDigitalClock(
260272

261273
// ドット
262274
ctx.fill_circle(x + colon_width * 0.3, clock_y + digit_height * 0.8,
263-
digit_height * 0.05);
275+
digit_height * 0.05);
264276
x += colon_width + spacing;
265277

266278
// ミリ秒(3桁、少し小さめに表示)
@@ -385,4 +397,4 @@ void FakeVideoCapturer::DrawColon(BLContext& ctx,
385397
ctx.fill_circle(x + dot_size, y + height * 0.7, dot_size);
386398
}
387399

388-
#endif // USE_FAKE_CAPTURE_DEVICE
400+
#endif // USE_FAKE_CAPTURE_DEVICE

src/rtc/fake_video_capturer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class FakeVideoCapturer : public sora::ScalableVideoTrackSource {
2727
int width = 640;
2828
int height = 480;
2929
int fps = 30;
30+
bool force_nv12 = false;
3031
};
3132

3233
static webrtc::scoped_refptr<FakeVideoCapturer> Create(Config config) {
@@ -75,4 +76,4 @@ class FakeVideoCapturer : public sora::ScalableVideoTrackSource {
7576

7677
#endif // USE_FAKE_CAPTURE_DEVICE
7778

78-
#endif // RTC_FAKE_VIDEO_CAPTURER_H_
79+
#endif // RTC_FAKE_VIDEO_CAPTURER_H_

test/momo.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def __init__(
3434
no_audio_device: bool = False,
3535
fake_capture_device: bool = True,
3636
force_i420: bool = False,
37+
force_yuy2: bool = False,
38+
force_nv12: bool = False,
3739
hw_mjpeg_decoder: bool | None = None,
3840
use_libcamera: bool = False,
3941
use_libcamera_native: bool = False,
@@ -149,6 +151,8 @@ def __init__(
149151
"no_audio_device": no_audio_device,
150152
"fake_capture_device": fake_capture_device,
151153
"force_i420": force_i420,
154+
"force_yuy2": force_yuy2,
155+
"force_nv12": force_nv12,
152156
"hw_mjpeg_decoder": hw_mjpeg_decoder,
153157
"use_libcamera": use_libcamera,
154158
"use_libcamera_native": use_libcamera_native,
@@ -377,6 +381,10 @@ def _build_args(self, mode: MomoMode, **kwargs: Any) -> list[str]:
377381
args.append("--fake-capture-device")
378382
if kwargs.get("force_i420"):
379383
args.append("--force-i420")
384+
if kwargs.get("force_yuy2"):
385+
args.append("--force-yuy2")
386+
if kwargs.get("force_nv12"):
387+
args.append("--force-nv12")
380388
if kwargs.get("hw_mjpeg_decoder") is not None:
381389
args.extend(["--hw-mjpeg-decoder", str(int(kwargs["hw_mjpeg_decoder"]))])
382390
if kwargs.get("use_libcamera"):

test/test_sora_mode_nv12.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import os
2+
3+
import pytest
4+
5+
from momo import Momo, MomoMode
6+
7+
# Sora モードのテストは TEST_SORA_MODE_SIGNALING_URLS が設定されていない場合スキップ
8+
pytestmark = pytest.mark.skipif(
9+
not os.environ.get("TEST_SORA_MODE_SIGNALING_URLS"),
10+
reason="TEST_SORA_MODE_SIGNALING_URLS not set in environment",
11+
)
12+
13+
14+
def test_sendonly_with_force_nv12_outbound_rtp(sora_settings, free_port):
15+
"""Sora モードで fake capture + --force-nv12 を指定し、送信統計が取得できることを確認"""
16+
17+
with Momo(
18+
mode=MomoMode.SORA,
19+
metrics_port=free_port,
20+
fake_capture_device=True,
21+
signaling_urls=sora_settings.signaling_urls,
22+
channel_id=sora_settings.channel_id,
23+
role="sendonly",
24+
audio=True,
25+
video=True,
26+
force_nv12=True,
27+
metadata=sora_settings.metadata,
28+
) as m:
29+
# 接続が確立するまで待機
30+
assert m.wait_for_connection(), "Failed to establish connection with --force-nv12"
31+
32+
# outbound-rtp(kind=video) が出現するまでメトリクスを待つ
33+
data = m.get_metrics(
34+
wait_stats=[{"type": "outbound-rtp", "kind": "video"}],
35+
wait_stats_timeout=10,
36+
)
37+
stats = data.get("stats", [])
38+
39+
# 送信(映像)の統計が存在し、送信が進んでいることを確認
40+
video_outbound = [
41+
s for s in stats if s.get("type") == "outbound-rtp" and s.get("kind") == "video"
42+
]
43+
assert len(video_outbound) >= 1, "No video outbound-rtp stats found"
44+
v = video_outbound[0]
45+
assert v.get("packetsSent", 0) > 0
46+
assert v.get("bytesSent", 0) > 0

0 commit comments

Comments
 (0)