Add Microsoft DirectDraw screencast producer

This commit is contained in:
bossiel 2015-05-20 20:46:33 +00:00
parent 2ec0624c6b
commit 79ff1fe75d
7 changed files with 866 additions and 5 deletions

View File

@ -0,0 +1,36 @@
/* Copyright (C) 2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*/
#ifndef TINYDAV_PRODUCER_SCREENCAST_D3D9_H
#define TINYDAV_PRODUCER_SCREENCAST_D3D9_H
#include "tinydav_config.h"
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT
#include "tinymedia/tmedia_producer.h"
TDAV_BEGIN_DECLS
TINYDAV_GEXTERN const tmedia_producer_plugin_def_t *tdav_producer_screencast_d3d9_plugin_def_t;
TDAV_END_DECLS
#endif /* TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT */
#endif /* TINYDAV_PRODUCER_SCREENCAST_D3D9_H */

View File

@ -0,0 +1,37 @@
/* Copyright (C) 2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*/
#ifndef TINYDAV_PRODUCER_SCREENCAST_DDRAW_H
#define TINYDAV_PRODUCER_SCREENCAST_DDRAW_H
#include "tinydav_config.h"
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT
#include "tinymedia/tmedia_producer.h"
TDAV_BEGIN_DECLS
tsk_bool_t tdav_producer_screencast_ddraw_plugin_is_supported();
TINYDAV_GEXTERN const tmedia_producer_plugin_def_t *tdav_producer_screencast_ddraw_plugin_def_t;
TDAV_END_DECLS
#endif /* TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT */
#endif /* TINYDAV_PRODUCER_SCREENCAST_DDRAW_H */

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Mamadou DIOP.
/* Copyright (C) 2014-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*

View File

@ -114,6 +114,7 @@ static const tsk_size_t __codec_plugins_all_count = sizeof(__codec_plugins_all)/
#include "tinydav/audio/waveapi/tdav_producer_waveapi.h"
#include "tinydav/audio/directsound/tdav_producer_dsound.h"
#include "tinydav/video/gdi/tdav_producer_screencast_gdi.h"
#include "tinydav/video/directx/tdav_producer_screencast_ddraw.h"
#include "tinydav/video/v4linux/tdav_producer_video_v4l2.h"
#include "tinydav/audio/coreaudio/tdav_producer_audioqueue.h"
#include "tinydav/audio/coreaudio/tdav_producer_audiounit.h"
@ -411,6 +412,11 @@ int tdav_init()
#elif HAVE_WASAPI // WASAPI
tmedia_producer_plugin_register(tdav_producer_wasapi_plugin_def_t);
#endif
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT // Windows DirectDraw (DirectX)
if (tdav_producer_screencast_ddraw_plugin_is_supported()) {
tmedia_producer_plugin_register(tdav_producer_screencast_ddraw_plugin_def_t);
}
#endif
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT // Windows GDI
tmedia_producer_plugin_register(tdav_producer_screencast_gdi_plugin_def_t);
#endif
@ -680,6 +686,9 @@ int tdav_deinit()
#if HAVE_WASAPI // WASAPI
tmedia_producer_plugin_unregister(tdav_producer_wasapi_plugin_def_t);
#endif
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT // Windows DirectDraw (DirectX)
tmedia_producer_plugin_unregister(tdav_producer_screencast_ddraw_plugin_def_t);
#endif
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT // Windows GDI
tmedia_producer_plugin_unregister(tdav_producer_screencast_gdi_plugin_def_t);
#endif

View File

@ -0,0 +1,179 @@
/* Copyright (C) 2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*/
#include "tinydav/video/directx/tdav_producer_screencast_d3d9.h"
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT
#include <windows.h>
#if TDAV_UNDER_WINDOWS_CE
# include <D3dm.h>
#else
# include <d3d9.h>
#endif
#ifdef _MSC_VER
# if TDAV_UNDER_WINDOWS_CE
# pragma comment(lib, "D3dm")
# pragma comment(lib, "D3dmguid")
# else
# pragma comment(lib, "d3d9")
# endif
#endif
#include "tsk_thread.h"
#include "tsk_memory.h"
#include "tsk_safeobj.h"
#include "tsk_timer.h"
#include "tsk_time.h"
#include "tsk_string.h"
#include "tsk_debug.h"
#define D3D9_DEBUG_INFO(FMT, ...) TSK_DEBUG_INFO("[D3D9 Producer] " FMT, ##__VA_ARGS__)
#define D3D9_DEBUG_WARN(FMT, ...) TSK_DEBUG_WARN("[D3D9 Producer] " FMT, ##__VA_ARGS__)
#define D3D9_DEBUG_ERROR(FMT, ...) TSK_DEBUG_ERROR("[D3D9 Producer] " FMT, ##__VA_ARGS__)
#define D3D9_DEBUG_FATAL(FMT, ...) TSK_DEBUG_FATAL("[D3D9 Producer] " FMT, ##__VA_ARGS__)
typedef struct tdav_producer_screencast_d3d9_s
{
TMEDIA_DECLARE_PRODUCER;
HWND hwnd_preview;
HWND hwnd_src;
tsk_thread_handle_t* tid[1];
void* p_buff_src; // must use VirtualAlloc()
tsk_size_t n_buff_src;
void* p_buff_neg; // must use VirtualAlloc()
tsk_size_t n_buff_neg;
tsk_bool_t b_started;
tsk_bool_t b_paused;
tsk_bool_t b_muted;
RECT rcScreen;
TSK_DECLARE_SAFEOBJ;
}
tdav_producer_screencast_d3d9_t;
/* ============ Media Producer Interface ================= */
static int _tdav_producer_screencast_d3d9_set(tmedia_producer_t *p_self, const tmedia_param_t* pc_param)
{
D3D9_DEBUG_ERROR("Not implemented");
return -1;
}
static int _tdav_producer_screencast_d3d9_prepare(tmedia_producer_t* p_self, const tmedia_codec_t* pc_codec)
{
D3D9_DEBUG_ERROR("Not implemented");
return -1;
}
static int _tdav_producer_screencast_d3d9_start(tmedia_producer_t* p_self)
{
D3D9_DEBUG_ERROR("Not implemented");
return -1;
}
static int _tdav_producer_screencast_d3d9_pause(tmedia_producer_t* p_self)
{
D3D9_DEBUG_ERROR("Not implemented");
return -1;
}
static int _tdav_producer_screencast_d3d9_stop(tmedia_producer_t* p_self)
{
D3D9_DEBUG_ERROR("Not implemented");
return -1;
}
//
// d3d9 screencast producer object definition
//
/* constructor */
static tsk_object_t* _tdav_producer_screencast_d3d9_ctor(tsk_object_t *self, va_list * app)
{
tdav_producer_screencast_d3d9_t *p_d3d9 = (tdav_producer_screencast_d3d9_t *)self;
if (p_d3d9) {
/* init base */
tmedia_producer_init(TMEDIA_PRODUCER(p_d3d9));
TMEDIA_PRODUCER(p_d3d9)->video.chroma = tmedia_chroma_bgr24; // RGB24 on x86 (little endians) stored as BGR24
/* init self with default values*/
TMEDIA_PRODUCER(p_d3d9)->video.fps = 15;
TMEDIA_PRODUCER(p_d3d9)->video.width = 352;
TMEDIA_PRODUCER(p_d3d9)->video.height = 288;
tsk_safeobj_init(p_d3d9);
}
return self;
}
/* destructor */
static tsk_object_t* _tdav_producer_screencast_d3d9_dtor(tsk_object_t * self)
{
tdav_producer_screencast_d3d9_t *p_d3d9 = (tdav_producer_screencast_d3d9_t *)self;
if (p_d3d9) {
/* stop */
if (p_d3d9->b_started) {
_tdav_producer_screencast_d3d9_stop((tmedia_producer_t*)p_d3d9);
}
/* deinit base */
tmedia_producer_deinit(TMEDIA_PRODUCER(p_d3d9));
/* deinit self */
if (p_d3d9->p_buff_neg) {
VirtualFree(p_d3d9->p_buff_neg, 0, MEM_RELEASE);
p_d3d9->p_buff_neg = NULL;
}
if (p_d3d9->p_buff_src) {
VirtualFree(p_d3d9->p_buff_src, 0, MEM_RELEASE);
p_d3d9->p_buff_src = NULL;
}
tsk_safeobj_deinit(p_d3d9);
TSK_DEBUG_INFO("*** d3d9 Screencast producer destroyed ***");
}
return self;
}
/* object definition */
static const tsk_object_def_t tdav_producer_screencast_d3d9_def_s =
{
sizeof(tdav_producer_screencast_d3d9_t),
_tdav_producer_screencast_d3d9_ctor,
_tdav_producer_screencast_d3d9_dtor,
tsk_null,
};
/* plugin definition*/
static const tmedia_producer_plugin_def_t tdav_producer_screencast_d3d9_plugin_def_s =
{
&tdav_producer_screencast_d3d9_def_s,
tmedia_bfcp_video,
"Microsoft Direct3D screencast producer",
_tdav_producer_screencast_d3d9_set,
_tdav_producer_screencast_d3d9_prepare,
_tdav_producer_screencast_d3d9_start,
_tdav_producer_screencast_d3d9_pause,
_tdav_producer_screencast_d3d9_stop
};
const tmedia_producer_plugin_def_t *tdav_producer_screencast_d3d9_plugin_def_t = &tdav_producer_screencast_d3d9_plugin_def_s;
#endif /* TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT */

View File

@ -0,0 +1,598 @@
/* Copyright (C) 2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*/
#include "tinydav/video/directx/tdav_producer_screencast_ddraw.h"
#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT
#include <windows.h>
#include <ddraw.h>
#include "tsk_thread.h"
#include "tsk_memory.h"
#include "tsk_safeobj.h"
#include "tsk_timer.h"
#include "tsk_time.h"
#include "tsk_string.h"
#include "tsk_debug.h"
#ifdef _MSC_VER
# pragma comment(lib, "Ddraw")
#endif
#if !defined(DDRAW_HIGH_PRIO_MEMCPY)
# define DDRAW_HIGH_PRIO_MEMCPY 0
#endif /* DDRAW_HIGH_PRIO_MEMCPY */
#if !defined(DDRAW_PREVIEW)
# if TDAV_UNDER_WINDOWS_CE && (BUILD_TYPE_GE || SIN_CITY)
# define DDRAW_PREVIEW 0 // Do not waste time displaying the preview on "WEC7 + (GE | SINCITY)"
# else
# define DDRAW_PREVIEW 1
# endif
#endif
#define DDRAW_DEBUG_INFO(FMT, ...) TSK_DEBUG_INFO("[DDRAW Producer] " FMT, ##__VA_ARGS__)
#define DDRAW_DEBUG_WARN(FMT, ...) TSK_DEBUG_WARN("[DDRAW Producer] " FMT, ##__VA_ARGS__)
#define DDRAW_DEBUG_ERROR(FMT, ...) TSK_DEBUG_ERROR("[DDRAW Producer] " FMT, ##__VA_ARGS__)
#define DDRAW_DEBUG_FATAL(FMT, ...) TSK_DEBUG_FATAL("[DDRAW Producer] " FMT, ##__VA_ARGS__)
#define DDRAW_SAFE_RELEASE(pp) if ((pp) && *(pp)) (*(pp))->Release(), *(pp) = NULL
#define DDRAW_CHECK_HR(x) { HRESULT __hr__ = (x); if (FAILED(__hr__)) { DDRAW_DEBUG_ERROR("Operation Failed (%08x)", __hr__); goto bail; } }
typedef struct tdav_producer_screencast_ddraw_s
{
TMEDIA_DECLARE_PRODUCER;
HWND hwnd_preview;
HWND hwnd_src;
#if DDRAW_PREVIEW
BITMAPINFO bi_preview;
#endif /* DDRAW_PREVIEW */
IDirectDraw* p_dd;
IDirectDrawSurface* p_surf_primary;
tsk_thread_handle_t* tid[1];
void* p_buff_neg; // must use VirtualAlloc()
tsk_size_t n_buff_neg;
tsk_size_t n_buff_rgb_bitscount;
tsk_bool_t b_started;
tsk_bool_t b_paused;
tsk_bool_t b_muted;
TSK_DECLARE_SAFEOBJ;
}
tdav_producer_screencast_ddraw_t;
static tmedia_chroma_t _tdav_producer_screencast_get_chroma(const DDPIXELFORMAT* pixelFormat);
static void* TSK_STDCALL _tdav_producer_screencast_record_thread(void *arg);
static int _tdav_producer_screencast_grab(tdav_producer_screencast_ddraw_t* p_self);
// public function used to check that we can use DDRAW plugin before loading it
tsk_bool_t tdav_producer_screencast_ddraw_plugin_is_supported()
{
static tsk_bool_t __checked = tsk_false; // static guard to avoid checking more than once
static tsk_bool_t __supported = tsk_false;
HRESULT hr = DD_OK;
DDSURFACEDESC ddsd;
DDPIXELFORMAT DDPixelFormat;
LPDIRECTDRAWSURFACE lpDDS = NULL;
LPDIRECTDRAW lpDD = NULL;
if (__checked) {
goto bail;
}
__checked = tsk_true;
DDRAW_CHECK_HR(hr = DirectDrawCreate(NULL, &lpDD, NULL));
DDRAW_CHECK_HR(hr = lpDD->SetCooperativeLevel(NULL, DDSCL_NORMAL));
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
DDRAW_CHECK_HR(hr = lpDD->CreateSurface(&ddsd, &lpDDS, NULL));
ZeroMemory(&DDPixelFormat, sizeof(DDPixelFormat));
DDPixelFormat.dwSize = sizeof(DDPixelFormat);
DDRAW_CHECK_HR(hr = lpDDS->GetPixelFormat(&DDPixelFormat));
DDRAW_DEBUG_INFO("dwRGBBitCount:%d, dwRBitMask:%x, dwGBitMask:%x, dwBBitMask:%x, dwRGBAlphaBitMask:%x",
DDPixelFormat.dwRGBBitCount, DDPixelFormat.dwRBitMask, DDPixelFormat.dwGBitMask, DDPixelFormat.dwBBitMask, DDPixelFormat.dwRGBAlphaBitMask);
if (_tdav_producer_screencast_get_chroma(&DDPixelFormat) == tmedia_chroma_none) {
DDRAW_CHECK_HR(hr = DDERR_INVALIDCAPS);
}
__supported = SUCCEEDED(hr);
bail:
DDRAW_SAFE_RELEASE(&lpDDS);
DDRAW_SAFE_RELEASE(&lpDD);
return __supported;
}
/* ============ Media Producer Interface ================= */
static int _tdav_producer_screencast_ddraw_set(tmedia_producer_t *p_self, const tmedia_param_t* pc_param)
{
int ret = 0;
tdav_producer_screencast_ddraw_t* p_ddraw = (tdav_producer_screencast_ddraw_t*)p_self;
if (!p_ddraw || !pc_param) {
DDRAW_DEBUG_ERROR("Invalid parameter");
return -1;
}
if (pc_param->value_type == tmedia_pvt_int64) {
if (tsk_striequals(pc_param->key, "local-hwnd") || tsk_striequals(pc_param->key, "preview-hwnd")) {
p_ddraw->hwnd_preview = (HWND)*((int64_t*)pc_param->value);
}
else if (tsk_striequals(pc_param->key, "src-hwnd")) {
p_ddraw->hwnd_src = (HWND)*((int64_t*)pc_param->value);
}
}
else if (pc_param->value_type == tmedia_pvt_int32) {
if (tsk_striequals(pc_param->key, "mute")) {
p_ddraw->b_muted = (TSK_TO_INT32((uint8_t*)pc_param->value) != 0);
}
}
return ret;
}
static int _tdav_producer_screencast_ddraw_prepare(tmedia_producer_t* p_self, const tmedia_codec_t* pc_codec)
{
tdav_producer_screencast_ddraw_t* p_ddraw = (tdav_producer_screencast_ddraw_t*)p_self;
int ret = 0;
HRESULT hr = DD_OK;
tsk_size_t n_buff_neg_new;
#if 0
DDPIXELFORMAT DDPixelFormat;
#endif
DDSURFACEDESC ddsd;
if (!p_ddraw || !pc_codec) {
DDRAW_DEBUG_ERROR("Invalid parameter");
DDRAW_CHECK_HR(hr = E_INVALIDARG);
}
tsk_safeobj_lock(p_ddraw);
// check support for DirectDraw again
if (!tdav_producer_screencast_ddraw_plugin_is_supported()) {
DDRAW_CHECK_HR(hr = E_FAIL);
}
TMEDIA_PRODUCER(p_ddraw)->video.fps = TMEDIA_CODEC_VIDEO(pc_codec)->out.fps;
TMEDIA_PRODUCER(p_ddraw)->video.width = TMEDIA_CODEC_VIDEO(pc_codec)->out.width;
TMEDIA_PRODUCER(p_ddraw)->video.height = TMEDIA_CODEC_VIDEO(pc_codec)->out.height;
// Hack the codec to avoid flipping
TMEDIA_CODEC_VIDEO(pc_codec)->out.flip = tsk_false;
DDRAW_DEBUG_INFO("Prepare with fps:%d, width:%d; height:%d", TMEDIA_PRODUCER(p_ddraw)->video.fps, TMEDIA_PRODUCER(p_ddraw)->video.width, TMEDIA_PRODUCER(p_ddraw)->video.height);
if (!p_ddraw->p_dd) {
DDRAW_CHECK_HR(hr = DirectDrawCreate(NULL, &p_ddraw->p_dd, NULL));
}
DDRAW_CHECK_HR(hr = p_ddraw->p_dd->SetCooperativeLevel(NULL, DDSCL_NORMAL));
if (!p_ddraw->p_surf_primary) {
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
DDRAW_CHECK_HR(hr = p_ddraw->p_dd->CreateSurface(&ddsd, &p_ddraw->p_surf_primary, NULL));
}
#if 0
ZeroMemory(&DDPixelFormat, sizeof(DDPixelFormat));
DDPixelFormat.dwSize = sizeof(DDPixelFormat);
DDRAW_CHECK_HR(hr = DDRAW_VTBL(p_ddraw->p_surf_primary)->GetPixelFormat(p_ddraw->p_surf_primary, &DDPixelFormat));
DDRAW_DEBUG_INFO("dwRGBBitCount:%d, dwRBitMask:%x, dwGBitMask:%x, dwBBitMask:%x, dwRGBAlphaBitMask:%x",
DDPixelFormat.dwRGBBitCount, DDPixelFormat.dwRBitMask, DDPixelFormat.dwGBitMask, DDPixelFormat.dwBBitMask, DDPixelFormat.dwRGBAlphaBitMask);
if ((TMEDIA_PRODUCER(p_ddraw)->video.chroma = _tdav_producer_screencast_get_chroma(&DDPixelFormat)) == tmedia_chroma_none) {
DDRAW_CHECK_HR(hr = DDERR_INVALIDCAPS);
}
#else
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PITCH | DDSD_PIXELFORMAT;
DDRAW_CHECK_HR(hr = p_ddraw->p_surf_primary->GetSurfaceDesc(&ddsd));
DDRAW_DEBUG_INFO("Prepare with neg. width:%d, height:%d, pitch=%ld", ddsd.dwWidth, ddsd.dwHeight, ddsd.lPitch);
TMEDIA_PRODUCER(p_ddraw)->video.width = ddsd.dwWidth;
TMEDIA_PRODUCER(p_ddraw)->video.height = ddsd.dwHeight;
p_ddraw->n_buff_rgb_bitscount = ddsd.ddpfPixelFormat.dwRGBBitCount;
DDRAW_DEBUG_INFO("Prepare with dwRGBBitCount:%d, dwRBitMask:%x, dwGBitMask:%x, dwBBitMask:%x, dwRGBAlphaBitMask:%x",
ddsd.ddpfPixelFormat.dwRGBBitCount, ddsd.ddpfPixelFormat.dwRBitMask, ddsd.ddpfPixelFormat.dwGBitMask, ddsd.ddpfPixelFormat.dwBBitMask, ddsd.ddpfPixelFormat.dwRGBAlphaBitMask);
if ((TMEDIA_PRODUCER(p_ddraw)->video.chroma = _tdav_producer_screencast_get_chroma(&ddsd.ddpfPixelFormat)) == tmedia_chroma_none) {
DDRAW_CHECK_HR(hr = DDERR_INVALIDCAPS);
}
#endif
n_buff_neg_new = (ddsd.dwWidth * ddsd.dwHeight * (ddsd.ddpfPixelFormat.dwRGBBitCount >> 3));
if (p_ddraw->n_buff_neg < n_buff_neg_new) {
if (p_ddraw->p_buff_neg) VirtualFree(p_ddraw->p_buff_neg, 0, MEM_RELEASE);
if (!(p_ddraw->p_buff_neg = VirtualAlloc(NULL, n_buff_neg_new, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))) {
p_ddraw->n_buff_neg = 0;
DDRAW_CHECK_HR(hr = DDERR_OUTOFMEMORY);
}
p_ddraw->n_buff_neg = n_buff_neg_new;
}
// BitmapInfo for preview
#if DDRAW_PREVIEW
ZeroMemory(&p_ddraw->bi_preview, sizeof(p_ddraw->bi_preview));
p_ddraw->bi_preview.bmiHeader.biSize = (DWORD)sizeof(BITMAPINFOHEADER);
p_ddraw->bi_preview.bmiHeader.biCompression = BI_RGB;
p_ddraw->bi_preview.bmiHeader.biPlanes = 1;
p_ddraw->bi_preview.bmiHeader.biWidth = ddsd.dwWidth;
p_ddraw->bi_preview.bmiHeader.biHeight = ddsd.dwHeight;
p_ddraw->bi_preview.bmiHeader.biBitCount = (WORD)ddsd.ddpfPixelFormat.dwRGBBitCount;
p_ddraw->bi_preview.bmiHeader.biSizeImage = (p_ddraw->bi_preview.bmiHeader.biWidth * p_ddraw->bi_preview.bmiHeader.biHeight * (p_ddraw->bi_preview.bmiHeader.biBitCount >> 3));
#endif /* DDRAW_PREVIEW */
bail:
tsk_safeobj_unlock(p_ddraw);
return SUCCEEDED(hr) ? 0 : -1;
}
static int _tdav_producer_screencast_ddraw_start(tmedia_producer_t* p_self)
{
tdav_producer_screencast_ddraw_t* p_ddraw = (tdav_producer_screencast_ddraw_t*)p_self;
int ret = 0;
if (!p_ddraw) {
DDRAW_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(p_ddraw);
p_ddraw->b_paused = tsk_false;
if (p_ddraw->b_started) {
DDRAW_DEBUG_INFO("Already started");
goto bail;
}
p_ddraw->b_started = tsk_true;
tsk_thread_create(&p_ddraw->tid[0], _tdav_producer_screencast_record_thread, p_ddraw);
#if DDRAW_HIGH_PRIO_MEMCPY
if (p_ddraw->tid[0]) {
tsk_thread_set_priority(p_ddraw->tid[0], TSK_THREAD_PRIORITY_TIME_CRITICAL);
}
#endif
bail:
if (ret) {
p_ddraw->b_started = tsk_false;
}
tsk_safeobj_unlock(p_ddraw);
return ret;
}
static int _tdav_producer_screencast_ddraw_pause(tmedia_producer_t* p_self)
{
tdav_producer_screencast_ddraw_t* p_ddraw = (tdav_producer_screencast_ddraw_t*)p_self;
if (!p_ddraw) {
DDRAW_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(p_ddraw);
p_ddraw->b_paused = tsk_true;
goto bail;
bail:
tsk_safeobj_unlock(p_ddraw);
return 0;
}
static int _tdav_producer_screencast_ddraw_stop(tmedia_producer_t* p_self)
{
tdav_producer_screencast_ddraw_t* p_ddraw = (tdav_producer_screencast_ddraw_t*)p_self;
if (!p_ddraw) {
DDRAW_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(p_ddraw);
if (!p_ddraw->b_started) {
DDRAW_DEBUG_INFO("Already stopped");
goto bail;
}
p_ddraw->b_started = tsk_false;
p_ddraw->b_paused = tsk_false;
// stop thread
if (p_ddraw->tid[0]) {
tsk_thread_join(&(p_ddraw->tid[0]));
}
bail:
tsk_safeobj_unlock(p_ddraw);
return 0;
}
static int _tdav_producer_screencast_grab(tdav_producer_screencast_ddraw_t* p_self)
{
int ret = 0;
HRESULT hr = S_OK;
DDSURFACEDESC ddsd;
DWORD nSizeWithoutPadding, nRowLengthInBytes;
tmedia_producer_t* p_base = TMEDIA_PRODUCER(p_self);
LPVOID lpBuffToSend;
if (!p_self) {
DDRAW_CHECK_HR(hr = E_INVALIDARG);
}
if (!p_self->b_started) {
#if defined(E_ILLEGAL_METHOD_CALL)
DDRAW_CHECK_HR(hr = E_ILLEGAL_METHOD_CALL);
#else
DDRAW_CHECK_HR(hr = E_FAIL);
#endif
}
if (p_self->p_surf_primary->IsLost() == DDERR_SURFACELOST) {
DDRAW_CHECK_HR(hr = p_self->p_surf_primary->Restore());
}
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PITCH | DDSD_PIXELFORMAT;
DDRAW_CHECK_HR(hr = p_self->p_surf_primary->Lock(NULL, &ddsd, DDLOCK_READONLY, NULL));
// make sure surface size and number of bits per pixel haven't changed
if (TMEDIA_PRODUCER(p_self)->video.width != ddsd.dwWidth || TMEDIA_PRODUCER(p_self)->video.height != ddsd.dwHeight || p_self->n_buff_rgb_bitscount != ddsd.ddpfPixelFormat.dwRGBBitCount) {
tsk_size_t n_buff_neg_new;
tmedia_chroma_t chroma_new;
DDRAW_DEBUG_WARN("surface has changed: width %d<>%d or height %d<>%d or rgb_bits_count %d<>%d",
p_base->video.width, ddsd.dwWidth,
p_base->video.height, ddsd.dwHeight,
p_self->n_buff_rgb_bitscount, ddsd.ddpfPixelFormat.dwRGBBitCount);
if ((chroma_new = _tdav_producer_screencast_get_chroma(&ddsd.ddpfPixelFormat)) == tmedia_chroma_none) {
DDRAW_CHECK_HR(hr = DDERR_INVALIDCAPS);
}
n_buff_neg_new = (ddsd.dwWidth * ddsd.dwHeight * (ddsd.ddpfPixelFormat.dwRGBBitCount >> 3));
if (p_self->n_buff_neg < n_buff_neg_new) {
if (p_self->p_buff_neg) VirtualFree(p_self->p_buff_neg, 0, MEM_RELEASE);
if (!(p_self->p_buff_neg = VirtualAlloc(NULL, n_buff_neg_new, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))) {
p_self->n_buff_neg = 0;
p_self->p_surf_primary->Unlock(NULL); // unlock before going to bail
DDRAW_CHECK_HR(hr = DDERR_OUTOFMEMORY);
}
p_self->n_buff_neg = n_buff_neg_new;
}
p_base->video.width = ddsd.dwWidth;
p_base->video.height = ddsd.dwHeight;
p_base->video.chroma = chroma_new;
p_self->n_buff_rgb_bitscount = ddsd.ddpfPixelFormat.dwRGBBitCount;
// preview
#if DDRAW_PREVIEW
p_self->bi_preview.bmiHeader.biWidth = ddsd.dwWidth;
p_self->bi_preview.bmiHeader.biHeight = ddsd.dwHeight;
p_self->bi_preview.bmiHeader.biBitCount = (WORD)ddsd.ddpfPixelFormat.dwRGBBitCount;
p_self->bi_preview.bmiHeader.biSizeImage = (p_self->bi_preview.bmiHeader.biWidth * p_self->bi_preview.bmiHeader.biHeight * (p_self->bi_preview.bmiHeader.biBitCount >> 3));
#endif /* DDRAW_PREVIEW */
}
nRowLengthInBytes = ddsd.dwWidth * (ddsd.ddpfPixelFormat.dwRGBBitCount >> 3);
nSizeWithoutPadding = ddsd.dwHeight * nRowLengthInBytes;
// init lpBuffToSend
if (ddsd.lPitch == nRowLengthInBytes) {
// no padding
lpBuffToSend = ddsd.lpSurface;
}
else {
// with padding
UINT8 *pSurfBuff = (UINT8 *)ddsd.lpSurface, *pNegBuff = (UINT8 *)p_self->p_buff_neg;
DWORD y;
for (y = 0; y < ddsd.dwHeight; ++y) {
CopyMemory(pNegBuff, pSurfBuff, nRowLengthInBytes);
pSurfBuff += ddsd.lPitch;
pNegBuff += nRowLengthInBytes;
}
lpBuffToSend = p_self->p_buff_neg;
}
// display preview
#if DDRAW_PREVIEW
if (p_self->hwnd_preview) {
HWND hWnd; // copy for thread-safeness
HDC hDC = GetDC((hWnd = p_self->hwnd_preview));
if (hDC) {
RECT rcPreview;
if (GetWindowRect(hWnd, &rcPreview)) {
LONG nPreviewWidth = (rcPreview.right - rcPreview.left);
LONG nPreviewHeight = (rcPreview.bottom - rcPreview.top);
StretchDIBits(
hDC,
0, 0, nPreviewWidth, nPreviewHeight,
0, 0, p_self->bi_preview.bmiHeader.biWidth, p_self->bi_preview.bmiHeader.biHeight,
lpBuffToSend,
&p_self->bi_preview,
DIB_RGB_COLORS,
SRCCOPY);
}
ReleaseDC(hWnd, hDC);
}
}
#endif /* DDRAW_PREVIEW */
// Encode, encrypt and send data
p_base->enc_cb.callback(p_base->enc_cb.callback_data, lpBuffToSend, nSizeWithoutPadding);
DDRAW_CHECK_HR(hr = p_self->p_surf_primary->Unlock(NULL));
bail:
if (hr == DDERR_SURFACELOST) {
/*hr = */p_self->p_surf_primary->Restore();
hr = S_OK;
}
return SUCCEEDED(hr) ? 0 : -1;
}
static tmedia_chroma_t _tdav_producer_screencast_get_chroma(const DDPIXELFORMAT* pixelFormat)
{
HRESULT hr = DD_OK;
if (pixelFormat->dwFlags != DDPF_RGB) {
DDRAW_DEBUG_ERROR("dwFlags(%d) != DDPF_RGB", pixelFormat->dwFlags);
DDRAW_CHECK_HR(hr = DDERR_INVALIDCAPS);
}
switch (pixelFormat->dwRGBBitCount) {
case 32: // RGB32
case 24: // RGB24
// pixels must be aligned for fast copy
if (pixelFormat->dwRBitMask != 0xff0000 || pixelFormat->dwGBitMask != 0xff00 || pixelFormat->dwBBitMask != 0xff || pixelFormat->dwRGBAlphaBitMask != 0) {
DDRAW_DEBUG_ERROR("Pixels not aligned");
}
return pixelFormat->dwRGBBitCount == 24 ? tmedia_chroma_bgr24 : tmedia_chroma_rgb32;
break;
case 16: // RGB565
// pixels must be aligned for fast copy
if (pixelFormat->dwRBitMask != 0xF800 || pixelFormat->dwGBitMask != 0x7E0 || pixelFormat->dwBBitMask != 0x1F) {
DDRAW_DEBUG_ERROR("Pixels not aligned");
}
return tmedia_chroma_rgb565le;
break;
default:
DDRAW_DEBUG_ERROR("dwRGBBitCount(%d) != 24 and 32", pixelFormat->dwRGBBitCount);
DDRAW_CHECK_HR(hr = DDERR_INVALIDCAPS);
break;
}
bail:
return tmedia_chroma_none;
}
static void* TSK_STDCALL _tdav_producer_screencast_record_thread(void *arg)
{
tdav_producer_screencast_ddraw_t* p_ddraw = (tdav_producer_screencast_ddraw_t*)arg;
int ret = 0;
// FPS manager
uint64_t TimeNow, TimeLastFrame = 0;
const uint64_t TimeFrameDuration = (1000 / TMEDIA_PRODUCER(p_ddraw)->video.fps);
DDRAW_DEBUG_INFO("Recorder thread -- START");
while (ret == 0 && p_ddraw->b_started) {
TimeNow = tsk_time_now();
if ((TimeNow - TimeLastFrame) > TimeFrameDuration) {
if (!p_ddraw->b_muted && !p_ddraw->b_paused) {
if (ret = _tdav_producer_screencast_grab(p_ddraw)) {
goto next;
}
}
TimeLastFrame = TimeNow;
}
else {
tsk_thread_sleep(1);
#if 0
DDRAW_DEBUG_INFO("Skip frame");
#endif
}
next:
;
}
DDRAW_DEBUG_INFO("Recorder thread -- STOP");
return tsk_null;
}
//
// ddraw screencast producer object definition
//
/* constructor */
static tsk_object_t* _tdav_producer_screencast_ddraw_ctor(tsk_object_t *self, va_list * app)
{
tdav_producer_screencast_ddraw_t *p_ddraw = (tdav_producer_screencast_ddraw_t *)self;
if (p_ddraw) {
/* init base */
tmedia_producer_init(TMEDIA_PRODUCER(p_ddraw));
TMEDIA_PRODUCER(p_ddraw)->video.chroma = tmedia_chroma_bgr24; // RGB24 on x86 (little endians) stored as BGR24
/* init self with default values*/
TMEDIA_PRODUCER(p_ddraw)->video.fps = 15;
TMEDIA_PRODUCER(p_ddraw)->video.width = 352;
TMEDIA_PRODUCER(p_ddraw)->video.height = 288;
tsk_safeobj_init(p_ddraw);
}
return self;
}
/* destructor */
static tsk_object_t* _tdav_producer_screencast_ddraw_dtor(tsk_object_t * self)
{
tdav_producer_screencast_ddraw_t *p_ddraw = (tdav_producer_screencast_ddraw_t *)self;
if (p_ddraw) {
/* stop */
if (p_ddraw->b_started) {
_tdav_producer_screencast_ddraw_stop((tmedia_producer_t*)p_ddraw);
}
/* deinit base */
tmedia_producer_deinit(TMEDIA_PRODUCER(p_ddraw));
/* deinit self */
if (p_ddraw->p_buff_neg) {
VirtualFree(p_ddraw->p_buff_neg, 0, MEM_RELEASE);
p_ddraw->p_buff_neg = NULL;
}
DDRAW_SAFE_RELEASE(&p_ddraw->p_surf_primary);
DDRAW_SAFE_RELEASE(&p_ddraw->p_dd);
tsk_safeobj_deinit(p_ddraw);
DDRAW_DEBUG_INFO("*** destroyed ***");
}
return self;
}
/* object definition */
static const tsk_object_def_t tdav_producer_screencast_ddraw_def_s =
{
sizeof(tdav_producer_screencast_ddraw_t),
_tdav_producer_screencast_ddraw_ctor,
_tdav_producer_screencast_ddraw_dtor,
tsk_null,
};
/* plugin definition*/
static const tmedia_producer_plugin_def_t tdav_producer_screencast_ddraw_plugin_def_s =
{
&tdav_producer_screencast_ddraw_def_s,
tmedia_bfcp_video,
"Microsoft DirectDraw screencast producer",
_tdav_producer_screencast_ddraw_set,
_tdav_producer_screencast_ddraw_prepare,
_tdav_producer_screencast_ddraw_start,
_tdav_producer_screencast_ddraw_pause,
_tdav_producer_screencast_ddraw_stop
};
const tmedia_producer_plugin_def_t *tdav_producer_screencast_ddraw_plugin_def_t = &tdav_producer_screencast_ddraw_plugin_def_s;
#endif /* TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT */

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Mamadou DIOP.
/* Copyright (C) 2014-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
@ -437,9 +437,11 @@ static void* TSK_STDCALL _tdav_producer_screencast_record_thread(void *arg)
while (ret == 0 && p_gdi->b_started) {
TimeNow = tsk_time_now();
if ((TimeNow - TimeLastFrame) > TimeFrameDuration) {
if (ret = _tdav_producer_screencast_grab(p_gdi)) {
goto next;
if ((TimeNow - TimeLastFrame) >= TimeFrameDuration) {
if (!p_gdi->b_muted && !p_gdi->b_paused) {
if (ret = _tdav_producer_screencast_grab(p_gdi)) {
goto next;
}
}
TimeLastFrame = TimeNow;
}