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
49bool 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);
55bool GetAudioCodecRecord(webrtc::VoECodec* voe_codec,
56                         webrtc::CodecInst& audio_codec);
57
58int 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.
550int 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
570bool 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