Commit 7e401f77 by 李维杰

commit mediacore project

parents
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{A1837C4E-381B-4696-B7CE-777423E5D84F}</ProjectGuid>
<RootNamespace>mediacoredemo</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../mediacore/include;E:\workspace\demo\ThirdLibs\SDL2-devel-2.0.8-VC\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(SolutionDir)bin\$(Configuration)\;E:\workspace\demo\ThirdLibs\SDL2-devel-2.0.8-VC\lib\x86</AdditionalLibraryDirectories>
<AdditionalDependencies>SDL2.lib;mediacored.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\ui_mode.cc" />
<ClCompile Include="src\video_player.cc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
<ClInclude Include="src\ui_mode.h" />
<ClInclude Include="src\video_player.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="mediacoredemo.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="源文件\src">
<UniqueIdentifier>{63f48388-477c-49fd-906b-885ec0b12fee}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\main.cpp">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="src\ui_mode.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="src\video_player.cc">
<Filter>源文件\src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="src\ui_mode.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="src\video_player.h">
<Filter>源文件\src</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="mediacoredemo.rc">
<Filter>资源文件</Filter>
</ResourceCompile>
</ItemGroup>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
\ No newline at end of file
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 mediacoredemo.rc 使用
//
#define IDD_DIALOG_MAIN 101
#define IDC_BUTTON_PUSH 1001
#define IDC_STATIC_VIDEO 1002
#define IDC_STATIC_VIDEO_REMOTE 1003
#define IDC_BUTTON_PULL 1008
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1009
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
// GT_HelloWorldWin32.cpp
// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <iostream>
#include <string>
#include "../resource.h"
#include "ui_mode.h"
#include "video_player.h"
CUiMode *ui_mode_ = NULL;
CVideoPlayer *local_video_render_ = NULL;
CVideoPlayer *remote_video_render_ = NULL;
static const std::string kPushParam = "{\
\"local_address\":\"192.168.42.146\",\n\
\"local_port_video\":9001,\n\
\"local_port_audio\":9002,\n\
\"remote_address\":\"192.168.42.121\",\n\
\"remote_port_video\":10001,\n\
\"remote_port_audio\":10002\n\
}";
static const std::string kPullParam = "{\
\"local_address\":\"192.168.42.121\",\n\
\"local_port_video\":10001,\n\
\"local_port_audio\":10002,\n\
\"remote_address\":\"192.168.42.146\",\n\
\"remote_port_video\":9001,\n\
\"remote_port_audio\":9002\n\
}";
int pull_handle_ = 0;
int push_handle_ = 0;
HWND main_hwnd_ = 0;
HWND local_video_hwnd_ = 0;
HWND remote_video_hwnd_ = 0;
INT_PTR WINAPI Main_Dialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
main_hwnd_ = hWnd;
local_video_hwnd_ = ::GetDlgItem(hWnd, IDC_STATIC_VIDEO);
remote_video_hwnd_ = ::GetDlgItem(hWnd, IDC_STATIC_VIDEO_REMOTE);
ui_mode_ = new CUiMode((int)hWnd);
ui_mode_->Initialize();
local_video_render_ = new CVideoPlayer((int)local_video_hwnd_, 1920, 1080);
remote_video_render_ = new CVideoPlayer((int)remote_video_hwnd_, 1920, 1080);
}
break;
case WM_USER_DRAW_REMOTE_VIDEO:
{
int width = 0;
int height = 0;
ui_mode_->GetMeRemoteVideoFrame(remote_video_render_->video_frame_buffer_y_, remote_video_render_->frame_y_pitch_,
remote_video_render_->video_frame_buffer_u_, remote_video_render_->frame_u_pitch_,
remote_video_render_->video_frame_buffer_v_, remote_video_render_->frame_v_pitch_,
width, height);
remote_video_render_->PlayVideo(width, height);
}
break;
case WM_USER_DRAW_LOCAL_VIDEO:
{
int width = 0;
int height = 0;
ui_mode_->GetMeLocalVideoFrame(local_video_render_->video_frame_buffer_y_, local_video_render_->frame_y_pitch_,
local_video_render_->video_frame_buffer_u_, local_video_render_->frame_u_pitch_,
local_video_render_->video_frame_buffer_v_, local_video_render_->frame_v_pitch_,
width, height);
local_video_render_->PlayVideo(width, height);
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDC_BUTTON_PUSH:
{
push_handle_ = ui_mode_->CreateRoleHandle(kPushParam.c_str(), ZRoleType::Z_ROLE_TYPE_PUBLISH);
ui_mode_->StartPush(push_handle_);
}
break;
case IDC_BUTTON_PULL:
{
pull_handle_ = ui_mode_->CreateRoleHandle(kPullParam.c_str(), ZRoleType::Z_ROLE_TYPE_SUBSCRIBE);
ui_mode_->StartPull(pull_handle_);
}
break;
}
}
break;
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, Main_Dialog, _ttoi(""));
return 0;
}
\ No newline at end of file
#include "ui_mode.h"
#include <Windows.h>
static const int kWidth = 1080;
static const int kHeight = 720;
CUiMode::CUiMode(int hWnd)
{
win_hwnd_ = hWnd;
push_handle_ = 0;
pull_handle_ = 0;
//local
width_ = 0;
height_ = 0;
local_buffer_y_ = new unsigned char[kWidth * kHeight];
local_buffer_u_ = new unsigned char[kWidth * kHeight];
local_buffer_v_ = new unsigned char[kWidth * kHeight];
remote_width_ = 0;
remote_height_ = 0;
remote_buffer_y_ = new unsigned char[kWidth * kHeight];
remote_buffer_u_ = new unsigned char[kWidth * kHeight];
remote_buffer_v_ = new unsigned char[kWidth * kHeight];
}
CUiMode::~CUiMode()
{
//local
delete local_buffer_y_;
delete local_buffer_u_;
delete local_buffer_v_;
local_buffer_y_ = nullptr;
local_buffer_u_ = nullptr;
local_buffer_v_ = nullptr;
//remote
delete remote_buffer_y_;
delete remote_buffer_u_;
delete remote_buffer_v_;
remote_buffer_y_ = nullptr;
remote_buffer_u_ = nullptr;
remote_buffer_v_ = nullptr;
}
void CUiMode::Initialize()
{
offcn::Initialize(this);
}
int CUiMode::CreateRoleHandle(const char *param, ZRoleType type)
{
int handle = offcn::CreateRoleHandle(param, this, type);
if (type == Z_ROLE_TYPE_PUBLISH)
{
push_handle_ = handle;
}
else
{
pull_handle_ = handle;
}
return handle;
}
void CUiMode::DestroyRoleHandle(int handle, ZRoleType type)
{
offcn::DestroyRoleHandle(handle, type);
}
void CUiMode::StartPush(int handle)
{
offcn::StartPublish(handle);
}
void CUiMode::StopPush(int handle)
{
offcn::StopPublish(handle);
}
void CUiMode::StartPull(int handle)
{
offcn::StartSubscribe(handle);
}
void CUiMode::StopPull(int handle)
{
offcn::StopSubscribe(handle);
}
void CUiMode::GetMeLocalVideoFrame(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height)
{
readLocalBuffer(dst_buffer_y, y_pitch, dst_buffer_u, u_pitch, dst_buffer_v, v_pitch, width, height);
}
void CUiMode::writeLocalBuffer(const unsigned char *pY, int y_pitch, const unsigned char *pU, int u_pitch, const unsigned char *pV, int v_pitch, int width, int height)
{
std::unique_lock<std::mutex> lck(local_mutex_);
local_y_pitch_ = y_pitch;
memcpy(local_buffer_y_, pY, y_pitch*height);
local_u_pitch_ = u_pitch;
memcpy(local_buffer_u_, pU, u_pitch*height / 2);
local_v_pitch_ = v_pitch;
memcpy(local_buffer_v_, pV, v_pitch*height / 2);
width_ = width;
height_ = height;
}
void CUiMode::readLocalBuffer(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height)
{
std::unique_lock<std::mutex> lck(local_mutex_);
width = width_;
height = height_;
y_pitch = local_y_pitch_;
u_pitch = local_u_pitch_;
v_pitch = local_v_pitch_;
memcpy(dst_buffer_y, local_buffer_y_, y_pitch*height);
memcpy(dst_buffer_u, local_buffer_u_, u_pitch*height / 2);
memcpy(dst_buffer_v, local_buffer_v_, v_pitch*height / 2);
}
void CUiMode::GetMeRemoteVideoFrame(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height)
{
readRemoteBuffer(dst_buffer_y, y_pitch, dst_buffer_u, u_pitch, dst_buffer_v, v_pitch, width, height);
}
void CUiMode::writeRemoteBuffer(const unsigned char *pY, int y_pitch, const unsigned char *pU, int u_pitch, const unsigned char *pV, int v_pitch, int width, int height)
{
std::unique_lock<std::mutex> lck(remote_mutex_);
remote_y_pitch_ = y_pitch;
memcpy(remote_buffer_y_, pY, y_pitch*height);
remote_u_pitch_ = u_pitch;
memcpy(remote_buffer_u_, pU, u_pitch*height / 2);
remote_v_pitch_ = v_pitch;
memcpy(remote_buffer_v_, pV, v_pitch*height / 2);
remote_width_ = width;
remote_height_ = height;
}
void CUiMode::readRemoteBuffer(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height)
{
std::unique_lock<std::mutex> lck(remote_mutex_);
width = remote_width_;
height = remote_height_;
y_pitch = remote_y_pitch_;
u_pitch = remote_u_pitch_;
v_pitch = remote_v_pitch_;
memcpy(dst_buffer_y, remote_buffer_y_, y_pitch*height);
memcpy(dst_buffer_u, remote_buffer_u_, u_pitch*height / 2);
memcpy(dst_buffer_v, remote_buffer_v_, v_pitch*height / 2);
}
void CUiMode::OnGlobalEvent(int code, const char *context)
{
}
void CUiMode::OnChannelEvent(int handle, int code, const char *context)
{
}
void CUiMode::OnChannelVideoFrame(int handle, const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch, int width, int height)
{
if (handle == push_handle_)
{
writeLocalBuffer(pY, yPitch, pU, uPitch, pV, vPitch, width, height);
::PostMessage((HWND)win_hwnd_, WM_USER_DRAW_LOCAL_VIDEO, 0, 0);
}
else
{
writeRemoteBuffer(pY, yPitch, pU, uPitch, pV, vPitch, width, height);
::PostMessage((HWND)win_hwnd_, WM_USER_DRAW_REMOTE_VIDEO, 0, 0);
}
}
void CUiMode::OnChannelAudioFrame(int handle, const void* audio_data, int bits_per_sample, int sample_rate, size_t number_of_channels, size_t number_of_frames, bool is_remote)
{
}
#pragma once
#include "ztypes.h"
#include "libmediacore.h"
#include <mutex>
#define WM_USER_DRAW_REMOTE_VIDEO WM_USER + 100
#define WM_USER_DRAW_LOCAL_VIDEO WM_USER + 101
class CUiMode : public GlobalObserver, public ChannelObserver
{
public:
CUiMode(int hWnd);
~CUiMode();
void Initialize();
int CreateRoleHandle(const char *param, ZRoleType type);
void DestroyRoleHandle(int handle, ZRoleType type);
void StartPush(int handle);
void StopPush(int handle);
void StartPull(int handle);
void StopPull(int handle);
/**
* GlobalObserver
*/
virtual void OnGlobalEvent(int code, const char *context);
/**
* ChannelObserver
*/
virtual void OnChannelEvent(int handle, int code, const char *context);
virtual void OnChannelVideoFrame(int handle, const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch, int width, int height);
virtual void OnChannelAudioFrame(int handle, const void* audio_data, int bits_per_sample, int sample_rate, size_t number_of_channels, size_t number_of_frames, bool is_remote);
/**
* UI thread get local capture video frame to play
*/
void GetMeLocalVideoFrame(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height);
/**
* UI thread get remote video frame to play
*/
void GetMeRemoteVideoFrame(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height);
private:
void writeLocalBuffer(const unsigned char *pY, int y_pitch, const unsigned char *pU, int u_pitch, const unsigned char *pV, int v_pitch, int width, int height);
void readLocalBuffer(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height);
unsigned char *local_buffer_y_;
unsigned char *local_buffer_u_;
unsigned char *local_buffer_v_;
int local_y_pitch_;
int local_u_pitch_;
int local_v_pitch_;
int width_;
int height_;
std::mutex local_mutex_;
private:
void writeRemoteBuffer(const unsigned char *pY, int y_pitch, const unsigned char *pU, int u_pitch, const unsigned char *pV, int v_pitch, int width, int height);
void readRemoteBuffer(unsigned char *dst_buffer_y, int &y_pitch, unsigned char *dst_buffer_u, int &u_pitch, unsigned char *dst_buffer_v, int &v_pitch, int &width, int &height);
unsigned char *remote_buffer_y_;
unsigned char *remote_buffer_u_;
unsigned char *remote_buffer_v_;
int remote_y_pitch_;
int remote_u_pitch_;
int remote_v_pitch_;
int remote_width_;
int remote_height_;
std::mutex remote_mutex_;
private:
int win_hwnd_;
int push_handle_;
int pull_handle_;
};
\ No newline at end of file
#include "video_player.h"
#include <Windows.h>
#include "SDL.h"
CVideoPlayer::CVideoPlayer(int hWnd, int width, int height)
{
wnd_handle_ = hWnd;
video_width_ = width;
video_height_ = height;
sdl_window_ = NULL;
sdl_render_ = NULL;
sdl_texture_ = NULL;
sdl_init_ok_ = false;
video_frame_buffer_y_ = new unsigned char[width*height];
video_frame_buffer_u_ = new unsigned char[width*height];
video_frame_buffer_v_ = new unsigned char[width*height];
frame_y_pitch_ = 0;
frame_u_pitch_ = 0;
frame_v_pitch_ = 0;
}
CVideoPlayer::~CVideoPlayer()
{
delete video_frame_buffer_y_;
delete video_frame_buffer_u_;
delete video_frame_buffer_v_;
video_frame_buffer_y_ = nullptr;
video_frame_buffer_u_ = nullptr;
video_frame_buffer_v_ = nullptr;
}
void CVideoPlayer::PlayVideo(int width, int height)
{
if (!sdl_init_ok_)
{
if (!InitSDL(wnd_handle_, width, height))
{
return;
}
}
else
{
if (width != video_width_ || height != video_height_)
{
UnInitSDL();
if (!InitSDL(wnd_handle_, width, height))
{
sdl_init_ok_ = false;
return;
}
}
}
PlayVideoWithSDL(video_frame_buffer_y_, frame_y_pitch_, video_frame_buffer_u_, frame_u_pitch_, video_frame_buffer_v_, frame_v_pitch_);
video_width_ = width;
video_height_ = height;
}
bool CVideoPlayer::InitSDL(int hWnd, int width, int height)
{
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
return false;
}
sdl_window_ = SDL_CreateWindowFrom((void *)hWnd);
if (!sdl_window_)
{
goto err;
}
sdl_render_ = SDL_CreateRenderer(sdl_window_, -1, 0);
if (!sdl_render_)
{
goto err;
}
sdl_texture_ = SDL_CreateTexture(sdl_render_, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
if (!sdl_texture_)
{
goto err;
}
sdl_init_ok_ = true;
video_width_ = width;
video_height_ = height;
return true;
err:
return false;
}
void CVideoPlayer::UnInitSDL()
{
if (sdl_render_)
{
SDL_DestroyRenderer(sdl_render_);
sdl_render_ = NULL;
}
if (sdl_texture_)
{
SDL_DestroyTexture(sdl_texture_);
sdl_texture_ = NULL;
}
if (sdl_window_)
{
SDL_DestroyWindow(sdl_window_);
sdl_window_ = NULL;
//https://www.cnblogs.com/lihaiping/p/4324315.html
ShowWindow((HWND)wnd_handle_, SW_SHOWNORMAL);
}
sdl_init_ok_ = false;
}
void CVideoPlayer::PlayVideoWithSDL(const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch)
{
int nRet = SDL_UpdateYUVTexture(sdl_texture_, NULL, pY, yPitch, pU, uPitch, pV, vPitch);
if (nRet != 0)
{
return;
}
int nWidth = 0;
int nHeight = 0;
SDL_GetWindowSize(sdl_window_, &nWidth, &nHeight);
SDL_Rect rc = { 0, 0, nWidth, nHeight };
SDL_RenderClear(sdl_render_);
SDL_RenderCopy(sdl_render_, sdl_texture_, NULL, NULL);
SDL_RenderPresent(sdl_render_);
}
\ No newline at end of file
#pragma once
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Texture;
class CVideoPlayer
{
public:
CVideoPlayer(int hWnd, int width, int height);
~CVideoPlayer();
void PlayVideo(int width, int height);
unsigned char *video_frame_buffer_y_;
unsigned char *video_frame_buffer_u_;
unsigned char *video_frame_buffer_v_;
int frame_y_pitch_;
int frame_u_pitch_;
int frame_v_pitch_;
int video_width_;
int video_height_;
private:
int wnd_handle_;
private:
bool InitSDL(int hWnd, int width, int height);
void UnInitSDL();
void PlayVideoWithSDL(const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch);
private:
SDL_Window *sdl_window_;
SDL_Renderer *sdl_render_;
SDL_Texture *sdl_texture_;
bool sdl_init_ok_;
};
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="源文件\src">
<UniqueIdentifier>{eea7a83f-6dab-467b-81b8-b18fda057a55}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\include">
<UniqueIdentifier>{8ee1d5dc-b0d0-4867-9488-7df10786e3f5}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\jsoncpp">
<UniqueIdentifier>{a1b9bfd2-b8bf-4857-8cad-4ffda20955c5}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="targetver.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="..\src\client_manager.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\direct_base_client.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\direct_pull_client.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\direct_push_client.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\encoder_setting.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\msg_data_customize.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\net_data_receive.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\offcn_call.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\src\rtprtcp_transport.h">
<Filter>源文件\src</Filter>
</ClInclude>
<ClInclude Include="..\include\libmediacore.h">
<Filter>源文件\include</Filter>
</ClInclude>
<ClInclude Include="..\include\ztypes.h">
<Filter>源文件\include</Filter>
</ClInclude>
<ClInclude Include="..\src\video_render_ex.h">
<Filter>源文件\src</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="..\src\client_manager.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\direct_base_client.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\direct_pull_client.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\direct_push_client.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\encoder_setting.cpp">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\net_data_receive.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\offcn_call.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\rtprtcp_transport.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\..\third_party\jsoncpp\src\json_reader.cpp">
<Filter>源文件\jsoncpp</Filter>
</ClCompile>
<ClCompile Include="..\..\third_party\jsoncpp\src\json_value.cpp">
<Filter>源文件\jsoncpp</Filter>
</ClCompile>
<ClCompile Include="..\..\third_party\jsoncpp\src\json_writer.cpp">
<Filter>源文件\jsoncpp</Filter>
</ClCompile>
<ClCompile Include="..\src\libmediacore.cc">
<Filter>源文件\src</Filter>
</ClCompile>
<ClCompile Include="..\src\video_render_ex.cc">
<Filter>源文件\src</Filter>
</ClCompile>
</ItemGroup>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
\ No newline at end of file
#ifndef __LIB_CONN_MIC_H__
#define __LIB_CONN_MIC_H__
#include "ztypes.h"
namespace offcn
{
/**
* Init global param
*/
ZG_C_API void Initialize(GlobalObserver *observer);
/**
* UnInit global param
*/
ZG_C_API void UnInitialize();
/**
* Set global param
"{
"mic_device_name":"my mic name(utf8)",
"speaker_device_name":"my speaker name(utf8)",
}"
*/
ZG_C_API void SetParam(const char *param);
/**
* Create handle
*
* @param param publish and subscribe param in json format
"{
"video_device_name":"my device name(utf8)",
"video_send_bitrate":300000
}"
*
* @return role handle id
*/
ZG_C_API int CreateRoleHandle(const char *param, ChannelObserver *observer, ZRoleType type);
/**
* Destroy handle
*
* @param handle role's handle id
* @param type role's type
*/
ZG_C_API void DestroyRoleHandle(int handle, ZRoleType type);
/**
* Input yuv data
*/
ZG_C_API void InputVideoData(int handle, const unsigned char *data, int width, int height, int frame_rate);
/**
* Start/Stop publish stream
*
* @param handle role handle id
*/
ZG_C_API void StartPublish(int handle);
ZG_C_API void StopPublish(int handle);
/**
* Start/Stop subscribe stream
*
* @param handle role handle id
*/
ZG_C_API void StartSubscribe(int handle);
ZG_C_API void StopSubscribe(int handle);
}
#endif
\ No newline at end of file
#ifndef __Z_CONN_MIC_TYPES_H__
#define __Z_CONN_MIC_TYPES_H__
#include <stdio.h>
#ifdef _WIN32
#define ZG_API_EXPORT __declspec(dllexport)
#define ZG_API_IMPORT __declspec(dllimport)
#else
#define ZG_API_EXPORT __attribute__ ((visibility("default")))
#define ZG_API_IMPORT
#endif
#ifdef __cplusplus
#define ZG_C_API_EXPORT extern "C" ZG_API_EXPORT
#define ZG_C_API_IMPORT extern "C" ZG_API_IMPORT
#else
#define ZG_C_API_EXPORT ZG_API_EXPORT
#define ZG_C_API_IMPORT ZG_API_IMPORT
#endif
#ifdef ZG_SDK_EXPORTS //need define this micro,when build dll, undefine this micro when use this dll
#define ZG_C_API ZG_C_API_EXPORT
#else
#define ZG_C_API ZG_C_API_IMPORT
#endif
typedef enum
{
Z_START_PUBLISH_SUCCESS, //start publish stream success
Z_START_PUBLISH_FAILURE,
Z_START_SUBSCRIBE_SUCCESS, //start subscribe success
Z_START_SUBSCRIBE_FAILURE,
Z_DESTROY_HANDLE_OK,
}ZEventCode;
typedef enum
{
Z_ROLE_TYPE_PUBLISH,
Z_ROLE_TYPE_SUBSCRIBE,
}ZRoleType;
class GlobalObserver
{
public:
virtual void OnGlobalEvent(int code, const char *context) = 0;
};
class ChannelObserver
{
public:
virtual void OnChannelEvent(int handle, int code, const char *context) = 0;
virtual void OnChannelVideoFrame(int handle, const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch, int width, int height) = 0;
virtual void OnChannelAudioFrame(int handle, const void* audio_data, int bits_per_sample, int sample_rate, size_t number_of_channels, size_t number_of_frames, bool is_remote) = 0;
};
#endif
\ No newline at end of file
#include "client_manager.h"
#include "msg_data_customize.h"
#include "direct_base_client.h"
#include "direct_push_client.h"
#include "direct_pull_client.h"
#include "offcn_call.h"
static int g_handle = 0;
ClientManager::ClientManager(GlobalObserver *observer)
:observer_(observer)
{
net_data_receiver_ = NULL;
signal_thread_.SetName("client manager signla thread", NULL);
signal_thread_.Start();
}
ClientManager::~ClientManager()
{
}
/**
* NetDataReceiveObserver
*/
void ClientManager::NetDataReceiveGetData(SOCKET socket, int handle_id, std::string ip, int port, const char *buffer, int size)
{
ZMsgDataNetData *data = new ZMsgDataNetData();
memcpy(data->buffer, buffer, size);
data->size = size;
data->socket = socket;
data->handle = handle_id;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Receive_Data, data);
}
void ClientManager::Init()
{
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Init);
}
int ClientManager::CreatePushHandle(std::string param, ChannelObserver *observer)
{
ZMsgDataHandle *data = new ZMsgDataHandle();
data->string_data_ = param;
data->int_data_ = ++g_handle;
data->observer_ = observer;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Create_Push_Handle, data);
return data->int_data_;
}
void ClientManager::DestroyPushHandle(int handle)
{
ZMessageDataInt *data = new ZMessageDataInt();
data->int_data_ = handle;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Destroy_Push_Handle, data);
}
void ClientManager::StartPush(int handle)
{
ZMessageDataInt *data = new ZMessageDataInt();
data->int_data_ = handle;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Start_Push, data);
}
void ClientManager::StopPush(int handle)
{
ZMessageDataInt *data = new ZMessageDataInt();
data->int_data_ = handle;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Stop_Push, data);
}
int ClientManager::CreatePullHandle(std::string param, ChannelObserver *observer)
{
ZMsgDataHandle *data = new ZMsgDataHandle();
data->string_data_ = param;
data->int_data_ = ++g_handle;
data->observer_ = observer;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Create_Pull_Handle, data);
return data->int_data_;
}
void ClientManager::DestroyPullHandle(int handle)
{
ZMessageDataInt *data = new ZMessageDataInt();
data->int_data_ = handle;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Destroy_Pull_Handle, data);
}
void ClientManager::StartPull(int handle)
{
ZMessageDataInt *data = new ZMessageDataInt();
data->int_data_ = handle;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Start_Pull, data);
}
void ClientManager::StopPull(int handle)
{
ZMessageDataInt *data = new ZMessageDataInt();
data->int_data_ = handle;
signal_thread_.Post(RTC_FROM_HERE, this, Client_Msg_Stop_Pull);
}
void ClientManager::OnMessage(rtc::Message* msg)
{
switch(msg->message_id)
{
case Client_Msg_Receive_Data:
{
ZMsgDataNetData *data = dynamic_cast<ZMsgDataNetData *>(msg->pdata);
if(data)
{
AllClientType::const_iterator itor;
itor = all_role_.find(data->handle);
DirectBaseClient *client = dynamic_cast<DirectBaseClient *>(itor->second);
if(client)
{
client->InputRtpRtcpData(data->socket, data->buffer, data->size);
}
}
}
break;
case Client_Msg_Init:
{
DoInit();
}
break;
case Client_Msg_Create_Push_Handle:
{
ZMsgDataHandle *data = dynamic_cast<ZMsgDataHandle *>(msg->pdata);
DoCreatePushHandle(data->int_data_, data->string_data_, data->observer_);
}
break;
case Client_Msg_Destroy_Push_Handle:
{
ZMessageDataInt *data = dynamic_cast<ZMessageDataInt *>(msg->pdata);
DoDestroyPushHandle(data->int_data_);
}
break;
case Client_Msg_Start_Push:
{
ZMessageDataInt *data = dynamic_cast<ZMessageDataInt *>(msg->pdata);
DoStartPush(data->int_data_);
}
break;
case Client_Msg_Stop_Push:
{
ZMessageDataInt *data = dynamic_cast<ZMessageDataInt *>(msg->pdata);
DoStopPush(data->int_data_);
}
break;
case Client_Msg_Create_Pull_Handle:
{
ZMsgDataHandle *data = dynamic_cast<ZMsgDataHandle *>(msg->pdata);
DoCreatePullHandle(data->int_data_, data->string_data_, data->observer_);
}
break;
case Client_Msg_Destroy_Pull_Handle:
{
ZMessageDataInt *data = dynamic_cast<ZMessageDataInt *>(msg->pdata);
DoDestroyPullHandle(data->int_data_);
}
break;
case Client_Msg_Start_Pull:
{
ZMessageDataInt *data = dynamic_cast<ZMessageDataInt *>(msg->pdata);
DoStartPull(data->int_data_);
}
break;
case Client_Msg_Stop_Pull:
{
ZMessageDataInt *data = dynamic_cast<ZMessageDataInt *>(msg->pdata);
DoStopPull(data->int_data_);
}
break;
}
delete msg->pdata;
msg->pdata = NULL;
}
void ClientManager::DoInit()
{
offcn_call_ = new OffcnCall();
offcn_call_->CreateCall(NULL, NULL);
net_data_receiver_ = new NetDataReceive(this);
net_data_receiver_->Start();
}
void ClientManager::DoCreatePushHandle(int handle, std::string param, ChannelObserver *observer)
{
DirectBaseClient *client = new DirectPushClient(offcn_call_, handle, param, true, observer);
SOCKET socket_audio = client->GetAudioSocket();
SOCKET socket_video = client->GetVideoSocket();
net_data_receiver_->AddConnection(socket_audio, handle);
net_data_receiver_->AddConnection(socket_video, handle);
all_role_.insert(std::pair<int, DirectBaseClient *>(handle, client));
}
void ClientManager::DoDestroyPushHandle(int handle)
{
AllClientType::const_iterator itor;
itor = all_role_.find(handle);
if(itor != all_role_.end())
{
DirectBaseClient *client = (DirectBaseClient *)itor->second;
if (client)
{
SOCKET socket_audio = client->GetAudioSocket();
SOCKET socket_video = client->GetVideoSocket();
net_data_receiver_->RemoveConnection(socket_audio);
net_data_receiver_->RemoveConnection(socket_video);
delete client;
all_role_.erase(itor);
}
}
}
void ClientManager::DoStartPush(int handle)
{
AllClientType::const_iterator itor;
itor = all_role_.find(handle);
if(itor != all_role_.end())
{
DirectPushClient *client = dynamic_cast<DirectPushClient *>(itor->second);
if(client)
{
client->StartPush();
}
}
}
void ClientManager::DoStopPush(int handle)
{
AllClientType::const_iterator itor;
itor = all_role_.find(handle);
if (itor != all_role_.end())
{
DirectPushClient *client = dynamic_cast<DirectPushClient *>(itor->second);
if (client)
{
client->StopPush();
}
}
}
void ClientManager::DoCreatePullHandle(int handle, std::string param, ChannelObserver *observer)
{
DirectBaseClient *client = new DirectPullClient(offcn_call_, handle, param, false, observer);
SOCKET socket_audio = client->GetAudioSocket();
SOCKET socket_video = client->GetVideoSocket();
net_data_receiver_->AddConnection(socket_audio, handle);
net_data_receiver_->AddConnection(socket_video, handle);
all_role_.insert(std::pair<int , DirectBaseClient *>(handle, client));
}
void ClientManager::DoDestroyPullHandle(int handle)
{
AllClientType::const_iterator itor;
itor = all_role_.find(handle);
if (itor != all_role_.end())
{
DirectPullClient *client = dynamic_cast<DirectPullClient *>(itor->second);
if (client)
{
SOCKET socket_audio = client->GetAudioSocket();
SOCKET socket_video = client->GetVideoSocket();
net_data_receiver_->RemoveConnection(socket_audio);
net_data_receiver_->RemoveConnection(socket_video);
delete client;
all_role_.erase(itor);
}
}
}
void ClientManager::DoStartPull(int handle)
{
AllClientType::const_iterator itor;
itor = all_role_.find(handle);
if (itor != all_role_.end())
{
DirectPullClient *client = dynamic_cast<DirectPullClient *>(itor->second);
if (client)
{
client->StartPull();
}
}
}
void ClientManager::DoStopPull(int handle)
{
AllClientType::const_iterator itor;
itor = all_role_.find(handle);
if (itor != all_role_.end())
{
DirectPullClient *client = dynamic_cast<DirectPullClient *>(itor->second);
if (client)
{
client->StopPull();
}
}
}
\ No newline at end of file
#pragma once
#include "net_data_receive.h"
#include "rtc_base/thread.h"
#include "rtc_base/messagehandler.h"
#include "ztypes.h"
#include <map>
class OffcnCall;
class DirectBaseClient;
class ClientManager : public rtc::MessageHandler, public NetDataReceiveObserver
{
public:
ClientManager(GlobalObserver *observer);
~ClientManager();
void Init();
int CreatePushHandle(std::string param, ChannelObserver *observer);
void DestroyPushHandle(int handle);
void StartPush(int handle);
void StopPush(int handle);
int CreatePullHandle(std::string param, ChannelObserver *observer);
void DestroyPullHandle(int handle);
void StartPull(int handle);
void StopPull(int handle);
public:
/**
* NetDataReceiveObserver
*/
virtual void NetDataReceiveGetData(SOCKET socket, int handle_id, std::string ip, int port, const char *buffer, int size);
virtual void OnMessage(rtc::Message* msg);
private:
enum
{
Client_Msg_Init,
Client_Msg_Create_Push_Handle,
Client_Msg_Destroy_Push_Handle,
Client_Msg_Start_Push,
Client_Msg_Stop_Push,
Client_Msg_Create_Pull_Handle,
Client_Msg_Destroy_Pull_Handle,
Client_Msg_Start_Pull,
Client_Msg_Stop_Pull,
Client_Msg_Receive_Data,
};
void DoInit();
void DoCreatePushHandle(int handle, std::string param, ChannelObserver *observer);
void DoDestroyPushHandle(int handle);
void DoStartPush(int handle);
void DoStopPush(int handle);
void DoCreatePullHandle(int handle, std::string param, ChannelObserver *observer);
void DoDestroyPullHandle(int handle);
void DoStartPull(int handle);
void DoStopPull(int handle);
private:
rtc::Thread signal_thread_;
private:
NetDataReceive *net_data_receiver_;
private:
typedef std::map<int, DirectBaseClient *> AllClientType;
AllClientType all_role_;
OffcnCall *offcn_call_;
private:
GlobalObserver *observer_;
};
\ No newline at end of file
#include "direct_base_client.h"
#include "json/json.h"
DirectBaseClient::DirectBaseClient(int handle, std::string param, bool is_pusher, ChannelObserver *observer)
:handle_(handle),
client_param_(param),
is_pusher_(is_pusher),
observer_(observer)
{
local_address_ = "";
local_port_video_ = 0;
local_port_audio_ = 0;
remote_address_ = "";
remote_port_video_ = 0;
remote_port_audio_ = 0;
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
return;
}
ParseParam();
video_render_ = new CVideoRenderEx(nullptr, this);
}
DirectBaseClient::~DirectBaseClient()
{
WSACleanup();
if (video_render_)
{
delete video_render_;
video_render_ = NULL;
}
}
void DirectBaseClient::ParseParam()
{
Json::Value root;
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> const reader(readerBuilder.newCharReader());
std::string client_param = client_param_;
const char *start_pos = client_param.c_str();
std::string err;
if (!reader->parse(start_pos, start_pos + client_param.length(), &root, &err))
{
return;
}
if (root["local_address"])
{
local_address_ = root["local_address"].asString();
}
if (root["local_port_video"])
{
local_port_video_ = root["local_port_video"].asInt();
}
else
{
local_port_video_ = 0;
}
if (root["local_port_audio"])
{
local_port_audio_ = root["local_port_audio"].asInt();
}
else
{
local_port_audio_ = 0;
}
if (root["remote_address"])
{
remote_address_ = root["remote_address"].asString();
}
if (root["remote_port_video"])
{
remote_port_video_ = root["remote_port_video"].asInt();
}
else
{
remote_port_video_ = 0;
}
if (root["remote_port_audio"])
{
remote_port_audio_ = root["remote_port_audio"].asInt();
}
else
{
remote_port_audio_ = 0;
}
}
void DirectBaseClient::OnVideoFrame(const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch, int width, int height)
{
if (observer_)
{
observer_->OnChannelVideoFrame(handle_, pY, yPitch, pU, uPitch, pV, vPitch, width, height);
}
}
\ No newline at end of file
#pragma once
#include <WinSock2.h>
#include <string>
#include "ztypes.h"
#include "video_render_ex.h"
class DirectBaseClient : public CVideoRenderObserver
{
public:
DirectBaseClient(int handle, std::string param, bool is_pusher, ChannelObserver *observer);
virtual ~DirectBaseClient();
int GetHandle() { return handle_; }
std::string GetClientParam() { return client_param_; }
virtual SOCKET GetAudioSocket() { return socket_audio_; }
virtual SOCKET GetVideoSocket() { return socket_video_; }
virtual void InputRtpRtcpData(SOCKET socket, char *buffer, int size) = 0;
virtual void ParseParam();
public:
/**
* CVideoRenderObserver
*/
virtual void OnVideoFrame(const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch, int width, int height);
protected:
std::string local_address_;
int local_port_video_;
int local_port_audio_;
std::string remote_address_;
int remote_port_video_;
int remote_port_audio_;
SOCKET socket_video_;
SOCKET socket_audio_;
protected:
ChannelObserver *observer_;
CVideoRenderEx *video_render_;
private:
int handle_;
bool is_pusher_;
std::string client_param_;
};
\ No newline at end of file
#include "direct_pull_client.h"
#include "offcn_call.h"
#include "json/json.h"
DirectPullClient::DirectPullClient(OffcnCall *offcn_call, int handle, std::string param, bool is_pusher, ChannelObserver *observer)
:DirectBaseClient(handle, param, is_pusher, observer), offcn_call_(offcn_call)
{
transport_audio_ = nullptr;
transport_video_ = nullptr;
audio_receive_stream_ = nullptr;
video_receive_stream_ = nullptr;
socket_audio_ = socket(AF_INET, SOCK_DGRAM, 0);
socket_video_ = socket(AF_INET, SOCK_DGRAM, 0);
}
DirectPullClient::~DirectPullClient()
{
}
void DirectPullClient::InputRtpRtcpData(SOCKET socket, char *buffer, int size)
{
if (offcn_call_)
{
rtc::CopyOnWriteBuffer packet(buffer, size);
if (socket == socket_audio_)
{
offcn_call_->GetCall()->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO, packet, rtc::TimeMicros());
}
else
{
offcn_call_->GetCall()->Receiver()->DeliverPacket(webrtc::MediaType::VIDEO, packet, rtc::TimeMicros());
}
}
}
bool DirectPullClient::StartPull()
{
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(local_port_audio_);
local_addr.sin_addr.s_addr = inet_addr(local_address_.c_str());
int nRet = bind(socket_audio_, (const sockaddr *)&local_addr, sizeof(local_addr));
if (nRet != 0)
{
goto Error;
}
local_addr.sin_port = htons(local_port_video_);
nRet = bind(socket_video_, (const sockaddr *)&local_addr, sizeof(local_addr));
if (nRet != 0)
{
goto Error;
}
transport_audio_ = new RtpRtcpTransport(this);
if(transport_audio_ == nullptr)
{
goto Error;
}
transport_video_ = new RtpRtcpTransport(this);
if (transport_video_ == nullptr)
{
goto Error;
}
audio_receive_stream_ = offcn_call_->CreateAudioReceiveStream(transport_audio_);
if(audio_receive_stream_ == nullptr)
{
goto Error;
}
video_receive_stream_ = offcn_call_->CreateVideoReceiveStream(video_render_, transport_video_);
if (video_receive_stream_ == nullptr)
{
goto Error;
}
offcn_call_->GetCall()->SignalChannelNetworkState(webrtc::MediaType::AUDIO, webrtc::kNetworkUp);
offcn_call_->GetCall()->SignalChannelNetworkState(webrtc::MediaType::VIDEO, webrtc::kNetworkUp);
audio_receive_stream_->Start();
video_receive_stream_->Start();
return true;
Error:
StopPull();
return false;
}
bool DirectPullClient::StopPull()
{
if (audio_receive_stream_)
{
audio_receive_stream_->Stop();
offcn_call_->GetCall()->DestroyAudioReceiveStream(audio_receive_stream_);
audio_receive_stream_ = nullptr;
}
if (video_receive_stream_)
{
video_receive_stream_->Stop();
offcn_call_->GetCall()->DestroyVideoReceiveStream(video_receive_stream_);
video_receive_stream_ = nullptr;
}
if(transport_audio_)
{
delete transport_audio_;
transport_audio_ = nullptr;
}
if (transport_video_)
{
delete transport_video_;
transport_video_ = nullptr;
}
if(socket_audio_ > 0)
{
closesocket(socket_audio_);
socket_audio_ = 0;
}
if (socket_video_ > 0)
{
closesocket(socket_video_);
socket_video_ = 0;
}
return true;
}
bool DirectPullClient::SendRtpRtcpData(RtpRtcpTransport *transport, const uint8_t *data, size_t length, bool is_rtcp)
{
if (remote_address_.size() <= 0)
{
return true;
}
struct sockaddr_in remote_addr;
memset(&remote_addr, 0, sizeof(remote_addr));
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(remote_address_.c_str());
int addrLen = sizeof(struct sockaddr_in);
int nRet = 0;
if (transport == transport_audio_)
{
if (remote_port_audio_ > 0)
{
remote_addr.sin_port = htons(remote_port_audio_);
nRet = sendto(socket_audio_, (const char *)data, length, 0, (struct sockaddr *)&remote_addr, addrLen);
}
}
else
{
if (remote_port_video_ > 0)
{
remote_addr.sin_port = htons(remote_port_video_);
nRet = sendto(socket_video_, (const char *)data, length, 0, (struct sockaddr *)&remote_addr, addrLen);
}
}
if (nRet < 0)
{
DWORD dw = GetLastError();
int i = 0;
return false;
}
return true;
}
\ No newline at end of file
#pragma once
#include <winsock2.h>
#include <Windows.h>
#include "call/audio_receive_stream.h"
#include "call/video_receive_stream.h"
#include "direct_base_client.h"
#include "rtprtcp_transport.h"
#include "video_render_ex.h"
class OffcnCall;
class DirectPullClient : public DirectBaseClient,
public RtpRtcpTransportObserver
{
public:
DirectPullClient(OffcnCall *offcn_call, int handle, std::string param, bool is_pusher, ChannelObserver *observer);
~DirectPullClient();
bool StartPull();
bool StopPull();
virtual void InputRtpRtcpData(SOCKET socket, char *buffer, int size);
public:
/**
* RtpRtcpTransportObserver
*/
virtual bool SendRtpRtcpData(RtpRtcpTransport *transport, const uint8_t *data, size_t length, bool is_rtcp);
private:
RtpRtcpTransport *transport_audio_;
RtpRtcpTransport *transport_video_;
private:
OffcnCall *offcn_call_;
webrtc::AudioReceiveStream *audio_receive_stream_;
webrtc::VideoReceiveStream *video_receive_stream_;
};
\ No newline at end of file
#include "direct_push_client.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
#include "offcn_call.h"
#include "encoder_setting.h"
#include "json/json.h"
using cricket::VideoFormat;
const VideoFormat formats[] = { {640, 480, VideoFormat::FpsToInterval(15), cricket::FOURCC_I420} };
DirectPushClient::DirectPushClient(OffcnCall *offcn_call, int handle, std::string param, bool is_pusher, ChannelObserver *observer)
:DirectBaseClient(handle, param, is_pusher, observer), offcn_call_(offcn_call)
{
transport_video_ = nullptr;
transport_audio_ = nullptr;
//create socket
socket_video_ = socket(AF_INET, SOCK_DGRAM, 0);
socket_audio_ = socket(AF_INET, SOCK_DGRAM, 0);
video_encoder_factory_ = webrtc::CreateBuiltinVideoEncoderFactory();
}
DirectPushClient::~DirectPushClient()
{
StopPush();
}
void DirectPushClient::InputRtpRtcpData(SOCKET socket, char *buffer, int size)
{
if (offcn_call_)
{
rtc::CopyOnWriteBuffer packet(buffer, size);
if (socket == socket_audio_)
{
offcn_call_->GetCall()->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO, packet, rtc::TimeMicros());
}
else if(socket == socket_video_)
{
offcn_call_->GetCall()->Receiver()->DeliverPacket(webrtc::MediaType::VIDEO, packet, rtc::TimeMicros());
}
}
}
bool DirectPushClient::StartPush()
{
rtc::VideoSinkWants wants;
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(1237);
local_addr.sin_addr.s_addr = inet_addr(local_address_.c_str());
int nRet = bind(socket_audio_, (const sockaddr *)&local_addr, sizeof(local_addr));
local_addr.sin_port = htons(1236);
nRet = bind(socket_video_, (const sockaddr *)&local_addr, sizeof(local_addr));
//create video capture
video_capturer_ = offcn::OpenVideoCaptureDevice();
if(video_capturer_ == nullptr)
{
return false;
}
//create transport
transport_video_ = new RtpRtcpTransport(this);
transport_audio_ = new RtpRtcpTransport(this);
//create video send stream
video_send_stream_ = offcn_call_->CreateVideoSendStream(video_encoder_factory_.get(), transport_video_, "offcn_stream_send_video");
//create audio send stream
audio_send_stream_ = offcn_call_->CreateAudioSendStream(transport_audio_, "offcn_stream_send_audio");
if(video_send_stream_ == nullptr || audio_send_stream_ == nullptr)
{
goto Error;
}
offcn_call_->GetCall()->SignalChannelNetworkState(webrtc::MediaType::VIDEO, webrtc::kNetworkUp);
offcn_call_->GetCall()->SignalChannelNetworkState(webrtc::MediaType::AUDIO, webrtc::kNetworkUp);
video_send_stream_->Start();
audio_send_stream_->Start();
video_send_stream_->SetSource((rtc::VideoSourceInterface<webrtc::VideoFrame> *)video_capturer_.get(), webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
video_capturer_->Start(formats[0]);
video_capturer_->AddOrUpdateSink(video_render_, wants);
return true;
Error:
StopPush();
return false;
}
bool DirectPushClient::StopPush()
{
if(video_capturer_)
{
video_capturer_->Stop();
video_capturer_.reset();
video_capturer_ = nullptr;
}
if (video_send_stream_)
{
offcn_call_->GetCall()->SignalChannelNetworkState(webrtc::MediaType::VIDEO, webrtc::kNetworkDown);
video_send_stream_->Stop();
offcn_call_->GetCall()->DestroyVideoSendStream(video_send_stream_);
video_send_stream_ = nullptr;
}
if(audio_send_stream_)
{
offcn_call_->GetCall()->SignalChannelNetworkState(webrtc::MediaType::AUDIO, webrtc::kNetworkDown);
audio_send_stream_->Stop();
offcn_call_->GetCall()->DestroyAudioSendStream(audio_send_stream_);
audio_send_stream_ = nullptr;
}
if(transport_video_)
{
delete transport_video_;
transport_video_ = nullptr;
}
if(transport_audio_)
{
delete transport_audio_;
transport_audio_ = nullptr;
}
if(socket_video_)
{
closesocket(socket_video_);
socket_video_ = 0;
}
if(socket_audio_)
{
closesocket(socket_audio_);
socket_audio_ = 0;
}
return true;
}
/**
* RtpRtcpTransportObserver
*/
bool DirectPushClient::SendRtpRtcpData(RtpRtcpTransport *transport, const uint8_t *data, size_t length, bool is_rtcp)
{
int nRet = 0;
struct sockaddr_in remote_addr;
memset(&remote_addr, 0, sizeof(remote_addr));
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(remote_address_.c_str());
int addrLen = sizeof(struct sockaddr_in);
int lastError = 0;
if (transport == transport_video_)
{
remote_addr.sin_port = htons(remote_port_video_);
nRet = sendto(socket_video_, (const char *)data, length, 0, (struct sockaddr *)&remote_addr, addrLen);
lastError = WSAGetLastError();
}
else
{
remote_addr.sin_port = htons(remote_port_audio_);
nRet = sendto(socket_audio_, (const char *)data, length, 0, (struct sockaddr *)&remote_addr, addrLen);
lastError = WSAGetLastError();
}
return true;
}
\ No newline at end of file
#pragma once
#include <winsock2.h>
#include <Windows.h>
#include <string>
#include "call/audio_send_stream.h"
#include "call/video_send_stream.h"
#include "direct_base_client.h"
#include "rtprtcp_transport.h"
#include "media/base/videocapturer.h"
#include "video_render_ex.h"
class OffcnCall;
class DirectPushClient : public DirectBaseClient, public RtpRtcpTransportObserver
{
public:
DirectPushClient(OffcnCall *offcn_call, int handle, std::string param, bool is_pusher, ChannelObserver *observer);
~DirectPushClient();
bool StartPush();
bool StopPush();
virtual void InputRtpRtcpData(SOCKET socket, char *buffer, int size);
public:
/**
* RtpRtcpTransportObserver
*/
virtual bool SendRtpRtcpData(RtpRtcpTransport *transport, const uint8_t *data, size_t length, bool is_rtcp);
private:
RtpRtcpTransport *transport_video_;
RtpRtcpTransport *transport_audio_;
private:
OffcnCall *offcn_call_;
std::unique_ptr<cricket::VideoCapturer> video_capturer_;
std::unique_ptr<webrtc::VideoEncoderFactory> video_encoder_factory_;
webrtc::AudioSendStream *audio_send_stream_;
webrtc::VideoSendStream *video_send_stream_;
};
\ No newline at end of file
#include "encoder_setting.h"
#include "rtc_base/refcountedobject.h"
#include "media/base/videocapturer.h"
#include "common_types.h"
#include "modules/video_coding/include/video_coding_defines.h"
#include "api/video_codecs/video_encoder.h"
namespace offcn
{
const size_t EncoderSetting::kMaxNumberOfStreams;
const int EncoderSetting::kMaxBitratePerStream[] = { 150 * 1000, 450 * 1000, 1500 * 1000 };
const int EncoderSetting::kDefaultMinBitratePerStream[] = { 30 * 1000, 200 * 1000, 700 * 1000 };
std::vector<webrtc::VideoStream> CreateVideoStream(int width, int height, const webrtc::VideoEncoderConfig& encoder_config)
{
std::vector<webrtc::VideoStream> stream_settings(encoder_config.number_of_streams);
int bitrate_left_bps = encoder_config.max_bitrate_bps;
for (size_t i = 0; i < encoder_config.number_of_streams; ++i)
{
stream_settings[i].width = (i + 1) * width / encoder_config.number_of_streams;
stream_settings[i].height = (i + 1) * height / encoder_config.number_of_streams;
stream_settings[i].max_framerate = 30;
stream_settings[i].min_bitrate_bps = EncoderSetting::kDefaultMinBitratePerStream[i];
int target_bitrate_bps = -1;
int max_bitrate_bps = -1;
// Use configured values instead of default values if values has been
// configured.
if (i < encoder_config.simulcast_layers.size())
{
const webrtc::VideoStream &stream = encoder_config.simulcast_layers[i];
max_bitrate_bps = stream.max_bitrate_bps > 0 ? stream.max_bitrate_bps : EncoderSetting::kMaxBitratePerStream[i];
max_bitrate_bps = std::min(bitrate_left_bps, max_bitrate_bps);
target_bitrate_bps = stream.target_bitrate_bps > 0 ? stream.target_bitrate_bps : EncoderSetting::kMaxBitratePerStream[i];
target_bitrate_bps = std::min(max_bitrate_bps, target_bitrate_bps);
}
else
{
max_bitrate_bps = std::min(bitrate_left_bps, EncoderSetting::kMaxBitratePerStream[i]);
target_bitrate_bps = max_bitrate_bps;
}
RTC_DCHECK_NE(target_bitrate_bps, -1);
RTC_DCHECK_NE(max_bitrate_bps, -1);
stream_settings[i].target_bitrate_bps = target_bitrate_bps;
stream_settings[i].max_bitrate_bps = max_bitrate_bps;
stream_settings[i].max_qp = 56;
if (i < encoder_config.simulcast_layers.size())
{
// Higher level controls are setting the active configuration for the
// VideoStream.
stream_settings[i].active = encoder_config.simulcast_layers[i].active;
}
else
{
stream_settings[i].active = true;
}
bitrate_left_bps -= stream_settings[i].target_bitrate_bps;
}
stream_settings[encoder_config.number_of_streams - 1].max_bitrate_bps += bitrate_left_bps;
stream_settings[0].bitrate_priority = encoder_config.bitrate_priority;
return stream_settings;
}
void FillEncoderConfiguration(webrtc::VideoCodecType codec_type, size_t num_streams, webrtc::VideoEncoderConfig* configuration)
{
configuration->codec_type = codec_type;
configuration->number_of_streams = num_streams;
configuration->video_stream_factory = new rtc::RefCountedObject<EncoderSetting>();
configuration->max_bitrate_bps = 0;
configuration->simulcast_layers = configuration->video_stream_factory.get()->CreateEncoderStreams(1280, 720, *configuration);
//std::vector<webrtc::VideoStream>(configuratio);
for (size_t i = 0; i < num_streams; ++i)
{
configuration->max_bitrate_bps += EncoderSetting::kMaxBitratePerStream[i];
}
}
std::vector<webrtc::VideoStream>
EncoderSetting::CreateEncoderStreams(int width, int height, const webrtc::VideoEncoderConfig &encoder_config)
{
return CreateVideoStream(width, height, encoder_config);
}
std::unique_ptr<cricket::VideoCapturer> OpenVideoCaptureDevice()
{
std::vector<std::string> device_names;
{
std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> info(webrtc::VideoCaptureFactory::CreateDeviceInfo());
if (!info)
{
return nullptr;
}
int num_devices = info->NumberOfDevices();
for (int i = 0; i < num_devices; ++i)
{
const uint32_t kSize = 256;
char name[kSize] = { 0 };
char id[kSize] = { 0 };
if (info->GetDeviceName(i, name, kSize, id, kSize) != -1)
{
device_names.push_back(name);
}
}
}
cricket::WebRtcVideoDeviceCapturerFactory factory;
std::unique_ptr<cricket::VideoCapturer> capturer;
for (const auto& name : device_names)
{
capturer = factory.Create(cricket::Device(name, 0));
if (capturer)
{
break;
}
}
return capturer;
}
void EncoderSetting::GetVideoCodecObj(webrtc::VideoCodec &codec, webrtc::VideoCodecType type)
{
memset(&codec, 0, sizeof(codec));
codec.mode = webrtc::VideoCodecMode::kRealtimeVideo;
codec.plType = 0;
codec.numberOfSimulcastStreams = 1;
codec.active = true;
codec.timing_frame_thresholds = { webrtc::kDefaultTimingFramesDelayMs, webrtc::kDefaultOutlierFrameSizePercent };
codec.width = 1280;
codec.height = 720;
codec.minBitrate = 30 * 1000 / 1000;
codec.maxBitrate = 500 * 1000 / 1000;
codec.qpMax = 56;
webrtc::SimulcastStream *sim_stream = &codec.simulcastStream[0];
sim_stream->width = 1280;
sim_stream->height = 720;
sim_stream->maxFramerate = 60;
sim_stream->minBitrate = codec.minBitrate;
sim_stream->maxBitrate = codec.maxBitrate;
sim_stream->targetBitrate = 300 * 1000 / 1000;
sim_stream->qpMax = codec.qpMax;
sim_stream->numberOfTemporalLayers = 1;
sim_stream->active = true;
codec.codecType = webrtc::kVideoCodecH264;
*codec.H264() = webrtc::VideoEncoder::GetDefaultH264Settings();
}
};
\ No newline at end of file
#pragma once
#include <winsock2.h>
#include <windows.h>
#include "call/video_receive_stream.h"
#include "call/video_send_stream.h"
#include "media/engine/webrtcvideocapturerfactory.h"
#include "modules/video_capture/video_capture_factory.h"
namespace offcn {
class EncoderSetting : public webrtc::VideoEncoderConfig::VideoStreamFactoryInterface
{
public:
static const size_t kMaxNumberOfStreams = 1;
static const int kMaxBitratePerStream[];
static const int kDefaultMinBitratePerStream[];
static void GetVideoCodecObj(webrtc::VideoCodec &codec, webrtc::VideoCodecType type);
static void GetVideoEncoderConfig(webrtc::VideoEncoderConfig &config, webrtc::VideoCodec &codec);
public:
std::vector<webrtc::VideoStream> CreateEncoderStreams(int width, int height, const webrtc::VideoEncoderConfig &encoder_config);
};
void FillEncoderConfiguration(webrtc::VideoCodecType codec_type, size_t num_streams, webrtc::VideoEncoderConfig* configuration);
std::unique_ptr<cricket::VideoCapturer> OpenVideoCaptureDevice();
}
\ No newline at end of file
#include "libmediacore.h"
#include "client_manager.h"
namespace offcn
{
static ClientManager *client_manager_ = NULL;
ZG_C_API void Initialize(GlobalObserver *observer)
{
if (client_manager_ == NULL)
{
client_manager_ = new ClientManager(observer);
client_manager_->Init();
}
}
ZG_C_API void UnInitialize()
{}
ZG_C_API void SetParam(const char *param)
{}
ZG_C_API int CreateRoleHandle(const char *param, ChannelObserver *observer, ZRoleType type)
{
if (type == Z_ROLE_TYPE_PUBLISH)
{
return client_manager_->CreatePushHandle(param, observer);
}
else
{
return client_manager_->CreatePullHandle(param, observer);
}
return 0;
}
ZG_C_API void DestroyRoleHandle(int handle, ZRoleType type)
{
if (type == Z_ROLE_TYPE_PUBLISH)
{
client_manager_->DestroyPushHandle(handle);
}
else
{
client_manager_->DestroyPullHandle(handle);
}
}
ZG_C_API void StartPublish(int handle)
{
client_manager_->StartPush(handle);
}
ZG_C_API void StopPublish(int handle)
{
client_manager_->StopPush(handle);
}
ZG_C_API void StartSubscribe(int handle)
{
client_manager_->StartPull(handle);
}
ZG_C_API void StopSubscribe(int handle)
{
client_manager_->StopPull(handle);
}
}
\ No newline at end of file
#pragma once
#include "ztypes.h"
#include "rtc_base/messagequeue.h"
class ZMessageDataInt: virtual public rtc::MessageData
{
public:
int int_data_;
};
class ZMessageDataString : virtual public rtc::MessageData
{
public:
std::string string_data_;
};
class ZMsgDataHandle : virtual public ZMessageDataInt,
virtual public ZMessageDataString
{
public:
ChannelObserver *observer_;
};
class ZMsgDataNetData : virtual public rtc::MessageData
{
public:
SOCKET socket;
std::string ip;
int port;
int handle;
char buffer[1500];
int size;
};
\ No newline at end of file
#include "net_data_receive.h"
NetDataReceive::NetDataReceive(NetDataReceiveObserver *observer)
:observer_(observer)
{
all_connections_.clear();
}
NetDataReceive::~NetDataReceive()
{}
bool NetDataReceive::Start()
{
thread_running_ = true;
recv_thread_ = std::thread(NetDataReceive::RecvThreadCallBack, this);
return true;
}
bool NetDataReceive::Stop()
{
thread_running_ = false;
if(recv_thread_.joinable())
{
recv_thread_.join();
}
return true;
}
void NetDataReceive::RecvThreadCallBack(void *obj)
{
NetDataReceive *pThis = (NetDataReceive *)obj;
pThis->RecvThread();
}
void NetDataReceive::RecvThread()
{
struct timeval tv = {0, 50 * 1000};
while(thread_running_)
{
fd_set read_fds;
FD_ZERO(&read_fds);
if (all_connections_.size() <= 0)
{
Sleep(10);
continue;
}
ConnectionMapType::const_iterator itor;
//mutex_connections_.lock();
{
std::unique_lock<std::mutex> lck(mutex_connections_);
for (itor = all_connections_.begin(); itor != all_connections_.end(); itor++)
{
FD_SET(itor->first, &read_fds);
}
}
int nRet = select(0, &read_fds, NULL, NULL, &tv);
if (nRet > 0)
{
{
std::unique_lock<std::mutex> lck(mutex_connections_);
for (itor = all_connections_.begin(); itor != all_connections_.end(); itor++)
{
if (FD_ISSET(itor->first, &read_fds))
{
struct sockaddr_in client;
int addr_len = sizeof(client);
char szBuffer[1500] = { 0 };
int len = recvfrom(itor->first, szBuffer, 1500, 0, (SOCKADDR*)&client, &addr_len);
int nPort = ntohs(client.sin_port);
char *ip = inet_ntoa(client.sin_addr);
if (len > 0)
{
if (observer_)
{
observer_->NetDataReceiveGetData(itor->first, itor->second, std::string(ip), nPort, szBuffer, len);
}
}
else
{
DWORD dw = GetLastError();
int i = 0;
}
}
}
}
}
//mutex_connections_.unlock();
}
}
bool NetDataReceive::AddConnection(SOCKET socket, int handle)
{
char szBuffer[256] = { 0 };
sprintf_s(szBuffer, 256, "add connection socket = %d", socket);
OutputDebugStringA(szBuffer);
//mutex_connections_.lock();
std::unique_lock<std::mutex> lck(mutex_connections_);
all_connections_.insert(std::pair<SOCKET, int>(socket, handle));
//mutex_connections_.unlock();
return true;
}
bool NetDataReceive::RemoveConnection(SOCKET socket)
{
ConnectionMapType::const_iterator itor;
//mutex_connections_.lock();
std::unique_lock<std::mutex> lck(mutex_connections_);
itor = all_connections_.find(socket);
if(itor != all_connections_.end())
{
all_connections_.erase(itor);
}
//mutex_connections_.unlock();
return true;
}
\ No newline at end of file
#pragma once
#include <WinSock2.h>
#include <thread>
#include <mutex>
#include <map>
class NetDataReceiveObserver
{
public:
virtual void NetDataReceiveGetData(SOCKET socket, int handle_id, std::string ip, int port, const char *buffer, int size) = 0;
};
class NetDataReceive
{
public:
NetDataReceive(NetDataReceiveObserver *observer);
~NetDataReceive();
bool Start();
bool Stop();
bool AddConnection(SOCKET socket, int handle);
bool RemoveConnection(SOCKET socket);
private:
static void RecvThreadCallBack(void *obj);
void RecvThread();
private:
bool thread_running_;
std::thread recv_thread_;
private:
std::mutex mutex_connections_;
typedef std::map<SOCKET, int> ConnectionMapType;
ConnectionMapType all_connections_;
private:
NetDataReceiveObserver *observer_;
};
\ No newline at end of file
#include "offcn_call.h"
#include "media/engine/adm_helpers.h"
#include "media/engine/apm_helpers.h"
#include "modules/audio_processing/audio_processing_impl.h"
#include "modules/audio_mixer/audio_mixer_impl.h"
#include "api/audio_codecs/audio_encoder_factory_template.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "encoder_setting.h"
#include <iostream>
static void FillEncoderConfiguration(webrtc::VideoCodecType codec_type, size_t num_streams, webrtc::VideoEncoderConfig *configuration)
{
configuration->codec_type = codec_type;
configuration->number_of_streams = num_streams;
configuration->video_stream_factory = new rtc::RefCountedObject<offcn::EncoderSetting>();
configuration->max_bitrate_bps = 0;
configuration->simulcast_layers = std::vector<webrtc::VideoStream>(num_streams);
for (size_t i = 0; i < num_streams; ++i)
{
configuration->max_bitrate_bps += offcn::EncoderSetting::kMaxBitratePerStream[i];
}
}
int OffcnCall::GetMicroPhoneIndexByName(rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_device, const char *name)
{
int num = audio_device->RecordingDevices();
if(num <= 0)
{
return -1;
}
if(name == NULL || strlen(name) == 0)
{
return 0;
}
for (int i = 0; i < num; i++)
{
char tmp_name[128] = { 0 };
char tmp_id[128] = { 0 };
if (audio_device->RecordingDeviceName(i, tmp_name, tmp_id) < 0)
{
continue;
}
if (strcmp(tmp_name, name) == 0)
{
return i;
}
}
return -1;
}
int OffcnCall::GetPlayoutIndexByName(rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_device, const char *name)
{
int num = audio_device->PlayoutDevices();
if (num <= 0)
{
return -1;
}
if(name == NULL || strlen(name) == 0)
{
return 0;
}
for (int i = 0; i < num; i++)
{
char tmp_name[128] = { 0 };
char tmp_id[128] = { 0 };
if (audio_device->PlayoutDeviceName(i, tmp_name, tmp_id) < 0)
{
continue;
}
if (strcmp(tmp_name, name) == 0)
{
return i;
}
}
return -1;
}
OffcnCall::OffcnCall()
{}
OffcnCall::~OffcnCall()
{
if(call_)
{
call_.reset();
}
}
bool OffcnCall::CreateCall(const char *playout_dev_name, const char *recorder_dev_name)
{
//audio device module
rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_device_module = CreateAudioModule(playout_dev_name, recorder_dev_name);
if(audio_device_module == nullptr)
{
return false;
}
//audio process
webrtc::Config apm_config;
webrtc::AudioProcessing *audio_processing = new rtc::RefCountedObject<webrtc::AudioProcessingImpl>(apm_config, nullptr, nullptr, nullptr, nullptr);
if(audio_processing == nullptr)
{
return false;
}
if(audio_processing->Initialize() != webrtc::AudioProcessing::kNoError)
{
delete audio_processing;
audio_processing = nullptr;
return false;
}
//audio mixer
rtc::scoped_refptr<webrtc::AudioMixer> audio_mixer = webrtc::AudioMixerImpl::Create();
if(audio_mixer == nullptr)
{
return false;
}
//audio state
webrtc::AudioState::Config audio_state_config;
audio_state_config.audio_device_module = audio_device_module;
audio_state_config.audio_processing = audio_processing;
audio_state_config.audio_mixer = audio_mixer;
rtc::scoped_refptr<webrtc::AudioState> audio_state = webrtc::AudioState::Create(audio_state_config);
if(audio_state == nullptr)
{
return false;
}
//call
webrtc::Call::Config call_config(&event_log_);
call_config.audio_state = audio_state;
audio_device_module->RegisterAudioCallback(audio_state->audio_transport());
call_ = absl::WrapUnique(webrtc::Call::Create(call_config));
if(call_ == nullptr)
{
return false;
}
return true;
}
rtc::scoped_refptr<webrtc::AudioDeviceModule> OffcnCall::CreateAudioModule(const char *playout_dev_name, const char *recorder_dev_name)
{
rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_module = webrtc::AudioDeviceModule::Create(webrtc::AudioDeviceModule::kPlatformDefaultAudio);
if(audio_module == nullptr)
{
return nullptr;
}
webrtc::adm_helpers::Init(audio_module);
int microphone_index = OffcnCall::GetMicroPhoneIndexByName(audio_module, recorder_dev_name);
int playout_index = OffcnCall::GetPlayoutIndexByName(audio_module, playout_dev_name);
if(microphone_index < 0 || playout_index < 0)
{
return nullptr;
}
int nRet = audio_module->SetRecordingDevice(microphone_index);
if(nRet != 0)
{
return nullptr;
}
nRet = audio_module->SetPlayoutDevice(playout_index);
if(nRet != 0)
{
return nullptr;
}
return audio_module;
}
webrtc::AudioSendStream *OffcnCall::CreateAudioSendStream(webrtc::Transport *transport, const char *cname)
{
webrtc::AudioSendStream::Config config(transport);
config.rtp.c_name = cname;
config.rtp.nack.rtp_history_ms = 500;
config.rtp.ssrc = 110;
config.send_codec_spec = webrtc::AudioSendStream::Config::SendCodecSpec(110, { "opus", 48000, 2, {{"stereo", "1"}} });
config.encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
webrtc::AudioSendStream *audio_stream = call_->CreateAudioSendStream(config);
if(audio_stream == nullptr)
{
return nullptr;
}
return audio_stream;
return nullptr;
}
webrtc::VideoSendStream *OffcnCall::CreateVideoSendStream(webrtc::VideoEncoderFactory *video_encoder_factory, webrtc::Transport *transport, const char *cname)
{
webrtc::VideoSendStream::Config config(transport);
config.rtp.ssrcs.push_back(112);
config.rtp.nack.rtp_history_ms = 500;
config.rtp.payload_name = "VP8";
config.rtp.payload_type = 112;
config.rtp.c_name = cname;
config.encoder_settings.encoder_factory = video_encoder_factory;
webrtc::VideoEncoderConfig encoder_config;
FillEncoderConfiguration(webrtc::kVideoCodecVP8, 1, &encoder_config);
encoder_config.max_bitrate_bps = 100 * 1000;
encoder_config.video_format.name = "VP8";
webrtc::VideoSendStream *video_stream = call_->CreateVideoSendStream(config.Copy(), encoder_config.Copy());
if (video_stream == nullptr)
{
return nullptr;
}
return video_stream;
}
webrtc::AudioReceiveStream *OffcnCall::CreateAudioReceiveStream(webrtc::Transport *transport)
{
webrtc::AudioReceiveStream::Config config;
config.rtp.remote_ssrc = 110;
config.rtp.local_ssrc = 114;
config.decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
config.decoder_map = {{110, {"opus", 48000, 2}}};
config.rtcp_send_transport = transport;
return call_->CreateAudioReceiveStream(config);
}
webrtc::VideoReceiveStream *OffcnCall::CreateVideoReceiveStream(rtc::VideoSinkInterface<webrtc::VideoFrame> *videosink, webrtc::Transport *transport)
{
webrtc::SdpVideoFormat format("VP8");
std::unique_ptr<webrtc::VideoDecoderFactory> video_decoder_factory = webrtc::CreateBuiltinVideoDecoderFactory();
video_decoder_ = video_decoder_factory->CreateVideoDecoder(format);
webrtc::VideoReceiveStream::Config config(transport);
config.renderer = videosink;//new FakeRenderer();
config.rtp.local_ssrc = 911;
config.rtp.remote_ssrc = 112;
webrtc::VideoReceiveStream::Decoder decoder;
decoder.decoder = video_decoder_.get();
decoder.payload_name = "VP8";
decoder.payload_type = 112;
config.decoders.push_back(decoder);
return call_->CreateVideoReceiveStream(config.Copy());
}
\ No newline at end of file
#pragma once
#include "call/call.h"
#include "logging/rtc_event_log/rtc_event_log.h"
#include "call/audio_send_stream.h"
#include "call/video_send_stream.h"
#include "api/video_codecs/video_decoder.h"
class OffcnCall
{
public:
OffcnCall();
~OffcnCall();
static int GetMicroPhoneIndexByName(rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_device, const char *name);
static int GetPlayoutIndexByName(rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_device, const char *name);
/**
* Create call object
*
* @param playout_dev_name audio play device name, utf8
* @param recorder_dev_name audio capture device name, utf8
*
* @return true - ok, false - not ok
*/
bool CreateCall(const char *playout_dev_name, const char *recorder_dev_name);
/**
* Return call object pointer
*/
webrtc::Call *GetCall(){ return call_.get(); }
webrtc::AudioSendStream *CreateAudioSendStream(webrtc::Transport *transport, const char *cname);
webrtc::VideoSendStream *CreateVideoSendStream(webrtc::VideoEncoderFactory *video_encoder_factory, webrtc::Transport *transport, const char *cname);
webrtc::AudioReceiveStream *CreateAudioReceiveStream(webrtc::Transport *transport);
webrtc::VideoReceiveStream *CreateVideoReceiveStream(rtc::VideoSinkInterface<webrtc::VideoFrame> *videosink, webrtc::Transport *transport);
private:
rtc::scoped_refptr<webrtc::AudioDeviceModule> CreateAudioModule(const char *playout_dev_name, const char *recorder_dev_name);
private:
webrtc::RtcEventLogNullImpl event_log_;
std::unique_ptr<webrtc::Call> call_;
std::unique_ptr<webrtc::VideoDecoder> video_decoder_;
};
\ No newline at end of file
#include "rtprtcp_transport.h"
RtpRtcpTransport::RtpRtcpTransport(RtpRtcpTransportObserver *observer)
:observer_(observer)
{
}
RtpRtcpTransport::~RtpRtcpTransport()
{
}
bool RtpRtcpTransport::SendRtp(const uint8_t *data, size_t length, const webrtc::PacketOptions &options)
{
if (observer_)
{
return observer_->SendRtpRtcpData(this, data, length, false);
}
return false;
}
bool RtpRtcpTransport::SendRtcp(const uint8_t *data, size_t length)
{
if (observer_)
{
return observer_->SendRtpRtcpData(this, data, length, true);
}
return false;
}
\ No newline at end of file
#pragma once
#include "api/call/transport.h"
class RtpRtcpTransport;
class RtpRtcpTransportObserver
{
public:
virtual bool SendRtpRtcpData(RtpRtcpTransport *transport, const uint8_t *data, size_t length, bool is_rtcp) = 0;
};
class RtpRtcpTransport : public webrtc::Transport
{
public:
RtpRtcpTransport(RtpRtcpTransportObserver *observer);
~RtpRtcpTransport();
bool SendRtp(const uint8_t *data, size_t length, const webrtc::PacketOptions &options);
bool SendRtcp(const uint8_t *data, size_t length);
private:
RtpRtcpTransportObserver *observer_;
};
\ No newline at end of file
#include "video_render_ex.h"
#include "api/video/i420_buffer.h"
#include "third_party/libyuv/include/libyuv/convert_argb.h"
CVideoRenderEx::CVideoRenderEx(webrtc::VideoTrackInterface *track, CVideoRenderObserver *observer)
:render_track_(track),
observer_(observer)
{
AddVideoSink();
}
CVideoRenderEx::~CVideoRenderEx()
{
}
void CVideoRenderEx::AddVideoSink()
{
if (render_track_)
{
render_track_->AddOrUpdateSink(this, rtc::VideoSinkWants());
}
}
void CVideoRenderEx::RemoveVideoSink()
{
if (render_track_)
{
render_track_->RemoveSink(this);
}
render_track_ = nullptr;
}
void CVideoRenderEx::OnFrame(const webrtc::VideoFrame& video_frame)
{
rtc::scoped_refptr<webrtc::I420BufferInterface> buffer(video_frame.video_frame_buffer()->ToI420());
if (video_frame.rotation() != webrtc::kVideoRotation_0)
{
buffer = webrtc::I420Buffer::Rotate(*buffer, video_frame.rotation());
}
//Play 'buffer'
if (observer_)
{
int w = buffer->width();
int h = buffer->height();
int y = buffer->StrideY();
int u = buffer->StrideU();
int v = buffer->StrideV();
observer_->OnVideoFrame(buffer->DataY(), buffer->StrideY(), buffer->DataU(), buffer->StrideU(), buffer->DataV(), buffer->StrideV(), buffer->width(), buffer->height());
}
}
\ No newline at end of file
#ifndef __VIDEO_RENDER_EX_H__
#define __VIDEO_RENDER_EX_H__
#include "api/mediastreaminterface.h"
#include "api/video/video_frame.h"
class CVideoRenderObserver
{
public:
virtual void OnVideoFrame(const unsigned char *pY, int yPitch, const unsigned char *pU, int uPitch, const unsigned char *pV, int vPitch, int width, int height) = 0;
};
class CVideoRenderEx : public rtc::VideoSinkInterface<webrtc::VideoFrame>
{
public:
CVideoRenderEx(webrtc::VideoTrackInterface *track, CVideoRenderObserver *observer);
virtual ~CVideoRenderEx();
public:
/**
* Add self to video track
*/
void AddVideoSink();
/**
* Remove self from video track
*/
void RemoveVideoSink();
public:
//
// VideoSinkInterface implementation
//
void OnFrame(const webrtc::VideoFrame& video_frame) override;
private:
rtc::scoped_refptr<webrtc::VideoTrackInterface> render_track_;
private:
CVideoRenderObserver *observer_;
};
#endif
\ No newline at end of file

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2046
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mediacoredemo", "demo\mediacoredemo\mediacoredemo.vcxproj", "{A1837C4E-381B-4696-B7CE-777423E5D84F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mediacore", "mediacore\build\mediacore.vcxproj", "{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Debug|x64.ActiveCfg = Debug|x64
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Debug|x64.Build.0 = Debug|x64
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Debug|x86.ActiveCfg = Debug|Win32
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Debug|x86.Build.0 = Debug|Win32
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Release|x64.ActiveCfg = Release|x64
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Release|x64.Build.0 = Release|x64
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Release|x86.ActiveCfg = Release|Win32
{A1837C4E-381B-4696-B7CE-777423E5D84F}.Release|x86.Build.0 = Release|Win32
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Debug|x64.ActiveCfg = Debug|x64
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Debug|x64.Build.0 = Debug|x64
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Debug|x86.ActiveCfg = Debug|Win32
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Debug|x86.Build.0 = Debug|Win32
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Release|x64.ActiveCfg = Release|x64
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Release|x64.Build.0 = Release|x64
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Release|x86.ActiveCfg = Release|Win32
{08E33C0C-B4FE-4162-BB45-1B73FBC3256C}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D49209B-53B5-491D-9AB8-81AA0F2D51F9}
EndGlobalSection
EndGlobal
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED
#define CPPTL_JSON_ALLOCATOR_H_INCLUDED
#include <cstring>
#include <memory>
#pragma pack(push, 8)
namespace Json {
template <typename T> class SecureAllocator {
public:
// Type definitions
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
/**
* Allocate memory for N items using the standard allocator.
*/
pointer allocate(size_type n) {
// allocate using "global operator new"
return static_cast<pointer>(::operator new(n * sizeof(T)));
}
/**
* Release memory which was allocated for N items at pointer P.
*
* The memory block is filled with zeroes before being released.
* The pointer argument is tagged as "volatile" to prevent the
* compiler optimizing out this critical step.
*/
void deallocate(volatile pointer p, size_type n) {
std::memset(p, 0, n * sizeof(T));
// free using "global operator delete"
::operator delete(p);
}
/**
* Construct an item in-place at pointer P.
*/
template <typename... Args> void construct(pointer p, Args&&... args) {
// construct using "placement new" and "perfect forwarding"
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}
size_type max_size() const { return size_t(-1) / sizeof(T); }
pointer address(reference x) const { return std::addressof(x); }
const_pointer address(const_reference x) const { return std::addressof(x); }
/**
* Destroy an item in-place at pointer P.
*/
void destroy(pointer p) {
// destroy using "explicit destructor"
p->~T();
}
// Boilerplate
SecureAllocator() {}
template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
template <typename U> struct rebind { using other = SecureAllocator<U>; };
};
template <typename T, typename U>
bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return true;
}
template <typename T, typename U>
bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return false;
}
} // namespace Json
#pragma pack(pop)
#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
#include <sstream>
#include <stdlib.h>
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
/** It should not be possible for a maliciously designed file to
* cause an abort() or seg-fault, so these macros are used only
* for pre-condition violations and internal logic errors.
*/
#if JSON_USE_EXCEPTION
// @todo <= add detail about condition in exception
#define JSON_ASSERT(condition) \
{ \
if (!(condition)) { \
Json::throwLogicError("assert json failed"); \
} \
}
#define JSON_FAIL_MESSAGE(message) \
{ \
JSONCPP_OSTRINGSTREAM oss; \
oss << message; \
Json::throwLogicError(oss.str()); \
abort(); \
}
#else // JSON_USE_EXCEPTION
#define JSON_ASSERT(condition) assert(condition)
// The call to assert() will show the failure message in debug builds. In
// release builds we abort, for a core-dump or debugger.
#define JSON_FAIL_MESSAGE(message) \
{ \
JSONCPP_OSTRINGSTREAM oss; \
oss << message; \
assert(false && oss.str().c_str()); \
abort(); \
}
#endif
#define JSON_ASSERT_MESSAGE(condition, message) \
if (!(condition)) { \
JSON_FAIL_MESSAGE(message); \
}
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_AUTOLINK_H_INCLUDED
#define JSON_AUTOLINK_H_INCLUDED
#include "config.h"
#ifdef JSON_IN_CPPTL
#include <cpptl/cpptl_autolink.h>
#endif
#if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && \
!defined(JSON_IN_CPPTL)
#define CPPTL_AUTOLINK_NAME "json"
#undef CPPTL_AUTOLINK_DLL
#ifdef JSON_DLL
#define CPPTL_AUTOLINK_DLL
#endif
#include "autolink.h"
#endif
#endif // JSON_AUTOLINK_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <stddef.h>
#include <stdint.h> //typedef int64_t, uint64_t
#include <string> //typedef String
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
#if defined(_MSC_VER) // MSVC
#if _MSC_VER <= 1200 // MSVC 6
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering
// with pragma push/pop).
#pragma warning(disable : 4786)
#endif // MSVC 6
#if _MSC_VER >= 1500 // MSVC 2008
/// Indicates that the following function is deprecated.
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#endif
#endif // defined(_MSC_VER)
// In c++11 the override keyword allows you to explicitly define that a function
// is intended to override the base-class version. This makes the code more
// manageable and fixes a set of common hard-to-find bugs.
#if __cplusplus >= 201103L
#define JSONCPP_OVERRIDE override
#define JSONCPP_NOEXCEPT noexcept
#define JSONCPP_OP_EXPLICIT explicit
#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
#define JSONCPP_OVERRIDE override
#define JSONCPP_NOEXCEPT throw()
#if _MSC_VER >= 1800 // MSVC 2013
#define JSONCPP_OP_EXPLICIT explicit
#else
#define JSONCPP_OP_EXPLICIT
#endif
#elif defined(_MSC_VER) && _MSC_VER >= 1900
#define JSONCPP_OVERRIDE override
#define JSONCPP_NOEXCEPT noexcept
#define JSONCPP_OP_EXPLICIT explicit
#else
#define JSONCPP_OVERRIDE
#define JSONCPP_NOEXCEPT throw()
#define JSONCPP_OP_EXPLICIT
#endif
#ifndef JSON_HAS_RVALUE_REFERENCES
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // MSVC >= 2010
#ifdef __clang__
#if __has_feature(cxx_rvalue_references)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // has_feature
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // GXX_EXPERIMENTAL
#endif // __clang__ || __GNUC__
#endif // not defined JSON_HAS_RVALUE_REFERENCES
#ifndef JSON_HAS_RVALUE_REFERENCES
#define JSON_HAS_RVALUE_REFERENCES 0
#endif
#ifdef __clang__
#if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif // GNUC version
#endif // __clang__ || __GNUC__
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if __GNUC__ >= 6
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
#include "version.h"
#if JSONCPP_USING_SECURE_MEMORY
#include "allocator.h" //typedef Allocator
#endif
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef int64_t Int64;
typedef uint64_t UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
#if JSONCPP_USING_SECURE_MEMORY
#define JSONCPP_STRING \
std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
#define JSONCPP_OSTRINGSTREAM \
std::basic_ostringstream<char, std::char_traits<char>, \
Json::SecureAllocator<char> >
#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char> >
#define JSONCPP_ISTRINGSTREAM \
std::basic_istringstream<char, std::char_traits<char>, \
Json::SecureAllocator<char> >
#define JSONCPP_ISTREAM std::istream
#else
#define JSONCPP_STRING std::string
#define JSONCPP_OSTRINGSTREAM std::ostringstream
#define JSONCPP_OSTREAM std::ostream
#define JSONCPP_ISTRINGSTREAM std::istringstream
#define JSONCPP_ISTREAM std::istream
#endif // if JSONCPP_USING_SECURE_MEMORY
} // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
#define CPPTL_JSON_FEATURES_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#pragma pack(push, 8)
namespace Json {
/** \brief Configuration passed to reader and writer.
* This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way.
*/
class JSON_API Features {
public:
/** \brief A configuration that allows all features and assumes all strings
* are UTF-8.
* - C & C++ comments are allowed
* - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8
*/
static Features all();
/** \brief A configuration that is strictly compatible with the JSON
* specification.
* - Comments are forbidden.
* - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8
*/
static Features strictMode();
/** \brief Initialize the configuration like JsonConfig::allFeatures;
*/
Features();
/// \c true if comments are allowed. Default: \c true.
bool allowComments_;
/// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_;
/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_;
/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_;
};
} // namespace Json
#pragma pack(pop)
#endif // CPPTL_JSON_FEATURES_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_JSON_H_INCLUDED
#define JSON_JSON_H_INCLUDED
#include "autolink.h"
#include "features.h"
#include "reader.h"
#include "value.h"
#include "writer.h"
#endif // JSON_JSON_H_INCLUDED
// DO NOT EDIT. This file (and "version") is generated by CMake.
// Run CMake configure step to update it.
#ifndef JSON_VERSION_H_INCLUDED
#define JSON_VERSION_H_INCLUDED
#define JSONCPP_VERSION_STRING "1.8.4"
#define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 8
#define JSONCPP_VERSION_PATCH 4
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
(JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY
#undef JSONCPP_USING_SECURE_MEMORY
#endif
#define JSONCPP_USING_SECURE_MEMORY 0
// If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory.
#endif // JSON_VERSION_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include <json/config.h>
#endif
// Also support old flag NO_LOCALE_SUPPORT
#ifdef NO_LOCALE_SUPPORT
#define JSONCPP_NO_LOCALE_SUPPORT
#endif
#ifndef JSONCPP_NO_LOCALE_SUPPORT
#include <clocale>
#endif
/* This header provides common string manipulation support, such as UTF-8,
* portable conversion from/to string...
*
* It is an internal header that must not be exposed.
*/
namespace Json {
static inline char getDecimalPoint() {
#ifdef JSONCPP_NO_LOCALE_SUPPORT
return '\0';
#else
struct lconv* lc = localeconv();
return lc ? *(lc->decimal_point) : '\0';
#endif
}
/// Converts a unicode code-point to UTF-8.
static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) {
JSONCPP_STRING result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f) {
result.resize(1);
result[0] = static_cast<char>(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
enum {
/// Constant that specify the size of the buffer that must be passed to
/// uintToString.
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
// Defines a char buffer for use with uintToString().
typedef char UIntToStringBuffer[uintToStringBufferSize];
/** Converts an unsigned integer to string.
* @param value Unsigned integer to convert to string
* @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free.
*/
static inline void uintToString(LargestUInt value, char*& current) {
*--current = 0;
do {
*--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
value /= 10;
} while (value != 0);
}
/** Change ',' to '.' everywhere in buffer.
*
* We had a sophisticated way, but it did not work in WinCE.
* @see https://github.com/open-source-parsers/jsoncpp/pull/9
*/
template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
for (; begin != end; ++begin) {
if (*begin == ',') {
*begin = '.';
}
}
return begin;
}
template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
char decimalPoint = getDecimalPoint();
if (decimalPoint == '\0' || decimalPoint == '.') {
return;
}
for (; begin != end; ++begin) {
if (*begin == '.') {
*begin = decimalPoint;
}
}
}
/**
* Return iterator that would be the new end of the range [begin,end), if we
* were to delete zeros in the end of string, but not the last zero before '.'.
*/
template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) {
for (; begin != end; --end) {
if (*(end - 1) != '0') {
return end;
}
// Don't delete the last zero before the decimal point.
if (begin != (end - 1) && *(end - 2) == '.') {
return end;
}
}
return end;
}
} // namespace Json
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
// included by json_value.cpp
namespace Json {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIteratorBase::ValueIteratorBase()
: current_(), isNull_(true) {
}
ValueIteratorBase::ValueIteratorBase(
const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {}
Value& ValueIteratorBase::deref() const {
return current_->second;
}
void ValueIteratorBase::increment() {
++current_;
}
void ValueIteratorBase::decrement() {
--current_;
}
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const {
#ifdef JSON_USE_CPPTL_SMALLMAP
return other.current_ - current_;
#else
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance
// of the default std::map::iterator, they can not be compared.
// To allow this, we handle this comparison specifically.
if (isNull_ && other.isNull_) {
return 0;
}
// Usage of std::distance is not portable (does not compile with Sun Studio 12
// RogueWave STL,
// which is the one used by default).
// Using a portable hand-made version for non random iterator instead:
// return difference_type( std::distance( current_, other.current_ ) );
difference_type myDistance = 0;
for (Value::ObjectValues::iterator it = current_; it != other.current_;
++it) {
++myDistance;
}
return myDistance;
#endif
}
bool ValueIteratorBase::isEqual(const SelfType& other) const {
if (isNull_) {
return other.isNull_;
}
return current_ == other.current_;
}
void ValueIteratorBase::copy(const SelfType& other) {
current_ = other.current_;
isNull_ = other.isNull_;
}
Value ValueIteratorBase::key() const {
const Value::CZString czstring = (*current_).first;
if (czstring.data()) {
if (czstring.isStaticString())
return Value(StaticString(czstring.data()));
return Value(czstring.data(), czstring.data() + czstring.length());
}
return Value(czstring.index());
}
UInt ValueIteratorBase::index() const {
const Value::CZString czstring = (*current_).first;
if (!czstring.data())
return czstring.index();
return Value::UInt(-1);
}
JSONCPP_STRING ValueIteratorBase::name() const {
char const* keey;
char const* end;
keey = memberName(&end);
if (!keey) return JSONCPP_STRING();
return JSONCPP_STRING(keey, end);
}
char const* ValueIteratorBase::memberName() const {
const char* cname = (*current_).first.data();
return cname ? cname : "";
}
char const* ValueIteratorBase::memberName(char const** end) const {
const char* cname = (*current_).first.data();
if (!cname) {
*end = NULL;
return NULL;
}
*end = cname + (*current_).first.length();
return cname;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueConstIterator::ValueConstIterator() {}
ValueConstIterator::ValueConstIterator(
const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueConstIterator::ValueConstIterator(ValueIterator const& other)
: ValueIteratorBase(other) {}
ValueConstIterator& ValueConstIterator::
operator=(const ValueIteratorBase& other) {
copy(other);
return *this;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIterator::ValueIterator() {}
ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueIterator::ValueIterator(const ValueConstIterator& other)
: ValueIteratorBase(other) {
throwRuntimeError("ConstIterator to Iterator should never be allowed.");
}
ValueIterator::ValueIterator(const ValueIterator& other)
: ValueIteratorBase(other) {}
ValueIterator& ValueIterator::operator=(const SelfType& other) {
copy(other);
return *this;
}
} // namespace Json
// DO NOT EDIT. This file (and "version") is generated by CMake.
// Run CMake configure step to update it.
#ifndef JSON_VERSION_H_INCLUDED
# define JSON_VERSION_H_INCLUDED
# define JSONCPP_VERSION_STRING "@JSONCPP_VERSION@"
# define JSONCPP_VERSION_MAJOR @JSONCPP_VERSION_MAJOR@
# define JSONCPP_VERSION_MINOR @JSONCPP_VERSION_MINOR@
# define JSONCPP_VERSION_PATCH @JSONCPP_VERSION_PATCH@
# define JSONCPP_VERSION_QUALIFIER
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY
#undef JSONCPP_USING_SECURE_MEMORY
#endif
#define JSONCPP_USING_SECURE_MEMORY @JSONCPP_USE_SECURE_MEMORY@
// If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory.
#endif // JSON_VERSION_H_INCLUDED
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment