Switch the sample client back to render the videos in the main window
instead of two popup windows. This also demonstrates one way of implementing the VideoRenderer interface. Review URL: http://webrtc-codereview.appspot.com/51004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@143 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@ -71,8 +71,6 @@ bool Conductor::InitializePeerConnection() {
|
|||||||
|
|
||||||
void Conductor::DeletePeerConnection() {
|
void Conductor::DeletePeerConnection() {
|
||||||
peer_connection_.reset();
|
peer_connection_.reset();
|
||||||
local_renderer_.reset();
|
|
||||||
remote_renderer_.reset();
|
|
||||||
handshake_ = NONE;
|
handshake_ = NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,14 +80,7 @@ void Conductor::StartCaptureDevice() {
|
|||||||
main_wnd_->SwitchToStreamingUI();
|
main_wnd_->SwitchToStreamingUI();
|
||||||
|
|
||||||
if (peer_connection_->SetVideoCapture("")) {
|
if (peer_connection_->SetVideoCapture("")) {
|
||||||
if (!local_renderer_.get()) {
|
peer_connection_->SetLocalVideoRenderer(main_wnd_->local_renderer());
|
||||||
// The window will be resized according to the stream properties
|
|
||||||
// when streaming starts.
|
|
||||||
local_renderer_.reset(
|
|
||||||
cricket::VideoRendererFactory::CreateGuiVideoRenderer(100, 100));
|
|
||||||
}
|
|
||||||
if (local_renderer_.get())
|
|
||||||
peer_connection_->SetLocalVideoRenderer(local_renderer_.get());
|
|
||||||
} else {
|
} else {
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
@ -139,13 +130,8 @@ void Conductor::OnAddStream(const std::string& stream_id, int channel_id,
|
|||||||
video_channel_ = channel_id;
|
video_channel_ = channel_id;
|
||||||
waiting_for_video_ = false;
|
waiting_for_video_ = false;
|
||||||
LOG(INFO) << "Setting video renderer for channel: " << channel_id;
|
LOG(INFO) << "Setting video renderer for channel: " << channel_id;
|
||||||
if (!remote_renderer_.get()) {
|
|
||||||
// The window size will be automatically corrected.
|
|
||||||
remote_renderer_.reset(
|
|
||||||
cricket::VideoRendererFactory::CreateGuiVideoRenderer(100, 100));
|
|
||||||
}
|
|
||||||
bool ok = peer_connection_->SetVideoRenderer(stream_id,
|
bool ok = peer_connection_->SetVideoRenderer(stream_id,
|
||||||
remote_renderer_.get());
|
main_wnd_->remote_renderer());
|
||||||
ASSERT(ok);
|
ASSERT(ok);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(audio_channel_ == -1);
|
ASSERT(audio_channel_ == -1);
|
||||||
|
@ -115,8 +115,6 @@ class Conductor
|
|||||||
MainWnd* main_wnd_;
|
MainWnd* main_wnd_;
|
||||||
int video_channel_;
|
int video_channel_;
|
||||||
int audio_channel_;
|
int audio_channel_;
|
||||||
talk_base::scoped_ptr<cricket::VideoRenderer> local_renderer_;
|
|
||||||
talk_base::scoped_ptr<cricket::VideoRenderer> remote_renderer_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PEERCONNECTION_SAMPLES_CLIENT_CONDUCTOR_H_
|
#endif // PEERCONNECTION_SAMPLES_CLIENT_CONDUCTOR_H_
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "peerconnection/samples/client/main_wnd.h"
|
#include "peerconnection/samples/client/main_wnd.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "talk/base/common.h"
|
#include "talk/base/common.h"
|
||||||
#include "talk/base/logging.h"
|
#include "talk/base/logging.h"
|
||||||
|
|
||||||
@ -145,6 +147,7 @@ void MainWnd::SwitchToPeerList(const Peers& peers) {
|
|||||||
|
|
||||||
ui_ = LIST_PEERS;
|
ui_ = LIST_PEERS;
|
||||||
LayoutPeerListUI(true);
|
LayoutPeerListUI(true);
|
||||||
|
::SetFocus(listbox_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWnd::SwitchToStreamingUI() {
|
void MainWnd::SwitchToStreamingUI() {
|
||||||
@ -159,9 +162,64 @@ void MainWnd::OnPaint() {
|
|||||||
|
|
||||||
RECT rc;
|
RECT rc;
|
||||||
::GetClientRect(handle(), &rc);
|
::GetClientRect(handle(), &rc);
|
||||||
HBRUSH brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
|
|
||||||
::FillRect(ps.hdc, &rc, brush);
|
if (ui_ == STREAMING && remote_video_.get() && local_video_.get()) {
|
||||||
::DeleteObject(brush);
|
const BITMAPINFO& bmi = remote_video_->bmi();
|
||||||
|
long height = abs(bmi.bmiHeader.biHeight);
|
||||||
|
long width = bmi.bmiHeader.biWidth;
|
||||||
|
|
||||||
|
HDC dc_mem = ::CreateCompatibleDC(ps.hdc);
|
||||||
|
|
||||||
|
// Set the map mode so that the ratio will be maintained for us.
|
||||||
|
HDC all_dc[] = { ps.hdc, dc_mem };
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(all_dc); ++i) {
|
||||||
|
SetMapMode(all_dc[i], MM_ISOTROPIC);
|
||||||
|
SetWindowExtEx(all_dc[i], width, height, NULL);
|
||||||
|
SetViewportExtEx(all_dc[i], rc.right, rc.bottom, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
HBITMAP bmp_mem = ::CreateCompatibleBitmap(ps.hdc, rc.right, rc.bottom);
|
||||||
|
HGDIOBJ bmp_old = ::SelectObject(dc_mem, bmp_mem);
|
||||||
|
HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
|
||||||
|
::FillRect(dc_mem, &rc, brush);
|
||||||
|
::DeleteObject(brush);
|
||||||
|
|
||||||
|
POINT logical_area = { rc.right, rc.bottom };
|
||||||
|
DPtoLP(ps.hdc, &logical_area, 1);
|
||||||
|
|
||||||
|
const uint8* image = remote_video_->image();
|
||||||
|
int max_unit = std::max(width, height);
|
||||||
|
int x = (logical_area.x / 2) - (width / 2);
|
||||||
|
int y = (logical_area.y / 2) - (height / 2);
|
||||||
|
|
||||||
|
StretchDIBits(dc_mem, x, y, width, height,
|
||||||
|
0, 0, width, height, image, &bmi, DIB_RGB_COLORS, SRCCOPY);
|
||||||
|
|
||||||
|
if ((rc.right - rc.left) > 200 && (rc.bottom - rc.top) > 200) {
|
||||||
|
const BITMAPINFO& bmi = local_video_->bmi();
|
||||||
|
image = local_video_->image();
|
||||||
|
long thumb_width = bmi.bmiHeader.biWidth / 4;
|
||||||
|
long thumb_height = abs(bmi.bmiHeader.biHeight) / 4;
|
||||||
|
StretchDIBits(dc_mem,
|
||||||
|
logical_area.x - thumb_width - 10,
|
||||||
|
logical_area.y - thumb_height - 10,
|
||||||
|
thumb_width, thumb_height,
|
||||||
|
0, 0, bmi.bmiHeader.biWidth, -bmi.bmiHeader.biHeight,
|
||||||
|
image, &bmi, DIB_RGB_COLORS, SRCCOPY);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitBlt(ps.hdc, 0, 0, logical_area.x, logical_area.y,
|
||||||
|
dc_mem, 0, 0, SRCCOPY);
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
|
::SelectObject(dc_mem, bmp_old);
|
||||||
|
::DeleteObject(bmp_mem);
|
||||||
|
::DeleteDC(dc_mem);
|
||||||
|
} else {
|
||||||
|
HBRUSH brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
|
||||||
|
::FillRect(ps.hdc, &rc, brush);
|
||||||
|
::DeleteObject(brush);
|
||||||
|
}
|
||||||
|
|
||||||
::EndPaint(handle(), &ps);
|
::EndPaint(handle(), &ps);
|
||||||
}
|
}
|
||||||
@ -193,6 +251,11 @@ void MainWnd::OnDefaultAction() {
|
|||||||
|
|
||||||
bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
|
bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
|
case WM_CREATE:
|
||||||
|
remote_video_.reset(new VideoRenderer(handle(), 1, 1));
|
||||||
|
local_video_.reset(new VideoRenderer(handle(), 1, 1));
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_ERASEBKGND:
|
case WM_ERASEBKGND:
|
||||||
*result = TRUE;
|
*result = TRUE;
|
||||||
return true;
|
return true;
|
||||||
@ -202,6 +265,8 @@ bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
|
|||||||
case WM_SETFOCUS:
|
case WM_SETFOCUS:
|
||||||
if (ui_ == CONNECT_TO_SERVER) {
|
if (ui_ == CONNECT_TO_SERVER) {
|
||||||
SetFocus(edit1_);
|
SetFocus(edit1_);
|
||||||
|
} else if (ui_ == LIST_PEERS) {
|
||||||
|
SetFocus(listbox_);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case WM_SIZE:
|
case WM_SIZE:
|
||||||
@ -224,6 +289,12 @@ bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
case VIDEO_RENDERER_MESSAGE: {
|
||||||
|
VideoRenderer* renderer = reinterpret_cast<VideoRenderer*>(lp);
|
||||||
|
const MSG* msg_ptr = reinterpret_cast<const MSG*>(wp);
|
||||||
|
renderer->OnMessage(*msg_ptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -394,3 +465,78 @@ void MainWnd::HandleTabbing() {
|
|||||||
} while (true);
|
} while (true);
|
||||||
::SetFocus(next);
|
::SetFocus(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MainWnd::VideoRenderer
|
||||||
|
//
|
||||||
|
|
||||||
|
MainWnd::VideoRenderer::VideoRenderer(HWND wnd, int width, int height)
|
||||||
|
: wnd_(wnd) {
|
||||||
|
ZeroMemory(&bmi_, sizeof(bmi_));
|
||||||
|
bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
bmi_.bmiHeader.biPlanes = 1;
|
||||||
|
bmi_.bmiHeader.biBitCount = 32;
|
||||||
|
bmi_.bmiHeader.biCompression = BI_RGB;
|
||||||
|
bmi_.bmiHeader.biWidth = width;
|
||||||
|
bmi_.bmiHeader.biHeight = -height;
|
||||||
|
bmi_.bmiHeader.biSizeImage = width * height *
|
||||||
|
(bmi_.bmiHeader.biBitCount >> 3);
|
||||||
|
image_.reset(new uint8[bmi_.bmiHeader.biSizeImage]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWnd::VideoRenderer::~VideoRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWnd::VideoRenderer::SetSize(int width, int height, int reserved) {
|
||||||
|
if (width != bmi_.bmiHeader.biWidth ||
|
||||||
|
height != -bmi_.bmiHeader.biHeight) {
|
||||||
|
// Update the bitmap info and image buffer.
|
||||||
|
// To avoid touching buffers from different threads, we always
|
||||||
|
// marshal messages through the main window's thread.
|
||||||
|
MSG msg = {0};
|
||||||
|
msg.message = WM_SIZE;
|
||||||
|
msg.lParam = width;
|
||||||
|
msg.wParam = height;
|
||||||
|
::SendMessage(wnd_, VIDEO_RENDERER_MESSAGE,
|
||||||
|
reinterpret_cast<WPARAM>(&msg),
|
||||||
|
reinterpret_cast<LPARAM>(this));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWnd::VideoRenderer::RenderFrame(const cricket::VideoFrame* frame) {
|
||||||
|
if (!frame)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MSG msg = {0};
|
||||||
|
msg.message = WM_PAINT;
|
||||||
|
msg.lParam = reinterpret_cast<LPARAM>(frame);
|
||||||
|
::SendMessage(wnd_, VIDEO_RENDERER_MESSAGE,
|
||||||
|
reinterpret_cast<WPARAM>(&msg),
|
||||||
|
reinterpret_cast<LPARAM>(this));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWnd::VideoRenderer::OnMessage(const MSG& msg) {
|
||||||
|
switch (msg.message) {
|
||||||
|
case WM_SIZE:
|
||||||
|
bmi_.bmiHeader.biWidth = static_cast<int>(msg.lParam);
|
||||||
|
bmi_.bmiHeader.biHeight = -static_cast<int>(msg.wParam);
|
||||||
|
bmi_.bmiHeader.biSizeImage = bmi_.bmiHeader.biWidth *
|
||||||
|
static_cast<int>(msg.wParam) *
|
||||||
|
(bmi_.bmiHeader.biBitCount >> 3);
|
||||||
|
image_.reset(new uint8[bmi_.bmiHeader.biSizeImage]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_PAINT: {
|
||||||
|
const cricket::VideoFrame* frame =
|
||||||
|
reinterpret_cast<const cricket::VideoFrame*>(msg.lParam);
|
||||||
|
frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB, image_.get(),
|
||||||
|
bmi_.bmiHeader.biSizeImage,
|
||||||
|
bmi_.bmiHeader.biWidth *
|
||||||
|
(bmi_.bmiHeader.biBitCount >> 3));
|
||||||
|
InvalidateRect(wnd_, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include "peerconnection/samples/client/peer_connection_client.h"
|
#include "peerconnection/samples/client/peer_connection_client.h"
|
||||||
#include "talk/base/win32.h"
|
#include "talk/base/win32.h"
|
||||||
|
#include "talk/session/phone/mediachannel.h"
|
||||||
|
#include "talk/session/phone/videocommon.h"
|
||||||
|
|
||||||
class MainWndCallback {
|
class MainWndCallback {
|
||||||
public:
|
public:
|
||||||
@ -38,6 +40,10 @@ class MainWnd {
|
|||||||
STREAMING,
|
STREAMING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum WindowMessages {
|
||||||
|
VIDEO_RENDERER_MESSAGE = WM_APP + 1,
|
||||||
|
};
|
||||||
|
|
||||||
MainWnd();
|
MainWnd();
|
||||||
~MainWnd();
|
~MainWnd();
|
||||||
|
|
||||||
@ -56,6 +62,40 @@ class MainWnd {
|
|||||||
HWND handle() const { return wnd_; }
|
HWND handle() const { return wnd_; }
|
||||||
UI current_ui() const { return ui_; }
|
UI current_ui() const { return ui_; }
|
||||||
|
|
||||||
|
cricket::VideoRenderer* local_renderer() const {
|
||||||
|
return local_video_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
cricket::VideoRenderer* remote_renderer() const {
|
||||||
|
return remote_video_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoRenderer : public cricket::VideoRenderer {
|
||||||
|
public:
|
||||||
|
VideoRenderer(HWND wnd, int width, int height);
|
||||||
|
virtual ~VideoRenderer();
|
||||||
|
|
||||||
|
virtual bool SetSize(int width, int height, int reserved);
|
||||||
|
|
||||||
|
// Called when a new frame is available for display.
|
||||||
|
virtual bool RenderFrame(const cricket::VideoFrame* frame);
|
||||||
|
|
||||||
|
void OnMessage(const MSG& msg);
|
||||||
|
|
||||||
|
const BITMAPINFO& bmi() const { return bmi_; }
|
||||||
|
const uint8* image() const { return image_.get(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum {
|
||||||
|
SET_SIZE,
|
||||||
|
RENDER_FRAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
HWND wnd_;
|
||||||
|
BITMAPINFO bmi_;
|
||||||
|
talk_base::scoped_array<uint8> image_;
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum ChildWindowID {
|
enum ChildWindowID {
|
||||||
EDIT_ID = 1,
|
EDIT_ID = 1,
|
||||||
@ -85,6 +125,8 @@ class MainWnd {
|
|||||||
void HandleTabbing();
|
void HandleTabbing();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
talk_base::scoped_ptr<VideoRenderer> remote_video_;
|
||||||
|
talk_base::scoped_ptr<VideoRenderer> local_video_;
|
||||||
UI ui_;
|
UI ui_;
|
||||||
HWND wnd_;
|
HWND wnd_;
|
||||||
HWND edit1_;
|
HWND edit1_;
|
||||||
|
Reference in New Issue
Block a user