1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 // 12 // vie_autotest_record.cc 13 // 14 // This code is also used as sample code for ViE 3.0 15 // 16 17 #include <fstream> 18 #include <stdio.h> 19 20 #include "webrtc/common_types.h" 21 #include "webrtc/system_wrappers/interface/tick_util.h" 22 #include "webrtc/test/channel_transport/include/channel_transport.h" 23 #include "webrtc/video_engine/include/vie_base.h" 24 #include "webrtc/video_engine/include/vie_capture.h" 25 #include "webrtc/video_engine/include/vie_codec.h" 26 #include "webrtc/video_engine/include/vie_network.h" 27 #include "webrtc/video_engine/include/vie_render.h" 28 #include "webrtc/video_engine/include/vie_rtp_rtcp.h" 29 #include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h" 30 #include "webrtc/video_engine/test/auto_test/interface/vie_autotest_defines.h" 31 #include "webrtc/video_engine/test/libvietest/include/tb_external_transport.h" 32 #include "webrtc/voice_engine/include/voe_base.h" 33 #include "webrtc/voice_engine/include/voe_network.h" 34 #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" 35 36 #define VCM_RED_PAYLOAD_TYPE 96 37 #define VCM_ULPFEC_PAYLOAD_TYPE 97 38 #define DEFAULT_AUDIO_PORT 11113 39 #define DEFAULT_AUDIO_CODEC "ISAC" 40 #define DEFAULT_VIDEO_CODEC_WIDTH 640 41 #define DEFAULT_VIDEO_CODEC_HEIGHT 480 42 #define DEFAULT_VIDEO_CODEC_START_RATE 1000 43 #define DEFAULT_RECORDING_FOLDER "RECORDING" 44 #define DEFAULT_RECORDING_AUDIO "/audio_debug.aec" 45 #define DEFAULT_RECORDING_VIDEO "/video_debug.yuv" 46 #define DEFAULT_RECORDING_AUDIO_RTP "/audio_rtpdump.rtp" 47 #define DEFAULT_RECORDING_VIDEO_RTP "/video_rtpdump.rtp" 48 49 bool GetAudioDevices(webrtc::VoEBase* voe_base, 50 webrtc::VoEHardware* voe_hardware, 51 char* recording_device_name, 52 int& recording_device_index, 53 char* playbackDeviceName, 54 int& playback_device_index); 55 bool GetAudioCodecRecord(webrtc::VoECodec* voe_codec, 56 webrtc::CodecInst& audio_codec); 57 58 int VideoEngineSampleRecordCode(void* window1, void* window2) { 59 int error = 0; 60 // Audio settings. 61 int audio_tx_port = DEFAULT_AUDIO_PORT; 62 int audio_rx_port = DEFAULT_AUDIO_PORT; 63 webrtc::CodecInst audio_codec; 64 int audio_channel = -1; 65 int audio_capture_device_index = -1; 66 int audio_playback_device_index = -1; 67 const unsigned int KMaxDeviceNameLength = 128; 68 const unsigned int KMaxUniqueIdLength = 256; 69 char deviceName[KMaxDeviceNameLength]; 70 char audio_capture_device_name[KMaxUniqueIdLength] = ""; 71 char audio_playbackDeviceName[KMaxUniqueIdLength] = ""; 72 73 // Network settings. 74 const char* ipAddress = "127.0.0.1"; 75 const int rtpPort = 6000; 76 77 // 78 // Create a VideoEngine instance 79 // 80 webrtc::VideoEngine* ptrViE = NULL; 81 ptrViE = webrtc::VideoEngine::Create(); 82 if (ptrViE == NULL) { 83 printf("ERROR in VideoEngine::Create\n"); 84 return -1; 85 } 86 87 error = ptrViE->SetTraceFilter(webrtc::kTraceAll); 88 if (error == -1) { 89 printf("ERROR in VideoEngine::SetTraceLevel\n"); 90 return -1; 91 } 92 93 std::string trace_file = 94 ViETest::GetResultOutputPath() + "ViERecordCall_trace.txt"; 95 error = ptrViE->SetTraceFile(trace_file.c_str()); 96 if (error == -1) { 97 printf("ERROR in VideoEngine::SetTraceFile\n"); 98 return -1; 99 } 100 101 // 102 // Create a VoE instance 103 // 104 webrtc::VoiceEngine* voe = webrtc::VoiceEngine::Create(); 105 // 106 // Init VideoEngine and create a channel 107 // 108 webrtc::ViEBase* ptrViEBase = webrtc::ViEBase::GetInterface(ptrViE); 109 if (ptrViEBase == NULL) { 110 printf("ERROR in ViEBase::GetInterface\n"); 111 return -1; 112 } 113 114 error = ptrViEBase->Init(); 115 if (error == -1) { 116 printf("ERROR in ViEBase::Init\n"); 117 return -1; 118 } 119 120 webrtc::VoEBase* voe_base = webrtc::VoEBase::GetInterface(voe); 121 if (voe_base == NULL) { 122 printf("ERROR in VoEBase::GetInterface\n"); 123 return -1; 124 } 125 error = voe_base->Init(); 126 if (error == -1) { 127 printf("ERROR in VoEBase::Init\n"); 128 return -1; 129 } 130 131 int videoChannel = -1; 132 error = ptrViEBase->CreateChannel(videoChannel); 133 if (error == -1) { 134 printf("ERROR in ViEBase::CreateChannel\n"); 135 return -1; 136 } 137 138 webrtc::VoEHardware* voe_hardware = 139 webrtc::VoEHardware::GetInterface(voe); 140 webrtc::VoECodec* voe_codec = webrtc::VoECodec::GetInterface(voe); 141 webrtc::VoEAudioProcessing* voe_apm = 142 webrtc::VoEAudioProcessing::GetInterface(voe); 143 webrtc::VoENetwork* voe_network = 144 webrtc::VoENetwork::GetInterface(voe); 145 146 // Get the audio device for the call. 147 memset(audio_capture_device_name, 0, KMaxUniqueIdLength); 148 memset(audio_playbackDeviceName, 0, KMaxUniqueIdLength); 149 GetAudioDevices(voe_base, voe_hardware, audio_capture_device_name, 150 audio_capture_device_index, audio_playbackDeviceName, 151 audio_playback_device_index); 152 153 // Get the audio codec for the call. 154 memset(static_cast<void*>(&audio_codec), 0, sizeof(audio_codec)); 155 GetAudioCodecRecord(voe_codec, audio_codec); 156 157 audio_channel = voe_base->CreateChannel(); 158 159 webrtc::scoped_ptr<webrtc::test::VoiceChannelTransport> 160 voice_channel_transport( 161 new webrtc::test::VoiceChannelTransport(voe_network, audio_channel)); 162 163 voice_channel_transport->SetSendDestination(ipAddress, audio_tx_port); 164 voice_channel_transport->SetLocalReceiver(audio_rx_port); 165 166 voe_hardware->SetRecordingDevice(audio_capture_device_index); 167 voe_hardware->SetPlayoutDevice(audio_playback_device_index); 168 voe_codec->SetSendCodec(audio_channel, audio_codec); 169 voe_apm->SetAgcStatus(true, webrtc::kAgcDefault); 170 voe_apm->SetNsStatus(true, webrtc::kNsHighSuppression); 171 172 // 173 // List available capture devices, allocate and connect. 174 // 175 webrtc::ViECapture* ptrViECapture = 176 webrtc::ViECapture::GetInterface(ptrViE); 177 if (ptrViECapture == NULL) { 178 printf("ERROR in ViECapture::GetInterface\n"); 179 return -1; 180 } 181 182 webrtc::VoERTP_RTCP* ptrVoERtpRtcp = 183 webrtc::VoERTP_RTCP::GetInterface(voe); 184 if (ptrVoERtpRtcp == NULL) { 185 printf("ERROR in VoERTP_RTCP::GetInterface\n"); 186 return -1; 187 } 188 189 memset(deviceName, 0, KMaxDeviceNameLength); 190 char uniqueId[KMaxUniqueIdLength]; 191 memset(uniqueId, 0, KMaxUniqueIdLength); 192 193 printf("Available capture devices:\n"); 194 int captureIdx = 0; 195 for (captureIdx = 0; 196 captureIdx < ptrViECapture->NumberOfCaptureDevices(); 197 captureIdx++) { 198 memset(deviceName, 0, KMaxDeviceNameLength); 199 memset(uniqueId, 0, KMaxUniqueIdLength); 200 201 error = ptrViECapture->GetCaptureDevice(captureIdx, deviceName, 202 KMaxDeviceNameLength, uniqueId, 203 KMaxUniqueIdLength); 204 if (error == -1) { 205 printf("ERROR in ViECapture::GetCaptureDevice\n"); 206 return -1; 207 } 208 printf("\t %d. %s\n", captureIdx + 1, deviceName); 209 } 210 printf("\nChoose capture device: "); 211 #ifdef WEBRTC_ANDROID 212 captureIdx = 0; 213 printf("0\n"); 214 #else 215 if (scanf("%d", &captureIdx) != 1) { 216 printf("Error in scanf()\n"); 217 return -1; 218 } 219 getchar(); 220 captureIdx = captureIdx - 1; // Compensate for idx start at 1. 221 #endif 222 error = ptrViECapture->GetCaptureDevice(captureIdx, deviceName, 223 KMaxDeviceNameLength, uniqueId, 224 KMaxUniqueIdLength); 225 if (error == -1) { 226 printf("ERROR in ViECapture::GetCaptureDevice\n"); 227 return -1; 228 } 229 230 int captureId = 0; 231 error = ptrViECapture->AllocateCaptureDevice(uniqueId, KMaxUniqueIdLength, 232 captureId); 233 if (error == -1) { 234 printf("ERROR in ViECapture::AllocateCaptureDevice\n"); 235 return -1; 236 } 237 238 error = ptrViECapture->ConnectCaptureDevice(captureId, videoChannel); 239 if (error == -1) { 240 printf("ERROR in ViECapture::ConnectCaptureDevice\n"); 241 return -1; 242 } 243 244 error = ptrViECapture->StartCapture(captureId); 245 if (error == -1) { 246 printf("ERROR in ViECapture::StartCapture\n"); 247 return -1; 248 } 249 250 // 251 // RTP/RTCP settings 252 // 253 webrtc::ViERTP_RTCP* ptrViERtpRtcp = 254 webrtc::ViERTP_RTCP::GetInterface(ptrViE); 255 if (ptrViERtpRtcp == NULL) { 256 printf("ERROR in ViERTP_RTCP::GetInterface\n"); 257 return -1; 258 } 259 260 error = ptrViERtpRtcp->SetRTCPStatus(videoChannel, 261 webrtc::kRtcpCompound_RFC4585); 262 if (error == -1) { 263 printf("ERROR in ViERTP_RTCP::SetRTCPStatus\n"); 264 return -1; 265 } 266 267 error = ptrViERtpRtcp->SetKeyFrameRequestMethod( 268 videoChannel, webrtc::kViEKeyFrameRequestPliRtcp); 269 if (error == -1) { 270 printf("ERROR in ViERTP_RTCP::SetKeyFrameRequestMethod\n"); 271 return -1; 272 } 273 274 error = ptrViERtpRtcp->SetRembStatus(videoChannel, true, true); 275 if (error == -1) { 276 printf("ERROR in ViERTP_RTCP::SetTMMBRStatus\n"); 277 return -1; 278 } 279 280 // 281 // Set up rendering 282 // 283 webrtc::ViERender* ptrViERender = webrtc::ViERender::GetInterface(ptrViE); 284 if (ptrViERender == NULL) { 285 printf("ERROR in ViERender::GetInterface\n"); 286 return -1; 287 } 288 289 error = ptrViERender->AddRenderer(captureId, window1, 0, 0.0, 0.0, 1.0, 1.0); 290 if (error == -1) { 291 printf("ERROR in ViERender::AddRenderer\n"); 292 return -1; 293 } 294 295 error = ptrViERender->StartRender(captureId); 296 if (error == -1) { 297 printf("ERROR in ViERender::StartRender\n"); 298 return -1; 299 } 300 301 error = ptrViERender->AddRenderer(videoChannel, window2, 1, 0.0, 0.0, 1.0, 302 1.0); 303 if (error == -1) { 304 printf("ERROR in ViERender::AddRenderer\n"); 305 return -1; 306 } 307 308 error = ptrViERender->StartRender(videoChannel); 309 if (error == -1) { 310 printf("ERROR in ViERender::StartRender\n"); 311 return -1; 312 } 313 314 // 315 // Setup codecs 316 // 317 webrtc::ViECodec* ptrViECodec = webrtc::ViECodec::GetInterface(ptrViE); 318 if (ptrViECodec == NULL) { 319 printf("ERROR in ViECodec::GetInterface\n"); 320 return -1; 321 } 322 323 webrtc::VideoCodec videoCodec; 324 memset(&videoCodec, 0, sizeof(webrtc::VideoCodec)); 325 int codecIdx = 0; 326 327 #ifdef WEBRTC_ANDROID 328 codecIdx = 0; 329 printf("0\n"); 330 #else 331 codecIdx = 0; // Compensate for idx start at 1. 332 #endif 333 334 error = ptrViECodec->GetCodec(codecIdx, videoCodec); 335 if (error == -1) { 336 printf("ERROR in ViECodec::GetCodec\n"); 337 return -1; 338 } 339 340 // Set spatial resolution option 341 videoCodec.width = DEFAULT_VIDEO_CODEC_WIDTH; 342 videoCodec.height = DEFAULT_VIDEO_CODEC_HEIGHT; 343 344 // Set start bit rate 345 videoCodec.startBitrate = DEFAULT_VIDEO_CODEC_START_RATE; 346 347 error = ptrViECodec->SetSendCodec(videoChannel, videoCodec); 348 if (error == -1) { 349 printf("ERROR in ViECodec::SetSendCodec\n"); 350 return -1; 351 } 352 353 // 354 // Address settings 355 // 356 webrtc::ViENetwork* ptrViENetwork = 357 webrtc::ViENetwork::GetInterface(ptrViE); 358 if (ptrViENetwork == NULL) { 359 printf("ERROR in ViENetwork::GetInterface\n"); 360 return -1; 361 } 362 webrtc::test::VideoChannelTransport* video_channel_transport = 363 new webrtc::test::VideoChannelTransport(ptrViENetwork, videoChannel); 364 365 error = video_channel_transport->SetSendDestination(ipAddress, rtpPort); 366 if (error == -1) { 367 printf("ERROR in SetSendDestination\n"); 368 return -1; 369 } 370 error = video_channel_transport->SetLocalReceiver(rtpPort); 371 if (error == -1) { 372 printf("ERROR in SetLocalReceiver\n"); 373 return -1; 374 } 375 376 std::string str; 377 int enable_labeling = 0; 378 std::cout << std::endl; 379 std::cout << "Do you want to label this recording?" << std::endl; 380 std::cout << "0. No (default)." << std::endl; 381 std::cout << "1. This call will be labeled on the fly." << std::endl; 382 std::getline(std::cin, str); 383 enable_labeling = atoi(str.c_str()); 384 385 uint32_t folder_time = static_cast<uint32_t> 386 (webrtc::TickTime::MillisecondTimestamp()); 387 std::stringstream folder_time_str; 388 folder_time_str << folder_time; 389 const std::string folder_name = "recording" + folder_time_str.str(); 390 printf("recording name = %s\n", folder_name.c_str()); 391 // TODO(mikhal): use file_utils. 392 #ifdef WIN32 393 _mkdir(folder_name.c_str()); 394 #else 395 mkdir(folder_name.c_str(), 0777); 396 #endif 397 const std::string audio_filename = folder_name + DEFAULT_RECORDING_AUDIO; 398 const std::string video_filename = folder_name + DEFAULT_RECORDING_VIDEO; 399 const std::string audio_rtp_filename = folder_name + 400 DEFAULT_RECORDING_AUDIO_RTP; 401 const std::string video_rtp_filename = folder_name + 402 DEFAULT_RECORDING_VIDEO_RTP; 403 std::fstream timing; 404 if (enable_labeling == 1) { 405 std::cout << "Press enter to stamp current time."<< std::endl; 406 std::string timing_file = folder_name + "/labeling.txt"; 407 timing.open(timing_file.c_str(), std::fstream::out | std::fstream::app); 408 } 409 printf("\nPress enter to start recording\n"); 410 std::getline(std::cin, str); 411 printf("\nRecording started\n\n"); 412 413 error = ptrViEBase->StartReceive(videoChannel); 414 if (error == -1) { 415 printf("ERROR in ViENetwork::StartReceive\n"); 416 return -1; 417 } 418 419 error = ptrViEBase->StartSend(videoChannel); 420 if (error == -1) { 421 printf("ERROR in ViENetwork::StartSend\n"); 422 return -1; 423 } 424 error = voe_base->StartSend(audio_channel); 425 if (error == -1) { 426 printf("ERROR in VoENetwork::StartSend\n"); 427 return -1; 428 } 429 430 // Engine started 431 432 voe_apm->StartDebugRecording(audio_filename.c_str()); 433 ptrViECodec->StartDebugRecording(videoChannel, video_filename.c_str()); 434 ptrViERtpRtcp->StartRTPDump(videoChannel, 435 video_rtp_filename.c_str(), webrtc::kRtpOutgoing); 436 ptrVoERtpRtcp->StartRTPDump(audio_channel, 437 audio_rtp_filename.c_str(), webrtc::kRtpOutgoing); 438 printf("Press s + enter to stop..."); 439 int64_t clock_time; 440 if (enable_labeling == 1) { 441 clock_time = webrtc::TickTime::MillisecondTimestamp(); 442 timing << clock_time << std::endl; 443 } 444 char c = getchar(); 445 fflush(stdin); 446 while (c != 's') { 447 if (c == '\n' && enable_labeling == 1) { 448 clock_time = webrtc::TickTime::MillisecondTimestamp(); 449 timing << clock_time << std::endl; 450 } 451 c = getchar(); 452 } 453 if (enable_labeling == 1) { 454 clock_time = webrtc::TickTime::MillisecondTimestamp(); 455 timing << clock_time << std::endl; 456 } 457 458 ptrViERtpRtcp->StopRTPDump(videoChannel, webrtc::kRtpOutgoing); 459 ptrVoERtpRtcp->StopRTPDump(audio_channel, webrtc::kRtpOutgoing); 460 voe_apm->StopDebugRecording(); 461 ptrViECodec->StopDebugRecording(videoChannel); 462 if (enable_labeling == 1) 463 timing.close(); 464 465 // Recording finished. Tear down Video Engine. 466 467 error = ptrViEBase->StopReceive(videoChannel); 468 if (error == -1) { 469 printf("ERROR in ViEBase::StopReceive\n"); 470 return -1; 471 } 472 473 error = ptrViEBase->StopSend(videoChannel); 474 if (error == -1) { 475 printf("ERROR in ViEBase::StopSend\n"); 476 return -1; 477 } 478 error = voe_base->StopSend(audio_channel); 479 480 error = ptrViERender->StopRender(captureId); 481 if (error == -1) { 482 printf("ERROR in ViERender::StopRender\n"); 483 return -1; 484 } 485 486 error = ptrViERender->RemoveRenderer(captureId); 487 if (error == -1) { 488 printf("ERROR in ViERender::RemoveRenderer\n"); 489 return -1; 490 } 491 492 error = ptrViERender->StopRender(videoChannel); 493 if (error == -1) { 494 printf("ERROR in ViERender::StopRender\n"); 495 return -1; 496 } 497 498 error = ptrViERender->RemoveRenderer(videoChannel); 499 if (error == -1) { 500 printf("ERROR in ViERender::RemoveRenderer\n"); 501 return -1; 502 } 503 504 error = ptrViECapture->StopCapture(captureId); 505 if (error == -1) { 506 printf("ERROR in ViECapture::StopCapture\n"); 507 return -1; 508 } 509 510 error = ptrViECapture->DisconnectCaptureDevice(videoChannel); 511 if (error == -1) { 512 printf("ERROR in ViECapture::DisconnectCaptureDevice\n"); 513 return -1; 514 } 515 516 error = ptrViECapture->ReleaseCaptureDevice(captureId); 517 if (error == -1) { 518 printf("ERROR in ViECapture::ReleaseCaptureDevice\n"); 519 return -1; 520 } 521 522 error = ptrViEBase->DeleteChannel(videoChannel); 523 if (error == -1) { 524 printf("ERROR in ViEBase::DeleteChannel\n"); 525 return -1; 526 } 527 delete video_channel_transport; 528 529 int remainingInterfaces = 0; 530 remainingInterfaces = ptrViECodec->Release(); 531 remainingInterfaces += ptrViECapture->Release(); 532 remainingInterfaces += ptrViERtpRtcp->Release(); 533 remainingInterfaces += ptrViERender->Release(); 534 remainingInterfaces += ptrViENetwork->Release(); 535 remainingInterfaces += ptrViEBase->Release(); 536 if (remainingInterfaces > 0) { 537 printf("ERROR: Could not release all interfaces\n"); 538 return -1; 539 } 540 bool deleted = webrtc::VideoEngine::Delete(ptrViE); 541 if (deleted == false) { 542 printf("ERROR in VideoEngine::Delete\n"); 543 return -1; 544 } 545 return 0; 546 } 547 548 549 // TODO(mikhal): Place above functionality under this class. 550 int ViEAutoTest::ViERecordCall() { 551 ViETest::Log(" "); 552 ViETest::Log("========================================"); 553 ViETest::Log(" ViE Record Call\n"); 554 555 if (VideoEngineSampleRecordCode(_window1, _window2) == 0) { 556 ViETest::Log(" "); 557 ViETest::Log(" ViE Autotest Record Call Done"); 558 ViETest::Log("========================================"); 559 ViETest::Log(" "); 560 return 0; 561 } 562 563 ViETest::Log(" "); 564 ViETest::Log(" ViE Autotest Record Call Failed"); 565 ViETest::Log("========================================"); 566 ViETest::Log(" "); 567 return 1; 568 } 569 570 bool GetAudioCodecRecord(webrtc::VoECodec* voe_codec, 571 webrtc::CodecInst& audio_codec) { 572 int error = 0; 573 int number_of_errors = 0; 574 memset(&audio_codec, 0, sizeof(webrtc::CodecInst)); 575 576 while (1) { 577 int codec_idx = 0; 578 int default_codec_idx = 0; 579 for (codec_idx = 0; codec_idx < voe_codec->NumOfCodecs(); codec_idx++) { 580 error = voe_codec->GetCodec(codec_idx, audio_codec); 581 number_of_errors += ViETest::TestError(error == 0, 582 "ERROR: %s at line %d", 583 __FUNCTION__, __LINE__); 584 585 // Test for default codec index. 586 if (strcmp(audio_codec.plname, DEFAULT_AUDIO_CODEC) == 0) { 587 default_codec_idx = codec_idx; 588 } 589 } 590 error = voe_codec->GetCodec(default_codec_idx, audio_codec); 591 number_of_errors += ViETest::TestError(error == 0, 592 "ERROR: %s at line %d", 593 __FUNCTION__, __LINE__); 594 return true; 595 } 596 assert(false); 597 return false; 598 } 599