| /* | 
 |  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include <iostream>  // NOLINT | 
 |  | 
 | #include "webrtc/common_types.h" | 
 | #include "webrtc/video_engine/include/vie_base.h" | 
 | #include "webrtc/video_engine/include/vie_capture.h" | 
 | #include "webrtc/video_engine/include/vie_codec.h" | 
 | #include "webrtc/video_engine/include/vie_network.h" | 
 | #include "webrtc/video_engine/include/vie_render.h" | 
 | #include "webrtc/video_engine/include/vie_rtp_rtcp.h" | 
 | #include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h" | 
 | #include "webrtc/video_engine/test/auto_test/interface/vie_autotest_defines.h" | 
 | #include "webrtc/video_engine/test/libvietest/include/tb_external_transport.h" | 
 | #include "webrtc/voice_engine/include/voe_base.h" | 
 |  | 
 | enum RelayMode { | 
 |   kRelayOneStream = 1, | 
 |   kRelayAllStreams = 2 | 
 | }; | 
 |  | 
 | #define VCM_RED_PAYLOAD_TYPE        96 | 
 | #define VCM_ULPFEC_PAYLOAD_TYPE     97 | 
 |  | 
 | const int kNumStreams = 3; | 
 |  | 
 | void InitialSingleStreamSettings(webrtc::VideoCodec* video_codec) { | 
 |   video_codec->numberOfSimulcastStreams = 0; | 
 |   video_codec->width = 1200; | 
 |   video_codec->height = 800; | 
 | } | 
 |  | 
 | void SetSimulcastSettings(webrtc::VideoCodec* video_codec) { | 
 |   video_codec->width = 1280; | 
 |   video_codec->height = 720; | 
 |  | 
 |   // Simulcast settings. | 
 |   video_codec->numberOfSimulcastStreams = kNumStreams; | 
 |   video_codec->simulcastStream[0].width = 320; | 
 |   video_codec->simulcastStream[0].height = 180; | 
 |   video_codec->simulcastStream[0].numberOfTemporalLayers = 0; | 
 |   video_codec->simulcastStream[0].maxBitrate = 100; | 
 |   video_codec->simulcastStream[0].targetBitrate = 100; | 
 |   video_codec->simulcastStream[0].minBitrate = 0; | 
 |   video_codec->simulcastStream[0].qpMax = video_codec->qpMax; | 
 |  | 
 |   video_codec->simulcastStream[1].width = 640; | 
 |   video_codec->simulcastStream[1].height = 360; | 
 |   video_codec->simulcastStream[1].numberOfTemporalLayers = 0; | 
 |   video_codec->simulcastStream[1].maxBitrate = 500; | 
 |   video_codec->simulcastStream[1].targetBitrate = 500; | 
 |   video_codec->simulcastStream[1].minBitrate = 200; | 
 |   video_codec->simulcastStream[1].qpMax = video_codec->qpMax; | 
 |  | 
 |   video_codec->simulcastStream[2].width = 1280; | 
 |   video_codec->simulcastStream[2].height = 720; | 
 |   video_codec->simulcastStream[2].numberOfTemporalLayers = 0; | 
 |   video_codec->simulcastStream[2].maxBitrate = 1200; | 
 |   video_codec->simulcastStream[2].targetBitrate = 1200; | 
 |   video_codec->simulcastStream[2].minBitrate = 900; | 
 |   video_codec->simulcastStream[2].qpMax = video_codec->qpMax; | 
 | } | 
 |  | 
 | void RuntimeSingleStreamSettings(webrtc::VideoCodec* video_codec) { | 
 |   SetSimulcastSettings(video_codec); | 
 |   video_codec->width = 1200; | 
 |   video_codec->height = 800; | 
 |   video_codec->numberOfSimulcastStreams = kNumStreams; | 
 |   video_codec->simulcastStream[0].maxBitrate = 0; | 
 |   video_codec->simulcastStream[0].targetBitrate = 0; | 
 |   video_codec->simulcastStream[0].minBitrate = 0; | 
 |   video_codec->simulcastStream[1].maxBitrate = 0; | 
 |   video_codec->simulcastStream[1].targetBitrate = 0; | 
 |   video_codec->simulcastStream[1].minBitrate = 0; | 
 |   video_codec->simulcastStream[2].maxBitrate = 0; | 
 |   video_codec->simulcastStream[2].targetBitrate = 0; | 
 |   video_codec->simulcastStream[2].minBitrate = 0; | 
 | } | 
 |  | 
 | int VideoEngineSimulcastTest(void* window1, void* window2) { | 
 |   // ******************************************************* | 
 |   //  Begin create/initialize Video Engine for testing | 
 |   // ******************************************************* | 
 |  | 
 |   int error = 0; | 
 |   int receive_channels[kNumStreams]; | 
 |  | 
 |   // Create a VideoEngine instance. | 
 |   webrtc::VideoEngine* video_engine = NULL; | 
 |   video_engine = webrtc::VideoEngine::Create(); | 
 |   if (video_engine == NULL) { | 
 |     printf("ERROR in VideoEngine::Create\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = video_engine->SetTraceFilter(webrtc::kTraceAll); | 
 |   if (error == -1) { | 
 |     printf("ERROR in VideoEngine::SetTraceLevel\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   std::string trace_file = | 
 |     ViETest::GetResultOutputPath() + "ViESimulcast_trace.txt"; | 
 |   error = video_engine->SetTraceFile(trace_file.c_str()); | 
 |   if (error == -1) { | 
 |     printf("ERROR in VideoEngine::SetTraceFile\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // Init VideoEngine and create a channel. | 
 |   webrtc::ViEBase* vie_base = webrtc::ViEBase::GetInterface(video_engine); | 
 |   if (vie_base == NULL) { | 
 |     printf("ERROR in ViEBase::GetInterface\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_base->Init(); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViEBase::Init\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   RelayMode relay_mode = kRelayOneStream; | 
 |   printf("Select relay mode:\n"); | 
 |   printf("\t1. Relay one stream\n"); | 
 |   printf("\t2. Relay all streams\n"); | 
 |   if (scanf("%d", reinterpret_cast<int*>(&relay_mode)) != 1) { | 
 |     printf("Error in scanf()\n"); | 
 |     return -1; | 
 |   } | 
 |   getchar(); | 
 |  | 
 |   webrtc::ViERTP_RTCP* vie_rtp_rtcp = | 
 |       webrtc::ViERTP_RTCP::GetInterface(video_engine); | 
 |   if (vie_rtp_rtcp == NULL) { | 
 |     printf("ERROR in ViERTP_RTCP::GetInterface\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   int video_channel = -1; | 
 |   error = vie_base->CreateChannel(video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViEBase::CreateChannel\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   for (int i = 0; i < kNumStreams; ++i) { | 
 |     receive_channels[i] = -1; | 
 |     error = vie_base->CreateReceiveChannel(receive_channels[i], video_channel); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViEBase::CreateChannel\n"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   // List available capture devices, allocate and connect. | 
 |   webrtc::ViECapture* vie_capture = | 
 |       webrtc::ViECapture::GetInterface(video_engine); | 
 |   if (vie_base == NULL) { | 
 |     printf("ERROR in ViECapture::GetInterface\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   const unsigned int KMaxDeviceNameLength = 128; | 
 |   const unsigned int KMaxUniqueIdLength = 256; | 
 |   char device_name[KMaxDeviceNameLength]; | 
 |   memset(device_name, 0, KMaxDeviceNameLength); | 
 |   char unique_id[KMaxUniqueIdLength]; | 
 |   memset(unique_id, 0, KMaxUniqueIdLength); | 
 |  | 
 |   printf("Available capture devices:\n"); | 
 |   int capture_idx = 0; | 
 |   for (capture_idx = 0; capture_idx < vie_capture->NumberOfCaptureDevices(); | 
 |        capture_idx++) { | 
 |     memset(device_name, 0, KMaxDeviceNameLength); | 
 |     memset(unique_id, 0, KMaxUniqueIdLength); | 
 |  | 
 |     error = vie_capture->GetCaptureDevice(capture_idx, device_name, | 
 |                                           KMaxDeviceNameLength, unique_id, | 
 |                                           KMaxUniqueIdLength); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViECapture::GetCaptureDevice\n"); | 
 |       return -1; | 
 |     } | 
 |     printf("\t %d. %s\n", capture_idx + 1, device_name); | 
 |   } | 
 |   printf("\nChoose capture device: "); | 
 | #ifdef WEBRTC_ANDROID | 
 |   capture_idx = 0; | 
 |   printf("0\n"); | 
 | #else | 
 |   if (scanf("%d", &capture_idx) != 1) { | 
 |     printf("Error in scanf()\n"); | 
 |     return -1; | 
 |   } | 
 |   getchar(); | 
 |   // Compensate for idx start at 1. | 
 |   capture_idx = capture_idx - 1; | 
 | #endif | 
 |   error = vie_capture->GetCaptureDevice(capture_idx, device_name, | 
 |                                         KMaxDeviceNameLength, unique_id, | 
 |                                         KMaxUniqueIdLength); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECapture::GetCaptureDevice\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   int capture_id = 0; | 
 |   error = vie_capture->AllocateCaptureDevice(unique_id, KMaxUniqueIdLength, | 
 |                                              capture_id); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECapture::AllocateCaptureDevice\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_capture->ConnectCaptureDevice(capture_id, video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECapture::ConnectCaptureDevice\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_capture->StartCapture(capture_id); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECapture::StartCapture\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // RTP/RTCP settings. | 
 |   error = vie_rtp_rtcp->SetRTCPStatus(video_channel, | 
 |                                       webrtc::kRtcpCompound_RFC4585); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERTP_RTCP::SetRTCPStatus\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   vie_rtp_rtcp->SetRembStatus(video_channel, true, false); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERTP_RTCP::SetRTCPStatus\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_rtp_rtcp->SetKeyFrameRequestMethod( | 
 |             video_channel, webrtc::kViEKeyFrameRequestPliRtcp); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERTP_RTCP::SetKeyFrameRequestMethod\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   for (int i = 0; i < kNumStreams; ++i) { | 
 |     error = vie_rtp_rtcp->SetRTCPStatus(receive_channels[i], | 
 |                                         webrtc::kRtcpCompound_RFC4585); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViERTP_RTCP::SetRTCPStatus\n"); | 
 |       return -1; | 
 |     } | 
 |  | 
 |     vie_rtp_rtcp->SetRembStatus(receive_channels[i], false, true); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViERTP_RTCP::SetRTCPStatus\n"); | 
 |       return -1; | 
 |     } | 
 |  | 
 |     error = vie_rtp_rtcp->SetKeyFrameRequestMethod( | 
 |         receive_channels[i], webrtc::kViEKeyFrameRequestPliRtcp); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViERTP_RTCP::SetKeyFrameRequestMethod\n"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   // Set up rendering. | 
 |   webrtc::ViERender* vie_render = webrtc::ViERender::GetInterface(video_engine); | 
 |   if (vie_render == NULL) { | 
 |     printf("ERROR in ViERender::GetInterface\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_render->AddRenderer(capture_id, window1, 0, 0.0, 0.0, 1.0, 1.0); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::AddRenderer\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_render->StartRender(capture_id); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::StartRender\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // Only rendering the thumbnail. | 
 |   int channel_to_render = video_channel; | 
 |   if (relay_mode == kRelayAllStreams) { | 
 |     channel_to_render = receive_channels[0]; | 
 |   } | 
 |   error = vie_render->AddRenderer(channel_to_render, window2, 1, 0.0, 0.0, 1.0, | 
 |                                   1.0); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::AddRenderer\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_render->StartRender(channel_to_render); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::StartRender\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // Setup codecs. | 
 |   webrtc::ViECodec* vie_codec = webrtc::ViECodec::GetInterface(video_engine); | 
 |   if (vie_codec == NULL) { | 
 |     printf("ERROR in ViECodec::GetInterface\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // Check available codecs and prepare receive codecs. | 
 |   printf("\nAvailable codecs:\n"); | 
 |   webrtc::VideoCodec video_codec; | 
 |   memset(&video_codec, 0, sizeof(webrtc::VideoCodec)); | 
 |   int codec_idx = 0; | 
 |   for (codec_idx = 0; codec_idx < vie_codec->NumberOfCodecs(); codec_idx++) { | 
 |     error = vie_codec->GetCodec(codec_idx, video_codec); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViECodec::GetCodec\n"); | 
 |       return -1; | 
 |     } | 
 |     // Try to keep the test frame size small when I420. | 
 |     if (video_codec.codecType != webrtc::kVideoCodecVP8) { | 
 |       continue; | 
 |     } | 
 |     for (int i = 0; i < kNumStreams; ++i) { | 
 |       error = vie_codec->SetReceiveCodec(receive_channels[i], video_codec); | 
 |       if (error == -1) { | 
 |         printf("ERROR in ViECodec::SetReceiveCodec\n"); | 
 |         return -1; | 
 |       } | 
 |     } | 
 |     if (video_codec.codecType != webrtc::kVideoCodecRED && | 
 |         video_codec.codecType != webrtc::kVideoCodecULPFEC) { | 
 |       printf("\t %d. %s\n", codec_idx + 1, video_codec.plName); | 
 |     } | 
 |     break; | 
 |   } | 
 |   error = vie_codec->GetCodec(codec_idx, video_codec); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECodec::GetCodec\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   bool simulcast_mode = true; | 
 |   int num_streams = 1; | 
 |   // Set spatial resolution option. | 
 |   if (simulcast_mode) { | 
 |     SetSimulcastSettings(&video_codec); | 
 |     num_streams = video_codec.numberOfSimulcastStreams; | 
 |   } else { | 
 |     InitialSingleStreamSettings(&video_codec); | 
 |     num_streams = 1; | 
 |   } | 
 |  | 
 |   // Set start bit rate. | 
 |   std::string str; | 
 |   std::cout << std::endl; | 
 |   std::cout << "Choose start rate (in kbps). Press enter for default:  "; | 
 |   std::getline(std::cin, str); | 
 |   int start_rate = atoi(str.c_str()); | 
 |   if (start_rate != 0) { | 
 |     video_codec.startBitrate = start_rate; | 
 |   } | 
 |  | 
 |   error = vie_codec->SetSendCodec(video_channel, video_codec); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECodec::SetSendCodec\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // Address settings. | 
 |   webrtc::ViENetwork* vie_network = | 
 |       webrtc::ViENetwork::GetInterface(video_engine); | 
 |   if (vie_network == NULL) { | 
 |     printf("ERROR in ViENetwork::GetInterface\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   TbExternalTransport::SsrcChannelMap ssrc_channel_map; | 
 |   for (int idx = 0; idx < num_streams; idx++) { | 
 |     error = vie_rtp_rtcp->SetLocalSSRC(video_channel, idx + 1,  // SSRC | 
 |                                        webrtc::kViEStreamTypeNormal, idx); | 
 |     ssrc_channel_map[idx + 1] = receive_channels[idx]; | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViERTP_RTCP::SetLocalSSRC(idx:%d)\n", | 
 |              idx); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   TbExternalTransport::SsrcChannelMap* channel_map = &ssrc_channel_map; | 
 |   if (relay_mode == kRelayOneStream) { | 
 |     channel_map = NULL; | 
 |   } | 
 |  | 
 |   // Setting External transport. | 
 |   TbExternalTransport ext_transport(*vie_network, video_channel, channel_map); | 
 |  | 
 |   error = vie_network->RegisterSendTransport(video_channel, ext_transport); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECodec::RegisterSendTransport \n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   for (int i = 0; i < kNumStreams; ++i) { | 
 |     error = vie_network->RegisterSendTransport(receive_channels[i], | 
 |                                                ext_transport); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViECodec::RegisterSendTransport \n"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   // Set network one-way delay value. | 
 |   // 10 ms one-way delay. | 
 |   NetworkParameters network; | 
 |   network.loss_model = kUniformLoss; | 
 |   network.mean_one_way_delay = 10; | 
 |   ext_transport.SetNetworkParameters(network); | 
 |  | 
 |   if (relay_mode == kRelayOneStream) { | 
 |     ext_transport.SetSSRCFilter(num_streams); | 
 |   } | 
 |  | 
 |   error = vie_base->StartSend(video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViENetwork::StartSend\n"); | 
 |     return -1; | 
 |   } | 
 |   error = vie_base->StartReceive(video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViENetwork::StartReceive\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   for (int i = 0; i < kNumStreams; ++i) { | 
 |     error = vie_base->StartReceive(receive_channels[i]); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViENetwork::StartReceive\n"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   // Create a receive channel to verify that it doesn't mess up toggling | 
 |   // between single stream and simulcast. | 
 |   int video_channel2 = -1; | 
 |   error = vie_base->CreateReceiveChannel(video_channel2, video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViEBase::CreateReceiveChannel\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // ******************************************************* | 
 |   //  Engine started | 
 |   // ******************************************************* | 
 |  | 
 |   printf("\nSimulcast call started\n\n"); | 
 |   do { | 
 |     printf("Enter new SSRC filter 1,2 or 3\n"); | 
 |     printf("... or 0 to switch between simulcast and a single stream\n"); | 
 |     printf("Press enter to stop..."); | 
 |     str.clear(); | 
 |     std::getline(std::cin, str); | 
 |     if (!str.empty()) { | 
 |       int ssrc = atoi(str.c_str()); | 
 |       if (ssrc == 0) { | 
 |         // Toggle between simulcast and a single stream with different | 
 |         // resolution. | 
 |         if (simulcast_mode) { | 
 |           RuntimeSingleStreamSettings(&video_codec); | 
 |           num_streams = 1; | 
 |           printf("Disabling simulcast\n"); | 
 |         } else { | 
 |           SetSimulcastSettings(&video_codec); | 
 |           num_streams = video_codec.numberOfSimulcastStreams; | 
 |           printf("Enabling simulcast\n"); | 
 |         } | 
 |         simulcast_mode = !simulcast_mode; | 
 |         if (vie_codec->SetSendCodec(video_channel, video_codec) != 0) { | 
 |           printf("ERROR switching between simulcast and single stream\n"); | 
 |           return -1; | 
 |         } | 
 |         for (int idx = 0; idx < num_streams; idx++) { | 
 |           error = vie_rtp_rtcp->SetLocalSSRC(video_channel, idx + 1,  // SSRC | 
 |                                              webrtc::kViEStreamTypeNormal, idx); | 
 |           if (error == -1) { | 
 |             printf("ERROR in ViERTP_RTCP::SetLocalSSRC(idx:%d)\n", idx); | 
 |             return -1; | 
 |           } | 
 |         } | 
 |         if (relay_mode == kRelayOneStream) { | 
 |           ext_transport.SetSSRCFilter(num_streams); | 
 |         } | 
 |       } else if (ssrc > 0 && ssrc < 4) { | 
 |         if (relay_mode == kRelayOneStream) { | 
 |           ext_transport.SetSSRCFilter(ssrc); | 
 |         } | 
 |       } else { | 
 |         printf("Invalid SSRC\n"); | 
 |       } | 
 |     } else { | 
 |       break; | 
 |     } | 
 |   } while (true); | 
 |  | 
 |   // ******************************************************* | 
 |   //  Testing finished. Tear down Video Engine | 
 |   // ******************************************************* | 
 |   error = vie_base->DeleteChannel(video_channel2); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViEBase::DeleteChannel\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   for (int i = 0; i < kNumStreams; ++i) { | 
 |     error = vie_base->StopReceive(receive_channels[i]); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViEBase::StopReceive\n"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   error = vie_base->StopReceive(video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViEBase::StopReceive\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_base->StopSend(video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViEBase::StopSend\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_render->StopRender(capture_id); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::StopRender\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_render->RemoveRenderer(capture_id); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::RemoveRenderer\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_render->StopRender(channel_to_render); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::StopRender\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_render->RemoveRenderer(channel_to_render); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViERender::RemoveRenderer\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_capture->StopCapture(capture_id); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECapture::StopCapture\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_capture->DisconnectCaptureDevice(video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECapture::DisconnectCaptureDevice\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   error = vie_capture->ReleaseCaptureDevice(capture_id); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViECapture::ReleaseCaptureDevice\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   for (int i = 0; i < kNumStreams; ++i) { | 
 |     error = vie_base->DeleteChannel(receive_channels[i]); | 
 |     if (error == -1) { | 
 |       printf("ERROR in ViEBase::DeleteChannel\n"); | 
 |       return -1; | 
 |     } | 
 |   } | 
 |  | 
 |   error = vie_base->DeleteChannel(video_channel); | 
 |   if (error == -1) { | 
 |     printf("ERROR in ViEBase::DeleteChannel\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   int remaining_interfaces = 0; | 
 |   remaining_interfaces = vie_codec->Release(); | 
 |   remaining_interfaces += vie_capture->Release(); | 
 |   remaining_interfaces += vie_rtp_rtcp->Release(); | 
 |   remaining_interfaces += vie_render->Release(); | 
 |   remaining_interfaces += vie_network->Release(); | 
 |   remaining_interfaces += vie_base->Release(); | 
 |   if (remaining_interfaces > 0) { | 
 |     printf("ERROR: Could not release all interfaces\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   bool deleted = webrtc::VideoEngine::Delete(video_engine); | 
 |   if (deleted == false) { | 
 |     printf("ERROR in VideoEngine::Delete\n"); | 
 |     return -1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int ViEAutoTest::ViESimulcastCall() { | 
 |   ViETest::Log(" "); | 
 |   ViETest::Log("========================================"); | 
 |   ViETest::Log(" ViE Autotest Simulcast Call\n"); | 
 |  | 
 |   if (VideoEngineSimulcastTest(_window1, _window2) == 0) { | 
 |     ViETest::Log(" "); | 
 |     ViETest::Log(" ViE Autotest Simulcast Call Done"); | 
 |     ViETest::Log("========================================"); | 
 |     ViETest::Log(" "); | 
 |  | 
 |     return 0; | 
 |   } | 
 |   ViETest::Log(" "); | 
 |   ViETest::Log(" ViE Autotest Simulcast Call Failed"); | 
 |   ViETest::Log("========================================"); | 
 |   ViETest::Log(" "); | 
 |   return 1; | 
 | } |