andrew@webrtc.org | a7b57da | 2012-10-22 18:19:23 +0000 | [diff] [blame] | 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 | #include "video_engine/vie_file_recorder.h" |
| 12 | |
| 13 | #include "modules/utility/interface/file_player.h" |
| 14 | #include "modules/utility/interface/file_recorder.h" |
| 15 | #include "system_wrappers/interface/critical_section_wrapper.h" |
| 16 | #include "system_wrappers/interface/tick_util.h" |
| 17 | #include "system_wrappers/interface/trace.h" |
| 18 | #include "video_engine/vie_defines.h" |
| 19 | |
| 20 | namespace webrtc { |
| 21 | |
| 22 | ViEFileRecorder::ViEFileRecorder(int instanceID) |
| 23 | : recorder_cs_(CriticalSectionWrapper::CreateCriticalSection()), |
| 24 | file_recorder_(NULL), |
| 25 | is_first_frame_recorded_(false), |
| 26 | is_out_stream_started_(false), |
| 27 | instance_id_(instanceID), |
| 28 | frame_delay_(0), |
| 29 | audio_channel_(-1), |
| 30 | audio_source_(NO_AUDIO), |
| 31 | voe_file_interface_(NULL) { |
| 32 | } |
| 33 | |
| 34 | ViEFileRecorder::~ViEFileRecorder() { |
| 35 | StopRecording(); |
| 36 | delete recorder_cs_; |
| 37 | } |
| 38 | |
| 39 | int ViEFileRecorder::StartRecording(const char* file_nameUTF8, |
| 40 | const VideoCodec& codec_inst, |
| 41 | AudioSource audio_source, |
| 42 | int audio_channel, |
| 43 | const CodecInst& audio_codec_inst, |
| 44 | VoiceEngine* voe_ptr, |
| 45 | const FileFormats file_format) { |
| 46 | CriticalSectionScoped lock(recorder_cs_); |
| 47 | |
| 48 | if (file_recorder_) { |
| 49 | WEBRTC_TRACE(kTraceError, kTraceVideo, instance_id_, |
| 50 | "ViEFileRecorder::StartRecording() - already recording."); |
| 51 | return -1; |
| 52 | } |
| 53 | file_recorder_ = FileRecorder::CreateFileRecorder(instance_id_, file_format); |
| 54 | if (!file_recorder_) { |
| 55 | WEBRTC_TRACE(kTraceError, kTraceVideo, instance_id_, |
| 56 | "ViEFileRecorder::StartRecording() failed to create recoder."); |
| 57 | return -1; |
| 58 | } |
| 59 | |
| 60 | int error = file_recorder_->StartRecordingVideoFile(file_nameUTF8, |
| 61 | audio_codec_inst, |
| 62 | codec_inst, |
| 63 | AMRFileStorage, |
| 64 | audio_source == NO_AUDIO); |
| 65 | if (error) { |
| 66 | WEBRTC_TRACE(kTraceError, kTraceVideo, instance_id_, |
| 67 | "ViEFileRecorder::StartRecording() failed to " |
| 68 | "StartRecordingVideoFile."); |
| 69 | FileRecorder::DestroyFileRecorder(file_recorder_); |
| 70 | file_recorder_ = NULL; |
| 71 | return -1; |
| 72 | } |
| 73 | |
| 74 | audio_source_ = audio_source; |
| 75 | if (voe_ptr && audio_source != NO_AUDIO) { |
| 76 | // VoE interface has been provided and we want to record audio. |
| 77 | voe_file_interface_ = VoEFile::GetInterface(voe_ptr); |
| 78 | if (!voe_file_interface_) { |
| 79 | WEBRTC_TRACE(kTraceError, kTraceVideo, instance_id_, |
| 80 | "ViEFileRecorder::StartRecording() failed to get VEFile " |
| 81 | "interface"); |
| 82 | return -1; |
| 83 | } |
| 84 | |
| 85 | // Always L16. |
| 86 | CodecInst engine_audio_codec_inst = {96, "L16", audio_codec_inst.plfreq, |
| 87 | audio_codec_inst.plfreq / 100, 1, |
| 88 | audio_codec_inst.plfreq * 16 }; |
| 89 | |
| 90 | switch (audio_source) { |
| 91 | // case NO_AUDIO is checked above. |
| 92 | case MICROPHONE: |
| 93 | error = voe_file_interface_->StartRecordingMicrophone( |
| 94 | this, &engine_audio_codec_inst); |
| 95 | break; |
| 96 | case PLAYOUT: |
| 97 | error = voe_file_interface_->StartRecordingPlayout( |
| 98 | audio_channel, this, &engine_audio_codec_inst); |
| 99 | break; |
| 100 | default: |
| 101 | assert(false && "Unknown audio_source"); |
| 102 | } |
| 103 | if (error != 0) { |
| 104 | WEBRTC_TRACE(kTraceError, kTraceVideo, instance_id_, |
| 105 | "ViEFileRecorder::StartRecording() failed to start recording" |
| 106 | " audio"); |
| 107 | FileRecorder::DestroyFileRecorder(file_recorder_); |
| 108 | file_recorder_ = NULL; |
| 109 | return -1; |
| 110 | } |
| 111 | is_out_stream_started_ = true; |
| 112 | audio_channel_ = audio_channel; |
| 113 | } |
| 114 | is_first_frame_recorded_ = false; |
| 115 | return 0; |
| 116 | } |
| 117 | |
| 118 | int ViEFileRecorder::StopRecording() { |
| 119 | int error = 0; |
| 120 | // We can not hold the ptr_cs_ while accessing VoE functions. It might cause |
| 121 | // deadlock in Write. |
| 122 | if (voe_file_interface_) { |
| 123 | switch (audio_source_) { |
| 124 | case MICROPHONE: |
| 125 | error = voe_file_interface_->StopRecordingMicrophone(); |
| 126 | break; |
| 127 | case PLAYOUT: |
| 128 | error = voe_file_interface_->StopRecordingPlayout(audio_channel_); |
| 129 | break; |
| 130 | case NO_AUDIO: |
| 131 | break; |
| 132 | default: |
| 133 | assert(false && "Unknown audio_source"); |
| 134 | } |
| 135 | if (error != 0) { |
| 136 | WEBRTC_TRACE(kTraceError, kTraceVideo, instance_id_, |
| 137 | "ViEFileRecorder::StopRecording() failed to stop recording " |
| 138 | "audio"); |
| 139 | } |
| 140 | } |
| 141 | CriticalSectionScoped lock(recorder_cs_); |
| 142 | if (voe_file_interface_) { |
| 143 | voe_file_interface_->Release(); |
| 144 | voe_file_interface_ = NULL; |
| 145 | } |
| 146 | |
| 147 | if (file_recorder_) { |
| 148 | if (file_recorder_->IsRecording()) { |
| 149 | int error = file_recorder_->StopRecording(); |
| 150 | if (error) { |
| 151 | return -1; |
| 152 | } |
| 153 | } |
| 154 | FileRecorder::DestroyFileRecorder(file_recorder_); |
| 155 | file_recorder_ = NULL; |
| 156 | } |
| 157 | is_first_frame_recorded_ = false; |
| 158 | is_out_stream_started_ = false; |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | void ViEFileRecorder::SetFrameDelay(int frame_delay) { |
| 163 | CriticalSectionScoped lock(recorder_cs_); |
| 164 | frame_delay_ = frame_delay; |
| 165 | } |
| 166 | |
| 167 | bool ViEFileRecorder::RecordingStarted() { |
| 168 | CriticalSectionScoped lock(recorder_cs_); |
| 169 | return file_recorder_ && file_recorder_->IsRecording(); |
| 170 | } |
| 171 | |
| 172 | bool ViEFileRecorder::FirstFrameRecorded() { |
| 173 | CriticalSectionScoped lock(recorder_cs_); |
| 174 | return is_first_frame_recorded_; |
| 175 | } |
| 176 | |
| 177 | bool ViEFileRecorder::IsRecordingFileFormat(const FileFormats file_format) { |
| 178 | CriticalSectionScoped lock(recorder_cs_); |
| 179 | return (file_recorder_->RecordingFileFormat() == file_format) ? true : false; |
| 180 | } |
| 181 | |
mikhal@webrtc.org | dc7e6cf | 2012-10-24 18:33:04 +0000 | [diff] [blame] | 182 | void ViEFileRecorder::RecordVideoFrame(const I420VideoFrame& video_frame) { |
andrew@webrtc.org | a7b57da | 2012-10-22 18:19:23 +0000 | [diff] [blame] | 183 | CriticalSectionScoped lock(recorder_cs_); |
| 184 | |
| 185 | if (file_recorder_ && file_recorder_->IsRecording()) { |
| 186 | if (!IsRecordingFileFormat(kFileFormatAviFile)) |
| 187 | return; |
| 188 | |
| 189 | // Compensate for frame delay in order to get audio/video sync when |
| 190 | // recording local video. |
mikhal@webrtc.org | dc7e6cf | 2012-10-24 18:33:04 +0000 | [diff] [blame] | 191 | const WebRtc_UWord32 time_stamp = video_frame.timestamp(); |
| 192 | const WebRtc_Word64 render_time_stamp = video_frame.render_time_ms(); |
| 193 | I420VideoFrame& unconst_video_frame = |
| 194 | const_cast<I420VideoFrame&>(video_frame); |
| 195 | unconst_video_frame.set_timestamp(time_stamp - 90 * frame_delay_); |
| 196 | unconst_video_frame.set_render_time_ms(render_time_stamp - frame_delay_); |
andrew@webrtc.org | a7b57da | 2012-10-22 18:19:23 +0000 | [diff] [blame] | 197 | |
| 198 | file_recorder_->RecordVideoToFile(unconst_video_frame); |
| 199 | |
mikhal@webrtc.org | dc7e6cf | 2012-10-24 18:33:04 +0000 | [diff] [blame] | 200 | unconst_video_frame.set_render_time_ms(render_time_stamp); |
| 201 | unconst_video_frame.set_timestamp(time_stamp); |
andrew@webrtc.org | a7b57da | 2012-10-22 18:19:23 +0000 | [diff] [blame] | 202 | } |
| 203 | } |
| 204 | |
| 205 | bool ViEFileRecorder::Write(const void* buf, int len) { |
| 206 | if (!is_out_stream_started_) |
| 207 | return true; |
| 208 | |
| 209 | // Always 10 ms L16 from VoE. |
| 210 | if (len % (2 * 80)) { |
| 211 | // Not 2 bytes 80 samples. |
| 212 | WEBRTC_TRACE(kTraceError, kTraceVideo, audio_channel_, |
| 213 | "Audio length not supported: %d.", len); |
| 214 | return true; |
| 215 | } |
| 216 | |
| 217 | AudioFrame audio_frame; |
| 218 | WebRtc_UWord16 length_in_samples = len / 2; |
| 219 | audio_frame.UpdateFrame(audio_channel_, 0, |
| 220 | static_cast<const WebRtc_Word16*>(buf), |
| 221 | length_in_samples, length_in_samples * 100, |
| 222 | AudioFrame::kUndefined, |
| 223 | AudioFrame::kVadUnknown); |
| 224 | |
| 225 | CriticalSectionScoped lock(recorder_cs_); |
| 226 | if (file_recorder_ && file_recorder_->IsRecording()) { |
| 227 | TickTime tick_time = TickTime::Now(); |
| 228 | file_recorder_->RecordAudioToFile(audio_frame, &tick_time); |
| 229 | } |
| 230 | |
| 231 | // Always return true to continue recording. |
| 232 | return true; |
| 233 | } |
| 234 | |
| 235 | int ViEFileRecorder::Rewind() { |
| 236 | // Not supported! |
| 237 | return -1; |
| 238 | } |
| 239 | |
| 240 | } // namespace webrtc |