├── .gitignore ├── README.md ├── SConstruct ├── codecs ├── codec_factory.cpp ├── codec_factory.h └── h264 │ ├── h264_decoder.h │ ├── h264_encoder.h │ ├── open_h264_decoder_impl.cpp │ ├── open_h264_decoder_impl.h │ ├── open_h264_encoder_impl.cpp │ └── open_h264_encoder_impl.h ├── json_parser ├── json_parser.cpp └── json_parser.h ├── main.cpp ├── observers ├── create_sdp_observer.cpp ├── create_sdp_observer.h ├── peer_connection_observer.cpp ├── peer_connection_observer.h ├── set_sdp_observer.cpp └── set_sdp_observer.h ├── peer_manager.h ├── peer_manager_imp.cpp └── peer_manager_imp.h /.gitignore: -------------------------------------------------------------------------------- 1 | .sconsign.dblite 2 | *.o 3 | *.swp 4 | cscope.* 5 | CMakeLists.txt.user 6 | build 7 | dbus_*_generated.* 8 | .idea 9 | cscope* 10 | *.swp 11 | *.autosave 12 | error.log 13 | bin 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # H264WebRTC 2 | This project try to integrate OpenH264 as the H264 implementation into Google WebRTC. It takes [Ericsson's signaling server](http://demo.openwebrtc.io:38080/) as the signaling server. 3 | 4 | ## Dependency 5 | * glib-2.0 6 | * libsoup-2.4 7 | * x11 8 | * sigc++-2.0 9 | * jsoncpp 10 | * [openh264](https://github.com/cisco/openh264) 11 | 12 | ## How to build 13 | * Build [webrtc](https://github.com/david7482/webrtcbuilds-builder) first 14 | * It will install the build at /opt/webrtc 15 | * We use [Scons](http://www.scons.org/) as the build system 16 | * Run `scons` then it will build the executable at `bin/` 17 | 18 | ## How to run 19 | * [Setup Firefox](https://github.com/EricssonResearch/openwebrtc/issues/425#issuecomment-119020464) to use H264 in a higher priority 20 | * Start a session from [here](http://demo.openwebrtc.io:38080/) 21 | * Run `./H264WebRTC` with `-s [session id]` 22 | * You could also try `-v` as the verbose mode 23 | * You should get the stream on Firefox. 24 | * Check SDP part of `about:webrtc` in Firefox to make sure if it is running on H264 25 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | debug = ARGUMENTS.get('debug', 0) 4 | webrtc_root_path = ARGUMENTS.get('webrtc-path', '') 5 | 6 | # Set our required libraries 7 | libraries = [] 8 | library_paths = '' 9 | cppDefines = {} 10 | cFlags = ['-Wall'] 11 | 12 | # For debug build 13 | if int(debug): 14 | cFlags += ['-g', '-O0'] 15 | 16 | # Define the attributes of the build environment shared 17 | env = Environment(CPPPATH = ['.']) 18 | env.Append(LIBS = libraries) 19 | env.Append(LIBPATH = library_paths) 20 | env.Append(CPPDEFINES = cppDefines) 21 | env.Append(CFLAGS = cFlags) 22 | env.Append(CPPFLAGS = cFlags) 23 | 24 | WEBRTC_PKG_ROOT_PATH = '/opt/webrtc' 25 | # For debug build 26 | if int(debug): 27 | env['ENV']['PKG_CONFIG_PATH'] = '%s/lib/Debug/pkgconfig' % WEBRTC_PKG_ROOT_PATH 28 | else: 29 | env['ENV']['PKG_CONFIG_PATH'] = '%s/lib/Release/pkgconfig' % WEBRTC_PKG_ROOT_PATH 30 | 31 | env.ParseConfig('pkg-config --cflags --libs glib-2.0') 32 | env.ParseConfig('pkg-config --cflags --libs libsoup-2.4') 33 | env.ParseConfig('pkg-config --cflags --libs --define-variable=prefix=%s libwebrtc_full' % WEBRTC_PKG_ROOT_PATH) 34 | env.ParseConfig('pkg-config --cflags --libs x11') 35 | env.ParseConfig('pkg-config --cflags --libs openh264') 36 | env.ParseConfig('pkg-config --cflags --libs jsoncpp') 37 | env.ParseConfig('pkg-config --cflags --libs sigc++-2.0') 38 | 39 | # Set sources for H264WebRTC 40 | sources = [ 'main.cpp', 41 | 'json_parser/json_parser.cpp', 42 | 'peer_manager_imp.cpp', 43 | 'observers/set_sdp_observer.cpp', 44 | 'observers/peer_connection_observer.cpp', 45 | 'observers/create_sdp_observer.cpp', 46 | 'codecs/codec_factory.cpp', 47 | 'codecs/h264/open_h264_encoder_impl.cpp', 48 | 'codecs/h264/open_h264_decoder_impl.cpp', 49 | ] 50 | 51 | # Build 52 | env.Program('#bin/H264WebRTC', sources) -------------------------------------------------------------------------------- /codecs/codec_factory.cpp: -------------------------------------------------------------------------------- 1 | #include "codec_factory.h" 2 | #include "glib.h" 3 | 4 | #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" 5 | #include "codecs/h264/h264_decoder.h" 6 | #include "codecs/h264/h264_encoder.h" 7 | 8 | namespace h264webrtc 9 | { 10 | 11 | H264EncoderFactory *H264EncoderFactory::Create() 12 | { 13 | return new H264EncoderFactory(); 14 | } 15 | 16 | H264EncoderFactory::H264EncoderFactory() 17 | { 18 | // H264 19 | video_codecs.push_back( 20 | cricket::WebRtcVideoEncoderFactory::VideoCodec(webrtc::kVideoCodecH264, "H264", 1920, 1080, 30)); 21 | 22 | // VP8 23 | video_codecs.push_back( 24 | cricket::WebRtcVideoEncoderFactory::VideoCodec(webrtc::kVideoCodecVP8, "VP8", 1920, 1080, 30)); 25 | } 26 | 27 | webrtc::VideoEncoder *H264EncoderFactory::CreateVideoEncoder(webrtc::VideoCodecType type) 28 | { 29 | g_debug("%s -> type: %d", __FUNCTION__, type); 30 | 31 | webrtc::VideoEncoder *encoder = NULL; 32 | switch (type) { 33 | case webrtc::kVideoCodecVP8: 34 | g_debug("Create VP8 encoder ->", __FUNCTION__); 35 | encoder = webrtc::VP8Encoder::Create(); 36 | g_debug("Create VP8 encoder <-", __FUNCTION__); 37 | break; 38 | case webrtc::kVideoCodecH264: 39 | g_debug("Create H264 encoder ->", __FUNCTION__); 40 | encoder = H264Encoder::Create(); 41 | g_debug("Create H264 encoder ->", __FUNCTION__); 42 | break; 43 | default: 44 | g_debug("%s <- invalid codec type", __FUNCTION__); 45 | break; 46 | } 47 | 48 | g_debug("%s <- encoder = %p", __FUNCTION__, encoder); 49 | return encoder; 50 | } 51 | 52 | void H264EncoderFactory::DestroyVideoEncoder(webrtc::VideoEncoder *encoder) 53 | { 54 | g_debug("%s ->", __FUNCTION__); 55 | delete encoder; 56 | g_debug("%s <-", __FUNCTION__); 57 | } 58 | 59 | const std::vector &H264EncoderFactory::codecs() const 60 | { 61 | return video_codecs; 62 | } 63 | 64 | H264DecoderFactory *H264DecoderFactory::Create() 65 | { 66 | return new H264DecoderFactory(); 67 | } 68 | 69 | H264DecoderFactory::H264DecoderFactory() 70 | { 71 | } 72 | 73 | webrtc::VideoDecoder *H264DecoderFactory::CreateVideoDecoder(webrtc::VideoCodecType type) 74 | { 75 | g_debug("%s -> type: %d", __FUNCTION__, type); 76 | 77 | webrtc::VideoDecoder *decoder = NULL; 78 | switch (type) { 79 | case webrtc::kVideoCodecVP8: 80 | g_debug("Create VP8 decoder ->", __FUNCTION__); 81 | decoder = webrtc::VP8Decoder::Create(); 82 | g_debug("Create VP8 decoder <-", __FUNCTION__); 83 | break; 84 | case webrtc::kVideoCodecH264: 85 | g_debug("Create H264 decoder ->", __FUNCTION__); 86 | decoder = H264Decoder::Create(); 87 | g_debug("Create H264 decoder ->", __FUNCTION__); 88 | break; 89 | default: 90 | g_debug("%s <- invalid codec type", __FUNCTION__); 91 | break; 92 | } 93 | 94 | g_debug("%s <- decoder = %p", __FUNCTION__, decoder); 95 | return decoder; 96 | } 97 | 98 | void H264DecoderFactory::DestroyVideoDecoder(webrtc::VideoDecoder *decoder) 99 | { 100 | g_debug("%s ->", __FUNCTION__); 101 | delete decoder; 102 | g_debug("%s <-", __FUNCTION__); 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /codecs/codec_factory.h: -------------------------------------------------------------------------------- 1 | #ifndef __H264WEBRTC_VIDEO_ENCODER_FACTORY_H__ 2 | #define __H264WEBRTC_VIDEO_ENCODER_FACTORY_H__ 3 | 4 | #include "talk/media/webrtc/webrtcvideoencoderfactory.h" 5 | #include "talk/media/webrtc/webrtcvideodecoderfactory.h" 6 | 7 | namespace h264webrtc 8 | { 9 | 10 | class H264EncoderFactory : public cricket::WebRtcVideoEncoderFactory 11 | { 12 | public: 13 | 14 | static H264EncoderFactory * Create(); 15 | 16 | virtual webrtc::VideoEncoder *CreateVideoEncoder(webrtc::VideoCodecType type); 17 | 18 | virtual void DestroyVideoEncoder(webrtc::VideoEncoder *encoder); 19 | 20 | virtual const std::vector &codecs() const; 21 | 22 | private: 23 | H264EncoderFactory(); 24 | 25 | std::vector video_codecs; 26 | }; 27 | 28 | class H264DecoderFactory : public cricket::WebRtcVideoDecoderFactory 29 | { 30 | public: 31 | 32 | static H264DecoderFactory * Create(); 33 | 34 | virtual webrtc::VideoDecoder *CreateVideoDecoder(webrtc::VideoCodecType type); 35 | 36 | virtual void DestroyVideoDecoder(webrtc::VideoDecoder *decoder); 37 | 38 | private: 39 | H264DecoderFactory(); 40 | 41 | }; 42 | 43 | } 44 | #endif //__QIC_VIDEO_ENCODER_FACTORY_H__ 45 | -------------------------------------------------------------------------------- /codecs/h264/h264_decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __H264_DECODER_H__ 2 | #define __H264_DECODER_H__ 3 | 4 | #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" 5 | 6 | namespace h264webrtc 7 | { 8 | 9 | class H264Decoder : public webrtc::VideoDecoder { 10 | public: 11 | static H264Decoder * Create(); 12 | 13 | virtual ~H264Decoder() {}; 14 | }; 15 | 16 | }// namespace h264webrtc 17 | 18 | #endif //__H264_DECODER_H__ 19 | -------------------------------------------------------------------------------- /codecs/h264/h264_encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __H264_ENCODER_H__ 2 | #define __H264_ENCODER_H__ 3 | 4 | #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" 5 | 6 | namespace h264webrtc 7 | { 8 | 9 | class H264Encoder : public webrtc::VideoEncoder 10 | { 11 | public: 12 | static H264Encoder *Create(); 13 | 14 | virtual ~H264Encoder() { }; 15 | }; 16 | 17 | }// namespace h264webrtc 18 | 19 | #endif // __H264_ENCODER_H__ 20 | -------------------------------------------------------------------------------- /codecs/h264/open_h264_decoder_impl.cpp: -------------------------------------------------------------------------------- 1 | #include "open_h264_decoder_impl.h" 2 | 3 | namespace h264webrtc 4 | { 5 | 6 | 7 | H264Decoder *H264Decoder::Create() 8 | { 9 | return new OpenH264DecoderImpl(); 10 | } 11 | 12 | OpenH264DecoderImpl::OpenH264DecoderImpl() 13 | { 14 | 15 | } 16 | 17 | OpenH264DecoderImpl::~OpenH264DecoderImpl() 18 | { 19 | 20 | } 21 | 22 | int OpenH264DecoderImpl::InitDecode(const webrtc::VideoCodec* codec_settings, int32_t number_of_cores) 23 | { 24 | return WEBRTC_VIDEO_CODEC_OK; 25 | } 26 | 27 | int OpenH264DecoderImpl::Decode(const webrtc::EncodedImage& input_image, 28 | bool missing_frames, 29 | const webrtc::RTPFragmentationHeader* fragmentation, 30 | const webrtc::CodecSpecificInfo* codec_specific_info, 31 | int64_t render_time_ms) 32 | { 33 | return WEBRTC_VIDEO_CODEC_OK; 34 | } 35 | 36 | int OpenH264DecoderImpl::RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* callback) 37 | { 38 | return WEBRTC_VIDEO_CODEC_OK; 39 | } 40 | 41 | int OpenH264DecoderImpl::Release() 42 | { 43 | return WEBRTC_VIDEO_CODEC_OK; 44 | } 45 | 46 | int OpenH264DecoderImpl::Reset() 47 | { 48 | return WEBRTC_VIDEO_CODEC_OK; 49 | } 50 | 51 | }// namespace h264webrtc -------------------------------------------------------------------------------- /codecs/h264/open_h264_decoder_impl.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPEN_H264_DECODER_IMPL_H__ 2 | #define __OPEN_H264_DECODER_IMPL_H__ 3 | 4 | #include "h264_decoder.h" 5 | 6 | namespace h264webrtc 7 | { 8 | 9 | class OpenH264DecoderImpl : public H264Decoder 10 | { 11 | public: 12 | OpenH264DecoderImpl(); 13 | 14 | virtual int InitDecode(const webrtc::VideoCodec* codec_settings, int32_t number_of_cores); 15 | 16 | virtual int Decode(const webrtc::EncodedImage& input_image, 17 | bool missing_frames, 18 | const webrtc::RTPFragmentationHeader* fragmentation, 19 | const webrtc::CodecSpecificInfo* codec_specific_info = NULL, 20 | int64_t render_time_ms = -1); 21 | 22 | virtual int RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* callback); 23 | 24 | virtual int Release(); 25 | virtual int Reset(); 26 | 27 | virtual ~OpenH264DecoderImpl(); 28 | }; 29 | 30 | }// namespace h264webrtc 31 | 32 | #endif //__OPEN_H264_DECODER_IMPL_H__ 33 | -------------------------------------------------------------------------------- /codecs/h264/open_h264_encoder_impl.cpp: -------------------------------------------------------------------------------- 1 | #include "open_h264_encoder_impl.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "glib.h" 10 | #include "webrtc/common.h" 11 | #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 12 | #include "webrtc/modules/interface/module_common_types.h" 13 | #include "webrtc/system_wrappers/interface/trace.h" 14 | #include "webrtc/system_wrappers/interface/tick_util.h" 15 | #include "webrtc/system_wrappers/interface/trace_event.h" 16 | #include "webrtc/base/scoped_ptr.h" 17 | #include "wels/codec_app_def.h" 18 | 19 | namespace h264webrtc 20 | { 21 | 22 | 23 | H264Encoder *H264Encoder::Create() 24 | { 25 | return new OpenH264EncoderImpl(); 26 | } 27 | 28 | OpenH264EncoderImpl::OpenH264EncoderImpl() : 29 | buffer(NULL), 30 | buffer_size(0), 31 | encoded_complete_callback(NULL), 32 | inited(false), 33 | timestamp(0), 34 | encoder(NULL) 35 | { 36 | memset(&codec, 0, sizeof(codec)); 37 | uint32_t seed = static_cast(webrtc::TickTime::MillisecondTimestamp()); 38 | srand(seed); 39 | } 40 | 41 | OpenH264EncoderImpl::~OpenH264EncoderImpl() 42 | { 43 | Release(); 44 | } 45 | 46 | int OpenH264EncoderImpl::Release() 47 | { 48 | if (buffer != NULL) { 49 | delete[] buffer; 50 | buffer = NULL; 51 | } 52 | if (encoder != NULL) { 53 | WelsDestroySVCEncoder(encoder); 54 | encoder = NULL; 55 | } 56 | buffer_size = 0; 57 | inited = false; 58 | return WEBRTC_VIDEO_CODEC_OK; 59 | } 60 | 61 | int OpenH264EncoderImpl::SetRates(uint32_t new_bitrate_kbit, uint32_t new_framerate) 62 | { 63 | g_debug("%s -> (%d, %d)", __FUNCTION__, new_bitrate_kbit, new_framerate); 64 | 65 | if (!inited) { 66 | return WEBRTC_VIDEO_CODEC_UNINITIALIZED; 67 | } 68 | if (new_framerate < 1) { 69 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; 70 | } 71 | // update bit rate 72 | if (codec.maxBitrate > 0 && new_bitrate_kbit > codec.maxBitrate) { 73 | new_bitrate_kbit = codec.maxBitrate; 74 | } 75 | 76 | g_debug("%s <-", __FUNCTION__); 77 | return WEBRTC_VIDEO_CODEC_OK; 78 | } 79 | 80 | int OpenH264EncoderImpl::InitEncode(const webrtc::VideoCodec *codec_settings, int number_of_cores, size_t max_payload_size) 81 | { 82 | g_debug("%s: codec_settings(%d x %d : %d, StartBitrate: %d kbps)", 83 | __FUNCTION__, 84 | codec_settings->width, codec_settings->height, 85 | codec_settings->maxFramerate, 86 | codec_settings->startBitrate); 87 | 88 | if (codec_settings->maxFramerate < 1) { 89 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; 90 | } 91 | // allow zero to represent an unspecified maxBitRate 92 | if (codec_settings->maxBitrate > 0 && codec_settings->startBitrate > codec_settings->maxBitrate) { 93 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; 94 | } 95 | if (codec_settings->width < 1 || codec_settings->height < 1) { 96 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; 97 | } 98 | if (number_of_cores < 1) { 99 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; 100 | } 101 | 102 | int ret_val = Release(); 103 | if (ret_val < 0) { 104 | return ret_val; 105 | } 106 | if (encoder == NULL) { 107 | ret_val = WelsCreateSVCEncoder(&encoder); 108 | if (ret_val != 0) { 109 | g_warning("%s: fails to create encoder: %d", __FUNCTION__, ret_val); 110 | return WEBRTC_VIDEO_CODEC_ERROR; 111 | } 112 | } 113 | 114 | SEncParamBase param; 115 | memset(¶m, 0, sizeof(SEncParamBase)); 116 | param.iUsageType = CAMERA_VIDEO_REAL_TIME; 117 | param.iRCMode = RC_QUALITY_MODE; 118 | param.fMaxFrameRate = codec_settings->maxFramerate; 119 | param.iPicWidth = codec_settings->width; 120 | param.iPicHeight = codec_settings->height; 121 | param.iTargetBitrate = codec_settings->startBitrate * 1024; 122 | ret_val = encoder->Initialize(¶m); 123 | if (ret_val != 0) { 124 | g_warning("%s: fails to initialize encoder: %d", __FUNCTION__, ret_val); 125 | WelsDestroySVCEncoder(encoder); 126 | encoder = NULL; 127 | return WEBRTC_VIDEO_CODEC_ERROR; 128 | } 129 | 130 | if (&codec != codec_settings) { 131 | codec = *codec_settings; 132 | } 133 | 134 | if (buffer != NULL) { 135 | delete[] buffer; 136 | } 137 | buffer_size = webrtc::CalcBufferSize(webrtc::kI420, codec.width, codec.height); 138 | buffer = new uint8_t[buffer_size]; 139 | 140 | timestamp = 0; 141 | inited = true; 142 | 143 | return WEBRTC_VIDEO_CODEC_OK; 144 | } 145 | 146 | int OpenH264EncoderImpl::GetTotalNaluCount(const SFrameBSInfo &info) 147 | { 148 | if (info.iLayerNum <= 0 || info.iFrameSizeInBytes <= 0) { 149 | return 0; 150 | } 151 | 152 | uint32_t totalNaluCount = 0; 153 | 154 | for (int layer_index = 0; layer_index < info.iLayerNum; layer_index++) { 155 | const SLayerBSInfo *layer_bs_info = &info.sLayerInfo[layer_index]; 156 | totalNaluCount += layer_bs_info->iNalCount; 157 | } 158 | 159 | return totalNaluCount; 160 | } 161 | 162 | bool OpenH264EncoderImpl::GetRTPFragmentationHeaderH264(const SFrameBSInfo &info, webrtc::RTPFragmentationHeader &header, webrtc::EncodedImage &encoded_image) 163 | { 164 | uint32_t totalNaluIndex = 0; 165 | for (int layer = 0; layer < info.iLayerNum; layer++) { 166 | 167 | const SLayerBSInfo *layer_bs_info = &info.sLayerInfo[layer]; 168 | if (layer_bs_info != NULL) { 169 | 170 | int layer_size = 0; 171 | int nal_begin = 4; 172 | uint8_t *nal_buffer = NULL; 173 | 174 | for (int nal_index = 0; nal_index < layer_bs_info->iNalCount; nal_index++) { 175 | 176 | nal_buffer = layer_bs_info->pBsBuf + nal_begin; 177 | layer_size += layer_bs_info->pNalLengthInByte[nal_index]; 178 | nal_begin += layer_size; 179 | 180 | int currentNaluSize = layer_bs_info->pNalLengthInByte[nal_index] - 4; 181 | memcpy(encoded_image._buffer + encoded_image._length, nal_buffer, (size_t)currentNaluSize); 182 | encoded_image._length += currentNaluSize; 183 | 184 | header.fragmentationOffset[totalNaluIndex] = encoded_image._length - currentNaluSize; 185 | header.fragmentationLength[totalNaluIndex] = (size_t)currentNaluSize; 186 | header.fragmentationPlType[totalNaluIndex] = 0; 187 | header.fragmentationTimeDiff[totalNaluIndex] = 0; 188 | totalNaluIndex++; 189 | } 190 | } 191 | } 192 | 193 | return true; 194 | } 195 | 196 | int OpenH264EncoderImpl::Encode(const webrtc::VideoFrame &input_image, 197 | const webrtc::CodecSpecificInfo *codec_specific_info, 198 | const std::vector *frame_types) 199 | { 200 | if (!inited) { 201 | return WEBRTC_VIDEO_CODEC_UNINITIALIZED; 202 | } 203 | if (input_image.IsZeroSize()) { 204 | return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; 205 | } 206 | if (encoded_complete_callback == NULL) { 207 | return WEBRTC_VIDEO_CODEC_UNINITIALIZED; 208 | } 209 | 210 | // We only support one stream at the moment. 211 | webrtc::VideoFrameType frame_type = webrtc::kDeltaFrame; 212 | if (frame_types && frame_types->size() > 0) { 213 | frame_type = (*frame_types)[0]; 214 | } 215 | g_debug("%s -> frame_type: %d, RTS: %ld, TS: %ld", __FUNCTION__, frame_type, input_image.render_time_ms(), timestamp); 216 | 217 | bool send_keyframe = (frame_type == webrtc::kKeyFrame); 218 | if (send_keyframe) { 219 | encoder->ForceIntraFrame(true); 220 | g_debug("ForceIntraFrame(width:%d, height:%d)", input_image.width(), input_image.height()); 221 | } 222 | 223 | // Check for change in frame size. 224 | if (input_image.width() != codec.width || input_image.height() != codec.height) { 225 | codec.width = (unsigned short) input_image.width(); 226 | codec.height = (unsigned short) input_image.height(); 227 | } 228 | 229 | SSourcePicture pic = {0}; 230 | pic.iPicWidth = input_image.width(); 231 | pic.iPicHeight = input_image.height(); 232 | pic.iColorFormat = videoFormatI420; 233 | pic.iStride[0] = input_image.stride(webrtc::kYPlane); 234 | pic.iStride[1] = input_image.stride(webrtc::kUPlane); 235 | pic.iStride[2] = input_image.stride(webrtc::kVPlane); 236 | pic.pData[0] = const_cast(input_image.buffer(webrtc::kYPlane)); 237 | pic.pData[1] = const_cast(input_image.buffer(webrtc::kUPlane)); 238 | pic.pData[2] = const_cast(input_image.buffer(webrtc::kVPlane)); 239 | // Cheat OpenH264 for the framerate 240 | pic.uiTimeStamp = timestamp; 241 | timestamp += (int64_t)(1000 / codec.maxFramerate); 242 | 243 | SFrameBSInfo info = {0}; 244 | int retVal = encoder->EncodeFrame(&pic, &info); 245 | if (retVal == videoFrameTypeSkip || 246 | info.iLayerNum == 0 || 247 | info.iFrameSizeInBytes == 0) { 248 | g_debug("%s <- Skip frame, retVal: %d\n", __FUNCTION__, retVal); 249 | return WEBRTC_VIDEO_CODEC_OK; 250 | } 251 | 252 | int totalNaluCount = GetTotalNaluCount(info); 253 | if (totalNaluCount == 0) { 254 | g_debug("%s <- No NALU\n", __FUNCTION__); 255 | return WEBRTC_VIDEO_CODEC_OK; 256 | } 257 | 258 | g_debug("%s layerNum: %d, FrameSizeInBytes: %d, NALUCount: %d", __FUNCTION__, info.iLayerNum, info.iFrameSizeInBytes, totalNaluCount); 259 | 260 | webrtc::EncodedImage encoded_image(buffer, 0, buffer_size); 261 | webrtc::RTPFragmentationHeader header; 262 | header.VerifyAndAllocateFragmentationHeader((size_t)totalNaluCount); 263 | GetRTPFragmentationHeaderH264(info, header, encoded_image); 264 | 265 | webrtc::CodecSpecificInfo codec_info; 266 | memset(&codec_info, 0, sizeof(codec_info)); 267 | codec_info.codecType = webrtc::kVideoCodecH264; 268 | 269 | encoded_image._encodedWidth = codec.width; 270 | encoded_image._encodedHeight = codec.height; 271 | encoded_image._timeStamp = input_image.timestamp(); 272 | encoded_image.capture_time_ms_ = input_image.render_time_ms(); 273 | encoded_image._frameType = frame_type; 274 | encoded_image._completeFrame = true; 275 | 276 | // call back 277 | encoded_complete_callback->Encoded(encoded_image, &codec_info, &header); 278 | 279 | g_debug("%s <- \n", __FUNCTION__); 280 | return WEBRTC_VIDEO_CODEC_OK; 281 | } 282 | 283 | int OpenH264EncoderImpl::RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback *callback) 284 | { 285 | encoded_complete_callback = callback; 286 | return WEBRTC_VIDEO_CODEC_OK; 287 | } 288 | 289 | int OpenH264EncoderImpl::SetChannelParameters(uint32_t /*packet_loss*/, int64_t rtt) 290 | { 291 | return WEBRTC_VIDEO_CODEC_OK; 292 | } 293 | 294 | }// namespace h264webrtc -------------------------------------------------------------------------------- /codecs/h264/open_h264_encoder_impl.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPEN_H264_ENCODER_IMPL_H__ 2 | #define __OPEN_H264_ENCODER_IMPL_H__ 3 | 4 | // OpenH264 headers 5 | #include "wels/codec_api.h" 6 | #include "wels/codec_def.h" 7 | #include "wels/codec_app_def.h" 8 | 9 | #include "h264_encoder.h" 10 | 11 | namespace h264webrtc 12 | { 13 | 14 | class OpenH264EncoderImpl : public H264Encoder 15 | { 16 | public: 17 | OpenH264EncoderImpl(); 18 | 19 | virtual ~OpenH264EncoderImpl(); 20 | 21 | // Free encoder memory. 22 | // 23 | // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. 24 | virtual int Release(); 25 | 26 | // Initialize the encoder with the information from the codecSettings 27 | // 28 | // Input: 29 | // - codec_settings : Codec settings 30 | // - number_of_cores : Number of cores available for the encoder 31 | // - max_payload_size : The maximum size each payload is allowed 32 | // to have. Usually MTU - overhead. 33 | // 34 | // Return value : Set bit rate if OK 35 | // <0 - Errors: 36 | // WEBRTC_VIDEO_CODEC_ERR_PARAMETER 37 | // WEBRTC_VIDEO_CODEC_ERR_SIZE 38 | // WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED 39 | // WEBRTC_VIDEO_CODEC_MEMORY 40 | // WEBRTC_VIDEO_CODEC_ERROR 41 | virtual int InitEncode(const webrtc::VideoCodec *codec_settings, 42 | int number_of_cores, 43 | size_t max_payload_size); 44 | 45 | // Encode an I420 image (as a part of a video stream). The encoded image 46 | // will be returned to the user through the encode complete callback. 47 | // 48 | // Input: 49 | // - input_image : Image to be encoded 50 | // - frame_types : Frame type to be generated by the encoder. 51 | // 52 | // Return value : WEBRTC_VIDEO_CODEC_OK if OK 53 | // <0 - Errors: 54 | // WEBRTC_VIDEO_CODEC_ERR_PARAMETER 55 | // WEBRTC_VIDEO_CODEC_MEMORY 56 | // WEBRTC_VIDEO_CODEC_ERROR 57 | // WEBRTC_VIDEO_CODEC_TIMEOUT 58 | 59 | virtual int Encode(const webrtc::VideoFrame &input_image, 60 | const webrtc::CodecSpecificInfo *codec_specific_info, 61 | const std::vector *frame_types); 62 | 63 | // Register an encode complete callback object. 64 | // 65 | // Input: 66 | // - callback : Callback object which handles encoded images. 67 | // 68 | // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. 69 | virtual int RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback *callback); 70 | 71 | // Inform the encoder of the new packet loss rate and the round-trip time of 72 | // the network. 73 | // 74 | // - packet_loss : Fraction lost 75 | // (loss rate in percent = 100 * packetLoss / 255) 76 | // - rtt : Round-trip time in milliseconds 77 | // Return value : WEBRTC_VIDEO_CODEC_OK if OK 78 | // <0 - Errors: WEBRTC_VIDEO_CODEC_ERROR 79 | // 80 | virtual int SetChannelParameters(uint32_t packet_loss, int64_t rtt); 81 | 82 | // Inform the encoder about the new target bit rate. 83 | // 84 | // - new_bitrate_kbit : New target bit rate 85 | // - frame_rate : The target frame rate 86 | // 87 | // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. 88 | virtual int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate); 89 | 90 | private: 91 | 92 | int GetTotalNaluCount(const SFrameBSInfo &info); 93 | bool GetRTPFragmentationHeaderH264(const SFrameBSInfo &info, webrtc::RTPFragmentationHeader &header, webrtc::EncodedImage &encoded_image); 94 | 95 | uint8_t *buffer; 96 | size_t buffer_size; 97 | webrtc::EncodedImageCallback *encoded_complete_callback; 98 | webrtc::VideoCodec codec; 99 | bool inited; 100 | int64_t timestamp; 101 | ISVCEncoder *encoder; 102 | }; // end of H264Encoder class 103 | 104 | }// namespace h264webrtc 105 | 106 | #endif // __OPEN_H264_ENCODER_IMPL_H__ 107 | -------------------------------------------------------------------------------- /json_parser/json_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "json_parser/json_parser.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace h264webrtc { 9 | 10 | bool GetStringFromJson(const Json::Value& in, std::string* out) { 11 | if (!in.isString()) { 12 | std::ostringstream s; 13 | if (in.isBool()) { 14 | s << std::boolalpha << in.asBool(); 15 | } else if (in.isInt()) { 16 | s << in.asInt(); 17 | } else if (in.isUInt()) { 18 | s << in.asUInt(); 19 | } else if (in.isDouble()) { 20 | s << in.asDouble(); 21 | } else { 22 | return false; 23 | } 24 | *out = s.str(); 25 | } else { 26 | *out = in.asString(); 27 | } 28 | return true; 29 | } 30 | 31 | bool GetIntFromJson(const Json::Value& in, int* out) { 32 | bool ret; 33 | if (!in.isString()) { 34 | ret = in.isConvertibleTo(Json::intValue); 35 | if (ret) { 36 | *out = in.asInt(); 37 | } 38 | } else { 39 | long val; // NOLINT 40 | const char* c_str = in.asCString(); 41 | char* end_ptr; 42 | errno = 0; 43 | val = strtol(c_str, &end_ptr, 10); // NOLINT 44 | ret = (end_ptr != c_str && *end_ptr == '\0' && !errno && 45 | val >= INT_MIN && val <= INT_MAX); 46 | *out = val; 47 | } 48 | return ret; 49 | } 50 | 51 | bool GetUIntFromJson(const Json::Value& in, unsigned int* out) { 52 | bool ret; 53 | if (!in.isString()) { 54 | ret = in.isConvertibleTo(Json::uintValue); 55 | if (ret) { 56 | *out = in.asUInt(); 57 | } 58 | } else { 59 | unsigned long val; // NOLINT 60 | const char* c_str = in.asCString(); 61 | char* end_ptr; 62 | errno = 0; 63 | val = strtoul(c_str, &end_ptr, 10); // NOLINT 64 | ret = (end_ptr != c_str && *end_ptr == '\0' && !errno && 65 | val <= UINT_MAX); 66 | *out = val; 67 | } 68 | return ret; 69 | } 70 | 71 | bool GetBoolFromJson(const Json::Value& in, bool* out) { 72 | bool ret; 73 | if (!in.isString()) { 74 | ret = in.isConvertibleTo(Json::booleanValue); 75 | if (ret) { 76 | *out = in.asBool(); 77 | } 78 | } else { 79 | if (in.asString() == "true") { 80 | *out = true; 81 | ret = true; 82 | } else if (in.asString() == "false") { 83 | *out = false; 84 | ret = true; 85 | } else { 86 | ret = false; 87 | } 88 | } 89 | return ret; 90 | } 91 | 92 | bool GetDoubleFromJson(const Json::Value& in, double* out) { 93 | bool ret; 94 | if (!in.isString()) { 95 | ret = in.isConvertibleTo(Json::realValue); 96 | if (ret) { 97 | *out = in.asDouble(); 98 | } 99 | } else { 100 | double val; 101 | const char* c_str = in.asCString(); 102 | char* end_ptr; 103 | errno = 0; 104 | val = strtod(c_str, &end_ptr); 105 | ret = (end_ptr != c_str && *end_ptr == '\0' && !errno); 106 | *out = val; 107 | } 108 | return ret; 109 | } 110 | 111 | namespace { 112 | template 113 | bool JsonArrayToVector(const Json::Value& value, 114 | bool (*getter)(const Json::Value& in, T* out), 115 | std::vector *vec) { 116 | vec->clear(); 117 | if (!value.isArray()) { 118 | return false; 119 | } 120 | 121 | for (Json::Value::ArrayIndex i = 0; i < value.size(); ++i) { 122 | T val; 123 | if (!getter(value[i], &val)) { 124 | return false; 125 | } 126 | vec->push_back(val); 127 | } 128 | 129 | return true; 130 | } 131 | // Trivial getter helper 132 | bool GetValueFromJson(const Json::Value& in, Json::Value* out) { 133 | *out = in; 134 | return true; 135 | } 136 | } // unnamed namespace 137 | 138 | bool JsonArrayToValueVector(const Json::Value& in, 139 | std::vector* out) { 140 | return JsonArrayToVector(in, GetValueFromJson, out); 141 | } 142 | 143 | bool JsonArrayToIntVector(const Json::Value& in, 144 | std::vector* out) { 145 | return JsonArrayToVector(in, GetIntFromJson, out); 146 | } 147 | 148 | bool JsonArrayToUIntVector(const Json::Value& in, 149 | std::vector* out) { 150 | return JsonArrayToVector(in, GetUIntFromJson, out); 151 | } 152 | 153 | bool JsonArrayToStringVector(const Json::Value& in, 154 | std::vector* out) { 155 | return JsonArrayToVector(in, GetStringFromJson, out); 156 | } 157 | 158 | bool JsonArrayToBoolVector(const Json::Value& in, 159 | std::vector* out) { 160 | return JsonArrayToVector(in, GetBoolFromJson, out); 161 | } 162 | 163 | bool JsonArrayToDoubleVector(const Json::Value& in, 164 | std::vector* out) { 165 | return JsonArrayToVector(in, GetDoubleFromJson, out); 166 | } 167 | 168 | namespace { 169 | template 170 | Json::Value VectorToJsonArray(const std::vector& vec) { 171 | Json::Value result(Json::arrayValue); 172 | for (size_t i = 0; i < vec.size(); ++i) { 173 | result.append(Json::Value(vec[i])); 174 | } 175 | return result; 176 | } 177 | } // unnamed namespace 178 | 179 | Json::Value ValueVectorToJsonArray(const std::vector& in) { 180 | return VectorToJsonArray(in); 181 | } 182 | 183 | Json::Value IntVectorToJsonArray(const std::vector& in) { 184 | return VectorToJsonArray(in); 185 | } 186 | 187 | Json::Value UIntVectorToJsonArray(const std::vector& in) { 188 | return VectorToJsonArray(in); 189 | } 190 | 191 | Json::Value StringVectorToJsonArray(const std::vector& in) { 192 | return VectorToJsonArray(in); 193 | } 194 | 195 | Json::Value BoolVectorToJsonArray(const std::vector& in) { 196 | return VectorToJsonArray(in); 197 | } 198 | 199 | Json::Value DoubleVectorToJsonArray(const std::vector& in) { 200 | return VectorToJsonArray(in); 201 | } 202 | 203 | bool GetValueFromJsonArray(const Json::Value& in, size_t n, 204 | Json::Value* out) { 205 | if (!in.isArray() || !in.isValidIndex(static_cast(n))) { 206 | return false; 207 | } 208 | 209 | *out = in[static_cast(n)]; 210 | return true; 211 | } 212 | 213 | bool GetIntFromJsonArray(const Json::Value& in, size_t n, 214 | int* out) { 215 | Json::Value x; 216 | return GetValueFromJsonArray(in, n, &x) && GetIntFromJson(x, out); 217 | } 218 | 219 | bool GetUIntFromJsonArray(const Json::Value& in, size_t n, 220 | unsigned int* out) { 221 | Json::Value x; 222 | return GetValueFromJsonArray(in, n, &x) && GetUIntFromJson(x, out); 223 | } 224 | 225 | bool GetStringFromJsonArray(const Json::Value& in, size_t n, 226 | std::string* out) { 227 | Json::Value x; 228 | return GetValueFromJsonArray(in, n, &x) && GetStringFromJson(x, out); 229 | } 230 | 231 | bool GetBoolFromJsonArray(const Json::Value& in, size_t n, 232 | bool* out) { 233 | Json::Value x; 234 | return GetValueFromJsonArray(in, n, &x) && GetBoolFromJson(x, out); 235 | } 236 | 237 | bool GetDoubleFromJsonArray(const Json::Value& in, size_t n, 238 | double* out) { 239 | Json::Value x; 240 | return GetValueFromJsonArray(in, n, &x) && GetDoubleFromJson(x, out); 241 | } 242 | 243 | bool GetValueFromJsonObject(const Json::Value& in, const std::string& k, 244 | Json::Value* out) { 245 | if (!in.isObject() || !in.isMember(k)) { 246 | return false; 247 | } 248 | 249 | *out = in[k]; 250 | return true; 251 | } 252 | 253 | bool GetIntFromJsonObject(const Json::Value& in, const std::string& k, 254 | int* out) { 255 | Json::Value x; 256 | return GetValueFromJsonObject(in, k, &x) && GetIntFromJson(x, out); 257 | } 258 | 259 | bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k, 260 | unsigned int* out) { 261 | Json::Value x; 262 | return GetValueFromJsonObject(in, k, &x) && GetUIntFromJson(x, out); 263 | } 264 | 265 | bool GetStringFromJsonObject(const Json::Value& in, const std::string& k, 266 | std::string* out) { 267 | Json::Value x; 268 | return GetValueFromJsonObject(in, k, &x) && GetStringFromJson(x, out); 269 | } 270 | 271 | bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k, 272 | bool* out) { 273 | Json::Value x; 274 | return GetValueFromJsonObject(in, k, &x) && GetBoolFromJson(x, out); 275 | } 276 | 277 | bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k, 278 | double* out) { 279 | Json::Value x; 280 | return GetValueFromJsonObject(in, k, &x) && GetDoubleFromJson(x, out); 281 | } 282 | 283 | std::string JsonValueToString(const Json::Value& json) { 284 | Json::FastWriter w; 285 | std::string value = w.write(json); 286 | return value.substr(0, value.size() - 1); // trim trailing newline 287 | } 288 | 289 | } // namespace h264webrtc 290 | -------------------------------------------------------------------------------- /json_parser/json_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef H264WEBRTC_JSON_PARSER_H 2 | #define H264WEBRTC_JSON_PARSER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "json/json.h" 8 | 9 | namespace h264webrtc { 10 | 11 | // Robust conversion operators, better than the ones in JsonCpp. 12 | bool GetIntFromJson(const Json::Value& in, int* out); 13 | bool GetUIntFromJson(const Json::Value& in, unsigned int* out); 14 | bool GetStringFromJson(const Json::Value& in, std::string* out); 15 | bool GetBoolFromJson(const Json::Value& in, bool* out); 16 | bool GetDoubleFromJson(const Json::Value& in, double* out); 17 | 18 | // Pull values out of a JSON array. 19 | bool GetValueFromJsonArray(const Json::Value& in, size_t n, Json::Value* out); 20 | bool GetIntFromJsonArray(const Json::Value& in, size_t n, int* out); 21 | bool GetUIntFromJsonArray(const Json::Value& in, size_t n, unsigned int* out); 22 | bool GetStringFromJsonArray(const Json::Value& in, size_t n, std::string* out); 23 | bool GetBoolFromJsonArray(const Json::Value& in, size_t n, bool* out); 24 | bool GetDoubleFromJsonArray(const Json::Value& in, size_t n, double* out); 25 | 26 | // Convert json arrays to std::vector 27 | bool JsonArrayToValueVector(const Json::Value& in, std::vector* out); 28 | bool JsonArrayToIntVector(const Json::Value& in, std::vector* out); 29 | bool JsonArrayToUIntVector(const Json::Value& in, std::vector* out); 30 | bool JsonArrayToStringVector(const Json::Value& in, std::vector* out); 31 | bool JsonArrayToBoolVector(const Json::Value& in, std::vector* out); 32 | bool JsonArrayToDoubleVector(const Json::Value& in, std::vector* out); 33 | 34 | // Convert std::vector to json array 35 | Json::Value ValueVectorToJsonArray(const std::vector& in); 36 | Json::Value IntVectorToJsonArray(const std::vector& in); 37 | Json::Value UIntVectorToJsonArray(const std::vector& in); 38 | Json::Value StringVectorToJsonArray(const std::vector& in); 39 | Json::Value BoolVectorToJsonArray(const std::vector& in); 40 | Json::Value DoubleVectorToJsonArray(const std::vector& in); 41 | 42 | // Pull values out of a JSON object. 43 | bool GetValueFromJsonObject(const Json::Value& in, const std::string& k, Json::Value* out); 44 | bool GetIntFromJsonObject(const Json::Value& in, const std::string& k, int* out); 45 | bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k, unsigned int* out); 46 | bool GetStringFromJsonObject(const Json::Value& in, const std::string& k, std::string* out); 47 | bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k, bool* out); 48 | bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k, double* out); 49 | 50 | // Writes out a Json value as a string. 51 | std::string JsonValueToString(const Json::Value& json); 52 | 53 | } // namespace h264webrtc 54 | 55 | #endif //H264WEBRTC_JSON_PARSER_H 56 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "peer_manager.h" 2 | #include "json_parser/json_parser.h" 3 | 4 | #include "glib.h" 5 | #include "glib-unix.h" 6 | #include "libsoup/soup.h" 7 | #include "webrtc/base/ssladapter.h" 8 | #include "webrtc/base/thread.h" 9 | 10 | namespace h264webrtc 11 | { 12 | 13 | void read_eventstream_line(GDataInputStream *input_stream, gpointer user_data); 14 | 15 | const gchar *server_address = "http://demo.openwebrtc.io:38080"; 16 | bool is_verbose_mode = false; 17 | gchar *session_id = NULL, *peer_id = NULL; 18 | guint32 client_id = 0; 19 | int mode = 0; 20 | GMainLoop *main_loop; 21 | PeerManager *peer_manager = NULL; 22 | bool is_peer_joined = false; 23 | 24 | GOptionEntry opt_entries[] = { 25 | {"verbose", 'v', 0, G_OPTION_ARG_NONE, &is_verbose_mode, "Enable verbose mode (default: disable)", NULL}, 26 | {"mode", 'm', 0, G_OPTION_ARG_INT, &mode, "Set camera mode (default: 0) (0: YuvFrameGenerator, 1: QIC camera, 2: V4L2 device)", NULL}, 27 | {"sessionid", 's', 0, G_OPTION_ARG_STRING, &session_id, "Specify the session id", NULL}, 28 | {NULL}, 29 | }; 30 | 31 | gboolean on_sig_handler(gpointer userdata) 32 | { 33 | g_debug("Quit"); 34 | g_main_loop_quit(main_loop); 35 | return G_SOURCE_REMOVE; 36 | } 37 | 38 | static void answer_finish(SoupSession *soup_session, GAsyncResult *result, gpointer user_data) 39 | { 40 | GInputStream *input_stream; 41 | input_stream = soup_session_send_finish(soup_session, result, NULL); 42 | if (!input_stream) 43 | g_warning("Failed to send back to server"); 44 | else 45 | g_object_unref(input_stream); 46 | } 47 | 48 | void signal_sdp_feedback(Json::Value value) 49 | { 50 | Json::Value jmessage; 51 | jmessage["sdp"] = value; 52 | 53 | std::string sdp = JsonValueToString(jmessage); 54 | g_debug("%s", sdp.c_str()); 55 | 56 | gchar *url = g_strdup_printf("%s/ctos/%s/%u/%s", h264webrtc::server_address, session_id, client_id, peer_id); 57 | SoupMessage *soup_message = soup_message_new("POST", url); 58 | soup_message_set_request(soup_message, "application/json", SOUP_MEMORY_COPY, sdp.c_str(), sdp.length()); 59 | g_free(url); 60 | 61 | SoupSession *soup_session = soup_session_new(); 62 | soup_session_send_async(soup_session, soup_message, NULL, (GAsyncReadyCallback) answer_finish, NULL); 63 | } 64 | 65 | void signal_candidate_feedback(Json::Value value) 66 | { 67 | Json::Value jmessage; 68 | jmessage["candidate"] = value; 69 | 70 | std::string candidate = JsonValueToString(jmessage); 71 | g_debug("%s", candidate.c_str()); 72 | 73 | gchar *url = g_strdup_printf("%s/ctos/%s/%u/%s", h264webrtc::server_address, session_id, client_id, peer_id); 74 | SoupSession *soup_session = soup_session_new(); 75 | SoupMessage *soup_message = soup_message_new("POST", url); 76 | g_free(url); 77 | soup_message_set_request(soup_message, "application/json", SOUP_MEMORY_COPY, candidate.c_str(), candidate.length()); 78 | soup_session_send_async(soup_session, soup_message, NULL, (GAsyncReadyCallback) answer_finish, NULL); 79 | } 80 | 81 | void eventstream_line_read(GDataInputStream *input_stream, GAsyncResult *result, gpointer user_data) 82 | { 83 | gchar *line; 84 | gsize line_length; 85 | 86 | line = g_data_input_stream_read_line_finish_utf8(input_stream, result, &line_length, NULL); 87 | g_return_if_fail(line); 88 | 89 | //g_debug("%s", line); 90 | if (!is_peer_joined && g_strstr_len(line, MIN(line_length, 10), "event:join")) { 91 | 92 | is_peer_joined = true; 93 | g_debug("Peer joined"); 94 | 95 | } else if (is_peer_joined && g_strstr_len(line, MIN(line_length, 11), "event:leave")) { 96 | 97 | peer_manager->deletePeerConnection(peer_id); 98 | g_free(peer_id); 99 | peer_id = NULL; 100 | is_peer_joined = false; 101 | g_debug("Peer leave"); 102 | 103 | } else if (is_peer_joined && g_strstr_len(line, MIN(line_length, 5), "data:")) { 104 | 105 | if (g_strstr_len(line, MIN(line_length, 6), "data:{")) { 106 | // Handle the "data:{"sdp":...}" and "data:{"candidate":...}" case 107 | Json::Reader reader; 108 | Json::Value jmessage; 109 | if (reader.parse(line + 5, jmessage)) { 110 | Json::Value value; 111 | if (GetValueFromJsonObject(jmessage, "sdp", &value)) { 112 | 113 | g_debug("Get sdp ->"); 114 | peer_manager->setOffser(peer_id, value); 115 | g_debug("Get sdp <-"); 116 | 117 | } else if (GetValueFromJsonObject(jmessage, "candidate", &value)) { 118 | 119 | g_debug("Get candidate ->"); 120 | peer_manager->addIceCandidate(peer_id, value); 121 | g_debug("Get candidate <-"); 122 | 123 | } else { 124 | g_debug("Invalid json message: %s", line + 5); 125 | } 126 | } 127 | } else { 128 | // Handle the peer id 129 | peer_id = g_strndup(line + 5, line_length - 5); 130 | g_debug("Get peer_id: %s", peer_id); 131 | } 132 | 133 | } 134 | 135 | g_free(line); 136 | 137 | read_eventstream_line(input_stream, NULL); 138 | } 139 | 140 | void read_eventstream_line(GDataInputStream *input_stream, gpointer user_data) 141 | { 142 | g_data_input_stream_read_line_async( 143 | input_stream, 144 | G_PRIORITY_DEFAULT, 145 | NULL, 146 | (GAsyncReadyCallback) eventstream_line_read, 147 | user_data 148 | ); 149 | } 150 | 151 | void eventsource_request_sent(SoupSession *soup_session, GAsyncResult *result, gpointer user_data) 152 | { 153 | GInputStream *input_stream; 154 | GDataInputStream *data_input_stream; 155 | 156 | input_stream = soup_session_send_finish(soup_session, result, NULL); 157 | if (input_stream) { 158 | data_input_stream = g_data_input_stream_new(input_stream); 159 | read_eventstream_line(data_input_stream, user_data); 160 | } else { 161 | g_warning("Failed to connect to the server"); 162 | } 163 | } 164 | 165 | void send_eventsource_request(const gchar *url) 166 | { 167 | SoupSession *soup_session; 168 | SoupMessage *soup_message; 169 | 170 | soup_session = soup_session_new(); 171 | soup_message = soup_message_new("GET", url); 172 | soup_session_send_async( 173 | soup_session, 174 | soup_message, 175 | NULL, 176 | (GAsyncReadyCallback) eventsource_request_sent, 177 | NULL 178 | ); 179 | } 180 | 181 | } // namespace h264webrtc 182 | 183 | int main(int argc, char **argv) 184 | { 185 | GError *error = NULL; 186 | GOptionContext *context; 187 | 188 | context = g_option_context_new("- H264Webrtc Server"); 189 | g_option_context_add_main_entries(context, h264webrtc::opt_entries, NULL); 190 | if (!g_option_context_parse(context, &argc, &argv, &error)) { 191 | g_error_free(error); 192 | return 1; 193 | } 194 | 195 | if (h264webrtc::session_id == NULL) { 196 | g_print("%s", g_option_context_get_help(context, true, NULL)); 197 | return 1; 198 | } 199 | 200 | if (h264webrtc::is_verbose_mode) { 201 | rtc::LogMessage::LogToDebug((h264webrtc::is_verbose_mode) ? rtc::INFO : rtc::LERROR); 202 | rtc::LogMessage::LogTimestamps(); 203 | rtc::LogMessage::LogThreads(); 204 | g_setenv("G_MESSAGES_DEBUG", "all", TRUE); 205 | } 206 | rtc::InitializeSSL(); 207 | 208 | // Create the soup session and connect to signal server 209 | h264webrtc::client_id = g_random_int(); 210 | gchar *url = g_strdup_printf("%s/stoc/%s/%u", h264webrtc::server_address, h264webrtc::session_id, h264webrtc::client_id); 211 | if (url) { 212 | h264webrtc::send_eventsource_request(url); 213 | g_free(url); 214 | } 215 | 216 | // webrtc server 217 | h264webrtc::peer_manager = h264webrtc::PeerManager::Create("stun.l.google.com:19302"); 218 | h264webrtc::peer_manager->signal_sdp_feedback.connect(sigc::ptr_fun(h264webrtc::signal_sdp_feedback)); 219 | h264webrtc::peer_manager->signal_candidate_feedback.connect(sigc::ptr_fun(h264webrtc::signal_candidate_feedback)); 220 | 221 | // Create and start the main loop 222 | h264webrtc::main_loop = g_main_loop_new(NULL, FALSE); 223 | g_unix_signal_add(SIGINT, (GSourceFunc) h264webrtc::on_sig_handler, NULL); 224 | g_main_loop_run(h264webrtc::main_loop); 225 | 226 | g_main_loop_unref(h264webrtc::main_loop); 227 | 228 | // Clean up 229 | delete h264webrtc::peer_manager; 230 | 231 | rtc::CleanupSSL(); 232 | 233 | return 0; 234 | } -------------------------------------------------------------------------------- /observers/create_sdp_observer.cpp: -------------------------------------------------------------------------------- 1 | #include "create_sdp_observer.h" 2 | 3 | #include "glib.h" 4 | 5 | #include "set_sdp_observer.h" 6 | #include "peer_manager.h" 7 | 8 | const std::string SDP_TYPE_NAME = "type"; 9 | const std::string SDP_NAME = "sdp"; 10 | 11 | CreateSDPObserver *CreateSDPObserver::Create(webrtc::PeerConnectionInterface* pc, 12 | sigc::signal signal_sdp_feedback) 13 | { 14 | return new rtc::RefCountedObject(pc, signal_sdp_feedback); 15 | } 16 | 17 | void CreateSDPObserver::OnSuccess(webrtc::SessionDescriptionInterface* desc) 18 | { 19 | m_pc->SetLocalDescription(SetSDPObserver::Create(), desc); 20 | 21 | Json::Value jmessage; 22 | jmessage[SDP_TYPE_NAME] = desc->type(); 23 | std::string sdp; 24 | if (!desc->ToString(&sdp)) { 25 | g_warning("Failed to serialize sdp"); 26 | return; 27 | } 28 | 29 | // trick for Firefox H264 support 30 | sdp += "a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r\n"; 31 | jmessage[SDP_NAME] = sdp; 32 | 33 | signal_sdp_feedback(jmessage); 34 | } 35 | 36 | void CreateSDPObserver::OnFailure(const std::string& error) 37 | { 38 | g_warning("Fail to create SDP: %s", error.c_str()); 39 | } -------------------------------------------------------------------------------- /observers/create_sdp_observer.h: -------------------------------------------------------------------------------- 1 | #ifndef __CREATE_SDP_OBSERVER_H__ 2 | #define __CREATE_SDP_OBSERVER_H__ 3 | 4 | #include "talk/app/webrtc/peerconnectioninterface.h" 5 | #include "peer_manager.h" 6 | 7 | class CreateSDPObserver : public webrtc::CreateSessionDescriptionObserver { 8 | public: 9 | static CreateSDPObserver * Create(webrtc::PeerConnectionInterface* pc, sigc::signal signal_sdp_feedback); 10 | 11 | virtual void OnSuccess(webrtc::SessionDescriptionInterface* desc); 12 | virtual void OnFailure(const std::string& error); 13 | 14 | protected: 15 | CreateSDPObserver(webrtc::PeerConnectionInterface* pc, sigc::signal signal_sdp_feedback) 16 | : m_pc(pc), 17 | signal_sdp_feedback(signal_sdp_feedback) 18 | {}; 19 | 20 | private: 21 | webrtc::PeerConnectionInterface* m_pc; 22 | sigc::signal signal_sdp_feedback; 23 | }; 24 | 25 | #endif //__CREATE_SDP_OBSERVER_H__ 26 | -------------------------------------------------------------------------------- /observers/peer_connection_observer.cpp: -------------------------------------------------------------------------------- 1 | #include "peer_connection_observer.h" 2 | 3 | #include "glib.h" 4 | 5 | const std::string CANDIDATE_SDP_MID_NAME = "sdpMid"; 6 | const std::string CANDIDATE_SDP_MLINE_INDEX_NAME = "sdpMLineIndex"; 7 | const std::string CANDIDATE_SDP_NAME = "candidate"; 8 | 9 | PeerConnectionObserver *PeerConnectionObserver::Create(sigc::signal signal_candidate_feedback) 10 | { 11 | return new PeerConnectionObserver(signal_candidate_feedback); 12 | } 13 | 14 | void PeerConnectionObserver::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) 15 | { 16 | g_debug("OnIceCandidate -> mline_index: %d", candidate->sdp_mline_index()); 17 | 18 | Json::Value jmessage; 19 | std::string candidate_string; 20 | jmessage[CANDIDATE_SDP_MID_NAME] = candidate->sdp_mid(); 21 | jmessage[CANDIDATE_SDP_MLINE_INDEX_NAME] = candidate->sdp_mline_index(); 22 | if (!candidate->ToString(&candidate_string)) { 23 | g_warning("Failed to serialize candidate"); 24 | return; 25 | } 26 | jmessage[CANDIDATE_SDP_NAME] = candidate_string; 27 | signal_candidate_feedback(jmessage); 28 | 29 | g_debug("OnIceCandidate <-"); 30 | } -------------------------------------------------------------------------------- /observers/peer_connection_observer.h: -------------------------------------------------------------------------------- 1 | #ifndef __PEER_CONNECTION_OBSERVER_H__ 2 | #define __PEER_CONNECTION_OBSERVER_H__ 3 | 4 | #include "talk/app/webrtc/peerconnectioninterface.h" 5 | #include "talk/app/webrtc/mediastreaminterface.h" 6 | 7 | #include "peer_manager.h" 8 | 9 | class PeerConnectionObserver : public webrtc::PeerConnectionObserver 10 | { 11 | public: 12 | static PeerConnectionObserver * Create(sigc::signal signal_candidate_feedback); 13 | virtual ~PeerConnectionObserver() {} 14 | 15 | virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate); 16 | virtual void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) {} 17 | virtual void OnStateChange(webrtc::PeerConnectionObserver::StateType state_changed) {} 18 | virtual void OnAddStream(webrtc::MediaStreamInterface* stream) {} 19 | virtual void OnRemoveStream(webrtc::MediaStreamInterface* stream) {} 20 | virtual void OnDataChannel(webrtc::DataChannelInterface* channel) {} 21 | virtual void OnRenegotiationNeeded() {} 22 | virtual void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) {} 23 | virtual void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) {} 24 | virtual void OnIceComplete() {} 25 | 26 | protected: 27 | PeerConnectionObserver(sigc::signal signal_candidate_feedback) 28 | : signal_candidate_feedback(signal_candidate_feedback) 29 | {}; 30 | 31 | private: 32 | sigc::signal signal_candidate_feedback; 33 | 34 | }; 35 | 36 | #endif //__PEER_CONNECTION_OBSERVER_H__ 37 | -------------------------------------------------------------------------------- /observers/set_sdp_observer.cpp: -------------------------------------------------------------------------------- 1 | #include "glib.h" 2 | #include "set_sdp_observer.h" 3 | 4 | SetSDPObserver *SetSDPObserver::Create() 5 | { 6 | return new rtc::RefCountedObject(); 7 | } 8 | 9 | void SetSDPObserver::OnSuccess() 10 | { 11 | g_debug("Success to set SDP"); 12 | } 13 | 14 | void SetSDPObserver::OnFailure(const std::string &error) 15 | { 16 | g_debug("Fail to set SDP: %s", error.c_str()); 17 | } -------------------------------------------------------------------------------- /observers/set_sdp_observer.h: -------------------------------------------------------------------------------- 1 | #ifndef __SET_SDP_OBSERVER_H__ 2 | #define __SET_SDP_OBSERVER_H__ 3 | 4 | #include "talk/app/webrtc/peerconnectioninterface.h" 5 | 6 | class SetSDPObserver : public webrtc::SetSessionDescriptionObserver { 7 | public: 8 | static SetSDPObserver* Create(); 9 | virtual void OnSuccess(); 10 | virtual void OnFailure(const std::string& error); 11 | 12 | protected: 13 | SetSDPObserver(){}; 14 | }; 15 | 16 | #endif //__SET_SDP_OBSERVER_H__ 17 | -------------------------------------------------------------------------------- /peer_manager.h: -------------------------------------------------------------------------------- 1 | #ifndef __H264WEBRTC_PEER_MANAGER_H__ 2 | #define __H264WEBRTC_PEER_MANAGER_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "json/json.h" 8 | 9 | namespace h264webrtc 10 | { 11 | 12 | class PeerManager 13 | { 14 | public: 15 | static PeerManager *Create(const std::string &stunurl); 16 | virtual ~PeerManager() {}; 17 | 18 | virtual void setOffser(const std::string &peerid, const Json::Value &sdp) = 0; 19 | virtual void addIceCandidate(const std::string &peerid, const Json::Value &candidate) = 0; 20 | virtual void deletePeerConnection(const std::string &peerid) = 0; 21 | 22 | sigc::signal signal_sdp_feedback; 23 | sigc::signal signal_candidate_feedback; 24 | }; 25 | 26 | } 27 | #endif // __H264WEBRTC_PEER_MANAGER_H__ 28 | -------------------------------------------------------------------------------- /peer_manager_imp.cpp: -------------------------------------------------------------------------------- 1 | #include "peer_manager_imp.h" 2 | 3 | #include 4 | 5 | #include "glib.h" 6 | #include "talk/app/webrtc/test/fakeperiodicvideocapturer.h" 7 | #include "talk/app/webrtc/videosourceinterface.h" 8 | 9 | #include "codecs/codec_factory.h" 10 | #include "json_parser/json_parser.h" 11 | #include "observers/set_sdp_observer.h" 12 | #include "observers/create_sdp_observer.h" 13 | #include "observers/peer_connection_observer.h" 14 | 15 | namespace h264webrtc 16 | { 17 | 18 | const std::string AUDIO_LABEL = "audio_label"; 19 | const std::string VIDEO_LABEL = "video_label"; 20 | const std::string STREAM_LABEL = "stream_label"; 21 | 22 | // Names used for a IceCandidate JSON object. 23 | const std::string CANDIDATE_SDP_MID_NAME = "sdpMid"; 24 | const std::string CANDIDATE_SDP_MLINE_INDEX_NAME = "sdpMLineIndex"; 25 | const std::string CANDIDATE_SDP_NAME = "candidate"; 26 | 27 | // Names used for a SessionDescription JSON object. 28 | const std::string SDP_TYPE_NAME = "type"; 29 | const std::string SDP_NAME = "sdp"; 30 | 31 | class VideoCapturerListener : public sigslot::has_slots<> 32 | { 33 | public: 34 | VideoCapturerListener(cricket::VideoCapturer *capturer) 35 | { 36 | capturer->SignalFrameCaptured.connect(this, &VideoCapturerListener::OnFrameCaptured); 37 | } 38 | 39 | void OnFrameCaptured(cricket::VideoCapturer *capturer, const cricket::CapturedFrame *frame) { 40 | g_debug("%s", __FUNCTION__); 41 | } 42 | }; 43 | 44 | PeerManager *PeerManager::Create(const std::string &stunurl) { 45 | return new PeerManagerImp(stunurl); 46 | } 47 | 48 | PeerManagerImp::PeerManagerImp(const std::string &stunurl) : 49 | stunurl(stunurl) 50 | { 51 | signaling_thread = new rtc::Thread(); 52 | worker_thread = new rtc::Thread(); 53 | signaling_thread->Start(); 54 | worker_thread->Start(); 55 | 56 | cricket::WebRtcVideoEncoderFactory *encoder_factory = H264EncoderFactory::Create(); 57 | g_assert(encoder_factory); 58 | 59 | cricket::WebRtcVideoDecoderFactory *decoder_factory = H264DecoderFactory::Create(); 60 | g_assert(decoder_factory); 61 | 62 | peer_connection_factory = webrtc::CreatePeerConnectionFactory( 63 | worker_thread, 64 | signaling_thread, 65 | NULL, 66 | encoder_factory, 67 | decoder_factory 68 | ); 69 | if (!peer_connection_factory.get()) { 70 | g_critical("Failed to initialize PeerConnectionFactory"); 71 | } 72 | } 73 | 74 | PeerManagerImp::~PeerManagerImp() 75 | { 76 | peer_connection_factory = NULL; 77 | } 78 | 79 | void PeerManagerImp::setOffser(const std::string &peerid, const Json::Value &sdp) 80 | { 81 | g_debug("setOffser -> peerid: %s", peerid.c_str()); 82 | 83 | 84 | std::string sdp_type, sdp_offer; 85 | if (!GetStringFromJsonObject(sdp, SDP_TYPE_NAME, &sdp_type) || 86 | !GetStringFromJsonObject(sdp, SDP_NAME, &sdp_offer)) { 87 | g_warning("setOffser <- Can't parse received message."); 88 | return; 89 | } 90 | 91 | webrtc::SdpParseError sdp_parse_error; 92 | webrtc::SessionDescriptionInterface *session_description = webrtc::CreateSessionDescription(sdp_type, sdp_offer, &sdp_parse_error); 93 | if (!session_description) { 94 | g_warning("setOffser <- Can't parse received session description message: %s", sdp_parse_error.description.c_str()); 95 | return; 96 | } 97 | g_debug("From peerid: %s, sdp sdp_type: %s", peerid.c_str(), session_description->type().c_str()); 98 | 99 | std::pair, webrtc::PeerConnectionObserver *> peer_connection = CreatePeerConnection(); 100 | if (!peer_connection.first) { 101 | g_warning("setOffser <- Fail to initialize peer connection"); 102 | return; 103 | } 104 | g_debug("Success to create peer connection"); 105 | 106 | // Set SDP offer to the PeerConnection 107 | rtc::scoped_refptr pc = peer_connection.first; 108 | pc->SetRemoteDescription(SetSDPObserver::Create(), session_description); 109 | 110 | // Register this peer 111 | peer_connection_map.insert(std::pair >(peerid, peer_connection.first)); 112 | peer_connectionobs_map.insert(std::pair(peerid, peer_connection.second)); 113 | 114 | // Create SDP answer 115 | webrtc::FakeConstraints constraints; 116 | constraints.SetMandatoryReceiveAudio(false); 117 | constraints.SetMandatoryReceiveVideo(false); 118 | pc->CreateAnswer(CreateSDPObserver::Create(pc, signal_sdp_feedback), &constraints); 119 | 120 | g_debug("setOffser <- peerid: %s", peerid.c_str()); 121 | } 122 | 123 | std::pair, webrtc::PeerConnectionObserver *> PeerManagerImp::CreatePeerConnection() 124 | { 125 | webrtc::PeerConnectionInterface::IceServers servers; 126 | webrtc::PeerConnectionInterface::IceServer server; 127 | server.uri = "stun:" + stunurl; 128 | servers.push_back(server); 129 | 130 | PeerConnectionObserver *obs = PeerConnectionObserver::Create(signal_candidate_feedback); 131 | rtc::scoped_refptr peer_connection = 132 | peer_connection_factory->CreatePeerConnection(servers, NULL, NULL, NULL, obs); 133 | if (!peer_connection.get()) { 134 | g_warning("CreatePeerConnection failed"); 135 | delete obs; 136 | return std::pair, webrtc::PeerConnectionObserver *>( 137 | NULL, NULL); 138 | } 139 | 140 | AddStreams(peer_connection); 141 | 142 | return std::pair, webrtc::PeerConnectionObserver *>( 143 | peer_connection, obs); 144 | } 145 | 146 | bool PeerManagerImp::AddStreams(webrtc::PeerConnectionInterface *peer_connection) 147 | { 148 | if (media_stream.get() == NULL) { 149 | 150 | cricket::VideoCapturer *capturer = OpenVideoCaptureDevice(); 151 | if (!capturer) { 152 | g_warning("Cannot create capturer"); 153 | return false; 154 | } 155 | 156 | // Register video capturer listener 157 | //VideoCapturerListener listener(capturer); 158 | 159 | // Create media stream 160 | media_stream = peer_connection_factory->CreateLocalMediaStream(STREAM_LABEL); 161 | if (!media_stream.get()) { 162 | g_warning("Fail to create stream"); 163 | return false; 164 | } 165 | 166 | // Create video track 167 | /*webrtc::FakeConstraints video_constraints; 168 | video_constraints.SetMandatoryMinWidth(320); 169 | video_constraints.SetMandatoryMinHeight(480);*/ 170 | rtc::scoped_refptr video_track( 171 | peer_connection_factory->CreateVideoTrack(VIDEO_LABEL, 172 | peer_connection_factory->CreateVideoSource(capturer, 173 | NULL)) 174 | ); 175 | 176 | // Create audio track 177 | rtc::scoped_refptr audio_track( 178 | peer_connection_factory->CreateAudioTrack(AUDIO_LABEL, 179 | peer_connection_factory->CreateAudioSource(NULL)) 180 | ); 181 | 182 | if (!media_stream->AddTrack(video_track)) { 183 | g_warning("Fail to add video track"); 184 | return false; 185 | } 186 | 187 | if (!media_stream->AddTrack(audio_track)) { 188 | g_warning("Fail to add audio track"); 189 | return false; 190 | } 191 | } 192 | 193 | if (!peer_connection->AddStream(media_stream)) { 194 | g_warning("Fail to add media stream to PeerConnection"); 195 | return false; 196 | } 197 | 198 | return true; 199 | 200 | } 201 | 202 | cricket::VideoCapturer *PeerManagerImp::OpenVideoCaptureDevice() 203 | { 204 | cricket::VideoCapturer *capturer = NULL; 205 | 206 | rtc::scoped_ptr dev_manager(cricket::DeviceManagerFactory::Create()); 207 | if (!dev_manager.get() || !dev_manager->Init()) { 208 | g_warning("Fail to create device manager"); 209 | return NULL; 210 | } 211 | 212 | std::vector devs; 213 | if (!dev_manager->GetVideoCaptureDevices(&devs)) { 214 | g_warning("Fail to enumerate video devices"); 215 | return NULL; 216 | } 217 | 218 | // Try to use the normal camera device 219 | for (std::vector::iterator it = devs.begin(); it != devs.end(); ++it) { 220 | cricket::Device device = *it; 221 | capturer = dev_manager->CreateVideoCapturer(device); 222 | if (capturer) { 223 | break; 224 | } else { 225 | g_warning("Fail to create video capture device: %s", it->id.c_str()); 226 | } 227 | } 228 | 229 | // Use the fake yuvframegenerator 230 | if (capturer == NULL) { 231 | cricket::Device device; 232 | if (dev_manager->GetVideoCaptureDevice("YuvFramesGenerator", &device)) { 233 | capturer = dev_manager->CreateVideoCapturer(device); 234 | } 235 | if (capturer == NULL) { 236 | g_warning("Fail to get fake video devices"); 237 | } else { 238 | g_warning("Success to create fake video devices"); 239 | } 240 | } 241 | 242 | if (capturer != NULL) 243 | g_debug("Create capturer device: %s", capturer->GetId().c_str()); 244 | 245 | return capturer; 246 | } 247 | 248 | void PeerManagerImp::deletePeerConnection(const std::string &peerid) 249 | { 250 | std::map >::iterator it = peer_connection_map.find( 251 | peerid); 252 | if (it != peer_connection_map.end()) { 253 | it->second = NULL; 254 | peer_connection_map.erase(it); 255 | } 256 | 257 | std::map::iterator obs_it = peer_connectionobs_map.find(peerid); 258 | if (obs_it == peer_connectionobs_map.end()) { 259 | PeerConnectionObserver *obs = dynamic_cast(obs_it->second); 260 | delete obs; 261 | peer_connectionobs_map.erase(obs_it); 262 | } 263 | } 264 | 265 | void PeerManagerImp::addIceCandidate(const std::string &peerid, const Json::Value &candidate) 266 | { 267 | g_debug("addIceCandidate -> peerid: %s", peerid.c_str()); 268 | 269 | std::map >::iterator it = peer_connection_map.find(peerid); 270 | if (it == peer_connection_map.end()) { 271 | g_warning("addIceCandidate <- Fail to find the existed peer connection."); 272 | return; 273 | } 274 | 275 | std::string sdp_mid, sdp; 276 | int sdp_mlineindex = 0; 277 | if (!GetStringFromJsonObject(candidate, CANDIDATE_SDP_MID_NAME, &sdp_mid) || 278 | !GetIntFromJsonObject(candidate, CANDIDATE_SDP_MLINE_INDEX_NAME, &sdp_mlineindex) || 279 | !GetStringFromJsonObject(candidate, CANDIDATE_SDP_NAME, &sdp)) { 280 | g_warning("addIceCandidate <- Fail to parse received message."); 281 | return; 282 | } 283 | 284 | rtc::scoped_ptr ice_candidate(webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp)); 285 | if (!ice_candidate.get()) { 286 | g_warning("addIceCandidate <- Fail to parse received candidate message."); 287 | return; 288 | } 289 | 290 | rtc::scoped_refptr pc = it->second; 291 | if (!pc->AddIceCandidate(ice_candidate.get())) { 292 | g_warning("addIceCandidate <- Failed to apply the received candidate"); 293 | return; 294 | } 295 | 296 | g_debug("addIceCandidate <- peerid: %s", peerid.c_str()); 297 | } 298 | 299 | } -------------------------------------------------------------------------------- /peer_manager_imp.h: -------------------------------------------------------------------------------- 1 | #ifndef __H264WEBRTC_PEER_MANAGER_IMP_H__ 2 | #define __H264WEBRTC_PEER_MANAGER_IMP_H__ 3 | 4 | #include "peer_manager.h" 5 | 6 | #include "talk/app/webrtc/mediastreaminterface.h" 7 | #include "talk/app/webrtc/peerconnectioninterface.h" 8 | #include "talk/app/webrtc/test/fakeconstraints.h" 9 | 10 | namespace h264webrtc 11 | { 12 | 13 | class PeerManagerImp : public PeerManager 14 | { 15 | public: 16 | PeerManagerImp(const std::string &stunurl); 17 | 18 | ~PeerManagerImp(); 19 | 20 | void setOffser(const std::string &peerid, const Json::Value &sdp); 21 | 22 | void addIceCandidate(const std::string &peerid, const Json::Value &candidate); 23 | 24 | void deletePeerConnection(const std::string &peerid); 25 | 26 | private: 27 | std::pair, webrtc::PeerConnectionObserver *> CreatePeerConnection(); 28 | 29 | bool AddStreams(webrtc::PeerConnectionInterface *peer_connection); 30 | 31 | cricket::VideoCapturer *OpenVideoCaptureDevice(); 32 | 33 | std::string stunurl; 34 | rtc::scoped_refptr peer_connection_factory; 35 | std::map > peer_connection_map; 36 | std::map peer_connectionobs_map; 37 | rtc::Thread *signaling_thread; 38 | rtc::Thread *worker_thread; 39 | rtc::scoped_refptr media_stream; 40 | }; 41 | 42 | } 43 | 44 | #endif //__H264WEBRTC_PEER_MANAGER_IMP_H__ 45 | --------------------------------------------------------------------------------