Skip to content

Commit

Permalink
[feature] 自己实现捕获摄像头
Browse files Browse the repository at this point in the history
  • Loading branch information
xianglai committed Oct 21, 2020
1 parent dbdfc7d commit 4965451
Show file tree
Hide file tree
Showing 6 changed files with 389 additions and 0 deletions.
73 changes: 73 additions & 0 deletions examples/BUILD.gn
Expand Up @@ -49,6 +49,7 @@ group("examples") {
":peerconnection_server",
":stunserver",
":turnserver",
":video_capture_demo",
]
if (current_os != "winuwp") {
deps += [ ":peerconnection_client" ]
Expand Down Expand Up @@ -770,6 +771,78 @@ if (is_linux || is_win) {
"../rtc_base:rtc_base_approved",
]
}
rtc_executable("video_capture_demo") {
testonly = true
sources = [
"video_capture/vcm_capturer_test.cc",
"video_capture/vcm_capturer_test.h",
"video_capture/video_capture_demo.cc",
"video_capture/video_capturer_test.cc",
"video_capture/video_capturer_test.h",
]

deps = [
"../api:audio_options_api",
"../api:create_peerconnection_factory",
"../api:libjingle_peerconnection_api",
"../api:media_stream_interface",
"../api:scoped_refptr",
"../api/audio:audio_mixer_api",
"../api/audio_codecs:audio_codecs_api",
"../api/video:video_frame_i420",
"../api/video:video_rtp_headers",
"../api/video_codecs:video_codecs_api",
"../media:rtc_media_base",
"../p2p:rtc_p2p",
"../rtc_base:checks",
"../rtc_base/third_party/sigslot",
"../system_wrappers:field_trial",
"../test:field_trial",
"../test:platform_video_capturer",
"../test:rtp_test_utils",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/types:optional",
]
if (is_win) {
configs += [ "//build/config/win:windowed" ]
deps += [ "../media:rtc_media_base" ]
}

if(is_linux) {
cflags = [ "-Wno-deprecated-declarations" ]
libs = [
"X11",
"Xcomposite",
"Xext",
"Xrender",
]
}

deps += [
"../api:libjingle_peerconnection_api",
"../api/audio_codecs:builtin_audio_decoder_factory",
"../api/audio_codecs:builtin_audio_encoder_factory",
"../api/video:video_frame",
"../api/video:video_rtp_headers",
"../api/video_codecs:builtin_video_decoder_factory",
"../api/video_codecs:builtin_video_encoder_factory",
"../media:rtc_audio_video",
"../modules/audio_device",
"../modules/audio_processing",
"../modules/audio_processing:api",
"../modules/video_capture:video_capture_module",
"../pc:libjingle_peerconnection",
"../pc:peerconnection",
"../rtc_base",
"../rtc_base:rtc_base_approved",
"../rtc_base:rtc_json",
"../test:video_test_common",
"../test:test_renderer",
"//third_party/abseil-cpp/absl/flags:flag",
"//third_party/abseil-cpp/absl/flags:parse",
"//third_party/libyuv",
]
}
}

if (is_win || is_android) {
Expand Down
94 changes: 94 additions & 0 deletions examples/video_capture/vcm_capturer_test.cc
@@ -0,0 +1,94 @@
#include "examples/video_capture/vcm_capturer_test.h"

#include "modules/video_capture/video_capture_factory.h"
#include "rtc_base/logging.h"

namespace webrtc_demo {

VcmCapturerTest::VcmCapturerTest() : vcm_(nullptr) {}

VcmCapturerTest::~VcmCapturerTest() {
Destroy();
}

bool VcmCapturerTest::Init(size_t width,
size_t height,
size_t target_fps,
size_t capture_device_index) {
std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> device_info(
webrtc::VideoCaptureFactory::CreateDeviceInfo());

char device_name[256];
char unique_name[256];

if (device_info->GetDeviceName(static_cast<uint32_t>(capture_device_index),
device_name, sizeof(device_name), unique_name,
sizeof(unique_name)) != 0) {
Destroy();
return false;
}

vcm_ = webrtc::VideoCaptureFactory::Create(unique_name);
if (!vcm_) {
return false;
}
vcm_->RegisterCaptureDataCallback(this);

device_info->GetCapability(vcm_->CurrentDeviceName(), 0, capability_);
capability_.width = static_cast<int32_t>(width);
capability_.height = static_cast<int32_t>(height);
capability_.maxFPS = static_cast<int32_t>(target_fps);
capability_.videoType = webrtc::VideoType::kI420;

if (vcm_->StartCapture(capability_) != 0) {
Destroy();
return false;
}

RTC_CHECK(vcm_->CaptureStarted());
return true;
}

VcmCapturerTest* VcmCapturerTest::Create(size_t width,
size_t height,
size_t target_fps,
size_t capture_device_index) {
std::unique_ptr<VcmCapturerTest> vcm_capturer(new VcmCapturerTest());
if (!vcm_capturer->Init(width, height, target_fps, capture_device_index)) {
RTC_LOG(LS_WARNING) << "Failed to create VcmCapturer(w = " << width
<< ", h = " << height << ", fps = " << target_fps
<< ")";
return nullptr;
}
return vcm_capturer.release();
}

void VcmCapturerTest::Destroy() {
if (!vcm_)
return;

vcm_->StopCapture();
vcm_->DeRegisterCaptureDataCallback();
// Release reference to VCM.
vcm_ = nullptr;
}

void VcmCapturerTest::OnFrame(const webrtc::VideoFrame& frame) {
static auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
static size_t cnt = 0;

RTC_LOG(LS_INFO) << "OnFrame";
VideoCapturerTest::OnFrame(frame);

cnt++;
auto timestamp_curr = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
if(timestamp_curr - timestamp > 1000) {
RTC_LOG(LS_INFO) << "FPS: " << cnt;
cnt = 0;
timestamp = timestamp_curr;
}
}

} // namespace webrtc_demo
39 changes: 39 additions & 0 deletions examples/video_capture/vcm_capturer_test.h
@@ -0,0 +1,39 @@
#ifndef EXAMPLES_VIDEO_CAPTURE_VCM_CAPTURER_TEST_H_
#define EXAMPLES_VIDEO_CAPTURE_VCM_CAPTURER_TEST_H_

#include <memory>

#include "modules/video_capture/video_capture.h"
#include "examples/video_capture/video_capturer_test.h"

namespace webrtc_demo {

class VcmCapturerTest : public VideoCapturerTest,
public rtc::VideoSinkInterface<webrtc::VideoFrame> {
public:
static VcmCapturerTest* Create(size_t width,
size_t height,
size_t target_fps,
size_t capture_device_index);

virtual ~VcmCapturerTest();

void OnFrame(const webrtc::VideoFrame& frame) override;

private:
VcmCapturerTest();

bool Init(size_t width,
size_t height,
size_t target_fps,
size_t capture_device_index);

void Destroy();

rtc::scoped_refptr<webrtc::VideoCaptureModule> vcm_;
webrtc::VideoCaptureCapability capability_;
};

} // namespace webrtc_demo

#endif // EXAMPLES_VIDEO_CAPTURE_VCM_CAPTURER_TEST_H_
45 changes: 45 additions & 0 deletions examples/video_capture/video_capture_demo.cc
@@ -0,0 +1,45 @@
#include "modules/video_capture/video_capture_factory.h"
#include "rtc_base/logging.h"
#include "examples/video_capture/vcm_capturer_test.h"
#include "test/video_renderer.h"

#include <iostream>
#include <thread>

int main() {
const size_t kWidth = 1920;
const size_t kHeight = 1080;
const size_t kFps = 30;

std::unique_ptr<webrtc_demo::VcmCapturerTest> capturer;
std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> info(
webrtc::VideoCaptureFactory::CreateDeviceInfo());
std::unique_ptr<webrtc::test::VideoRenderer> renderer;

if (!info) {
RTC_LOG(LERROR) << "CreateDeviceInfo failed";
return -1;
}
int num_devices = info->NumberOfDevices();
for (int i = 0; i < num_devices; ++i) {
capturer.reset(
webrtc_demo::VcmCapturerTest::Create(kWidth, kHeight, kFps, i));
if (capturer) {
break;
}
}

if (!capturer) {
RTC_LOG(LERROR) << "Cannot found available video device";
return -1;
}

renderer.reset(webrtc::test::VideoRenderer::Create("Camera", kWidth, kHeight));
capturer->AddOrUpdateSink(renderer.get(), rtc::VideoSinkWants());

std::this_thread::sleep_for(std::chrono::seconds(30));
capturer->RemoveSink(renderer.get());

RTC_LOG(WARNING) << "Demo exit";
return 0;
}
84 changes: 84 additions & 0 deletions examples/video_capture/video_capturer_test.cc
@@ -0,0 +1,84 @@
#include "examples/video_capture/video_capturer_test.h"

#include "api/video/i420_buffer.h"
#include "api/video/video_rotation.h"
#include "rtc_base/logging.h"

namespace webrtc_demo {

VideoCapturerTest::~VideoCapturerTest() = default;

void VideoCapturerTest::OnFrame(const webrtc::VideoFrame& original_frame) {
int cropped_width = 0;
int cropped_height = 0;
int out_width = 0;
int out_height = 0;

webrtc::VideoFrame frame = MaybePreprocess(original_frame);

if (!video_adapter_.AdaptFrameResolution(
frame.width(), frame.height(), frame.timestamp_us() * 1000,
&cropped_width, &cropped_height, &out_width, &out_height)) {
// Drop frame in order to respect frame rate constraint.
return;
}

if (out_height != frame.height() || out_width != frame.width()) {
// Video adapter has requested a down-scale. Allocate a new buffer and
// return scaled version.
// For simplicity, only scale here without cropping.
rtc::scoped_refptr<webrtc::I420Buffer> scaled_buffer =
webrtc::I420Buffer::Create(out_width, out_height);
scaled_buffer->ScaleFrom(*frame.video_frame_buffer()->ToI420());
webrtc::VideoFrame::Builder new_frame_builder =
webrtc::VideoFrame::Builder()
.set_video_frame_buffer(scaled_buffer)
.set_rotation(webrtc::kVideoRotation_0)
.set_timestamp_us(frame.timestamp_us())
.set_id(frame.id());
;
if (frame.has_update_rect()) {
webrtc::VideoFrame::UpdateRect new_rect =
frame.update_rect().ScaleWithFrame(frame.width(), frame.height(), 0,
0, frame.width(), frame.height(),
out_width, out_height);
new_frame_builder.set_update_rect(new_rect);
}
broadcaster_.OnFrame(new_frame_builder.build());
} else {
// No adaptations needed, just return the frame as is.
broadcaster_.OnFrame(frame);
}
}

rtc::VideoSinkWants VideoCapturerTest::GetSinkWants() {
return broadcaster_.wants();
}

void VideoCapturerTest::AddOrUpdateSink(
rtc::VideoSinkInterface<webrtc::VideoFrame>* sink,
const rtc::VideoSinkWants& wants) {
broadcaster_.AddOrUpdateSink(sink, wants);
UpdateVideoAdapter();
}

void VideoCapturerTest::RemoveSink(
rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
broadcaster_.RemoveSink(sink);
UpdateVideoAdapter();
}

void VideoCapturerTest::UpdateVideoAdapter() {
video_adapter_.OnSinkWants(broadcaster_.wants());
}

webrtc::VideoFrame VideoCapturerTest::MaybePreprocess(
const webrtc::VideoFrame& frame) {
std::lock_guard<std::mutex> lock(mutex_);
if (preprocessor_ != nullptr) {
return preprocessor_->Preprocess(frame);
} else {
return frame;
}
}
} // namespace webrtc_demo

0 comments on commit 4965451

Please sign in to comment.