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
VideoEngineSampleRecordCode(void * window1,void * window2)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.
ViERecordCall()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
GetAudioCodecRecord(webrtc::VoECodec * voe_codec,webrtc::CodecInst & audio_codec)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