Bug 797671: Import Webrtc.org code from stable branch 3.12 (rev 2820) rs=jesup
[gecko.git] / media / webrtc / trunk / src / video_engine / vie_file_player.cc
blob1f01cad00cc6d2910fc65e7336f274afc984fbe9
1 /*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
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 */
11 #include "video_engine/vie_file_player.h"
13 #include "modules/utility/interface/file_player.h"
14 #include "system_wrappers/interface/critical_section_wrapper.h"
15 #include "system_wrappers/interface/event_wrapper.h"
16 #include "system_wrappers/interface/thread_wrapper.h"
17 #include "system_wrappers/interface/tick_util.h"
18 #include "system_wrappers/interface/trace.h"
19 #include "video_engine/include/vie_file.h"
20 #include "video_engine/vie_defines.h"
21 #include "voice_engine/include/voe_base.h"
22 #include "voice_engine/include/voe_file.h"
23 #include "voice_engine/include/voe_video_sync.h"
25 namespace webrtc {
27 const int kThreadWaitTimeMs = 100;
29 ViEFilePlayer* ViEFilePlayer::CreateViEFilePlayer(
30 int file_id,
31 int engine_id,
32 const char* file_nameUTF8,
33 const bool loop,
34 const FileFormats file_format,
35 VoiceEngine* voe_ptr) {
36 ViEFilePlayer* self = new ViEFilePlayer(file_id, engine_id);
37 if (!self || self->Init(file_nameUTF8, loop, file_format, voe_ptr) != 0) {
38 delete self;
39 self = NULL;
41 return self;
44 ViEFilePlayer::ViEFilePlayer(int Id,
45 int engine_id)
46 : ViEFrameProviderBase(Id, engine_id),
47 play_back_started_(false),
48 feedback_cs_(NULL),
49 audio_cs_(NULL),
50 file_player_(NULL),
51 audio_stream_(false),
52 video_clients_(0),
53 audio_clients_(0),
54 local_audio_channel_(-1),
55 observer_(NULL),
56 voe_file_interface_(NULL),
57 voe_video_sync_(NULL),
58 decode_thread_(NULL),
59 decode_event_(NULL),
60 decoded_audio_length_(0) {
61 memset(file_name_, 0, FileWrapper::kMaxFileNameSize);
62 memset(decoded_audio_, 0, kMaxDecodedAudioLength);
65 ViEFilePlayer::~ViEFilePlayer() {
66 // StopPlay deletes decode_thread_.
67 StopPlay();
68 delete decode_event_;
69 delete audio_cs_;
70 delete feedback_cs_;
73 int ViEFilePlayer::Init(const char* file_nameUTF8,
74 const bool loop,
75 const FileFormats file_format,
76 VoiceEngine* voice_engine) {
77 feedback_cs_ = CriticalSectionWrapper::CreateCriticalSection();
78 if (!feedback_cs_) {
79 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
80 "ViEFilePlayer::StartPlay() failed to allocate critsect");
81 return -1;
84 audio_cs_ = CriticalSectionWrapper::CreateCriticalSection();
85 if (!audio_cs_) {
86 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
87 "ViEFilePlayer::StartPlay() failed to allocate critsect");
88 return -1;
91 decode_event_ = EventWrapper::Create();
92 if (!decode_event_) {
93 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
94 "ViEFilePlayer::StartPlay() failed to allocate event");
95 return -1;
97 if (strlen(file_nameUTF8) > FileWrapper::kMaxFileNameSize) {
98 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
99 "ViEFilePlayer::StartPlay() Too long filename");
100 return -1;
102 strncpy(file_name_, file_nameUTF8, strlen(file_nameUTF8) + 1);
104 file_player_ = FilePlayer::CreateFilePlayer(ViEId(engine_id_, id_),
105 file_format);
106 if (!file_player_) {
107 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
108 "ViEFilePlayer::StartPlay() failed to create file player");
109 return -1;
111 if (file_player_->RegisterModuleFileCallback(this) == -1) {
112 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
113 "ViEFilePlayer::StartPlay() failed to "
114 "RegisterModuleFileCallback");
115 file_player_ = NULL;
116 return -1;
118 decode_thread_ = ThreadWrapper::CreateThread(FilePlayDecodeThreadFunction,
119 this, kHighestPriority,
120 "ViEFilePlayThread");
121 if (!decode_thread_) {
122 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
123 "ViEFilePlayer::StartPlay() failed to start decode thread.");
124 file_player_ = NULL;
125 return -1;
128 // Always try to open with Audio since we don't know on what channels the
129 // audio should be played on.
130 WebRtc_Word32 error = file_player_->StartPlayingVideoFile(file_name_, loop,
131 false);
132 if (error) {
133 // Failed to open the file with audio, try without.
134 error = file_player_->StartPlayingVideoFile(file_name_, loop, true);
135 audio_stream_ = false;
136 if (error) {
137 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
138 "ViEFilePlayer::StartPlay() failed to Start play video "
139 "file");
140 return -1;
143 } else {
144 audio_stream_ = true;
147 if (audio_stream_) {
148 if (voice_engine) {
149 // A VoiceEngine has been provided and we want to play audio on local
150 // a channel.
151 voe_file_interface_ = VoEFile::GetInterface(voice_engine);
152 if (!voe_file_interface_) {
153 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
154 "ViEFilePlayer::StartPlay() failed to get VEFile "
155 "interface");
156 return -1;
158 voe_video_sync_ = VoEVideoSync::GetInterface(voice_engine);
159 if (!voe_video_sync_) {
160 WEBRTC_TRACE(kTraceError, kTraceVideo,
161 ViEId(engine_id_, id_),
162 "ViEFilePlayer::StartPlay() failed to get "
163 "VoEVideoSync interface");
164 return -1;
169 // Read audio /(or just video) every 10ms.
170 decode_event_->StartTimer(true, 10);
171 return 0;
174 int ViEFilePlayer::FrameCallbackChanged() {
175 // Starts the decode thread when someone cares.
176 if (ViEFrameProviderBase::NumberOfRegisteredFrameCallbacks() >
177 video_clients_) {
178 if (!play_back_started_) {
179 play_back_started_ = true;
180 unsigned int thread_id;
181 if (decode_thread_->Start(thread_id)) {
182 WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, ViEId(engine_id_, id_),
183 "ViEFilePlayer::FrameCallbackChanged() Started file decode"
184 " thread %u", thread_id);
185 } else {
186 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
187 "ViEFilePlayer::FrameCallbackChanged() Failed to start "
188 "file decode thread.");
190 } else if (!file_player_->IsPlayingFile()) {
191 if (file_player_->StartPlayingVideoFile(file_name_, false,
192 !audio_stream_) != 0) {
193 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
194 "ViEFilePlayer::FrameCallbackChanged(), Failed to restart "
195 "the file player.");
199 video_clients_ = ViEFrameProviderBase::NumberOfRegisteredFrameCallbacks();
200 return 0;
203 bool ViEFilePlayer::FilePlayDecodeThreadFunction(void* obj) {
204 return static_cast<ViEFilePlayer*>(obj)->FilePlayDecodeProcess();
207 bool ViEFilePlayer::FilePlayDecodeProcess() {
208 if (decode_event_->Wait(kThreadWaitTimeMs) == kEventSignaled) {
209 if (audio_stream_ && audio_clients_ == 0) {
210 // There is audio but no one cares, read the audio here.
211 Read(NULL, 0);
213 if (file_player_->TimeUntilNextVideoFrame() < 10) {
214 // Less than 10ms to next videoframe.
215 if (file_player_->GetVideoFromFile(decoded_video_) != 0) {
218 if (decoded_video_.Length() > 0) {
219 if (local_audio_channel_ != -1 && voe_video_sync_) {
220 // We are playing audio locally.
221 int audio_delay = 0;
222 if (voe_video_sync_->GetPlayoutBufferSize(audio_delay) == 0) {
223 decoded_video_.SetRenderTime(decoded_video_.RenderTimeMs() +
224 audio_delay);
227 DeliverFrame(&decoded_video_);
228 decoded_video_.SetLength(0);
231 return true;
234 int ViEFilePlayer::StopPlay() {
235 // Only called from destructor.
236 if (decode_thread_) {
237 decode_thread_->SetNotAlive();
238 if (decode_thread_->Stop()) {
239 delete decode_thread_;
240 } else {
241 assert(false && "ViEFilePlayer::StopPlay() Failed to stop decode thread");
242 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
243 "ViEFilePlayer::StartPlay() Failed to stop file decode "
244 "thread.");
247 decode_thread_ = NULL;
248 if (decode_event_) {
249 decode_event_->StopTimer();
251 StopPlayAudio();
253 if (voe_file_interface_) {
254 voe_file_interface_->Release();
255 voe_file_interface_ = NULL;
257 if (voe_video_sync_) {
258 voe_video_sync_->Release();
259 voe_video_sync_ = NULL;
262 if (file_player_) {
263 file_player_->StopPlayingFile();
264 FilePlayer::DestroyFilePlayer(file_player_);
265 file_player_ = NULL;
267 return 0;
270 int ViEFilePlayer::StopPlayAudio() {
271 // Stop sending audio.
273 std::set<int>::iterator it = audio_channels_sending_.begin();
274 while (it != audio_channels_sending_.end()) {
275 StopSendAudioOnChannel(*it);
276 // StopSendAudioOnChannel erases the item from the map.
277 it = audio_channels_sending_.begin();
280 // Stop local audio playback.
281 if (local_audio_channel_ != -1) {
282 StopPlayAudioLocally(local_audio_channel_);
284 local_audio_channel_ = -1;
285 audio_channel_buffers_.clear();
286 audio_clients_ = 0;
287 return 0;
290 int ViEFilePlayer::Read(void* buf, int len) {
291 // Protect from simultaneous reading from multiple channels.
292 CriticalSectionScoped lock(*audio_cs_);
293 if (NeedsAudioFromFile(buf)) {
294 // We will run the VoE in 16KHz.
295 if (file_player_->Get10msAudioFromFile(decoded_audio_,
296 decoded_audio_length_, 16000) != 0) {
297 // No data.
298 decoded_audio_length_ = 0;
299 return 0;
301 // 2 bytes per sample.
302 decoded_audio_length_ *= 2;
303 if (buf) {
304 audio_channel_buffers_.push_back(buf);
306 } else {
307 // No need for new audiobuffer from file, ie the buffer read from file has
308 // not been played on this channel.
310 if (buf) {
311 memcpy(buf, decoded_audio_, decoded_audio_length_);
313 return decoded_audio_length_;
316 bool ViEFilePlayer::NeedsAudioFromFile(void* buf) {
317 bool needs_new_audio = false;
318 if (audio_channel_buffers_.size() == 0) {
319 return true;
322 // Check if we the buf already have read the current audio.
323 for (std::list<void*>::iterator it = audio_channel_buffers_.begin();
324 it != audio_channel_buffers_.end(); ++it) {
325 if (*it == buf) {
326 needs_new_audio = true;
327 audio_channel_buffers_.erase(it);
328 break;
331 return needs_new_audio;
334 void ViEFilePlayer::PlayFileEnded(const WebRtc_Word32 id) {
335 WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, id),
336 "%s: file_id %d", __FUNCTION__, id_);
337 file_player_->StopPlayingFile();
339 CriticalSectionScoped lock(*feedback_cs_);
340 if (observer_) {
341 observer_->PlayFileEnded(id_);
345 bool ViEFilePlayer::IsObserverRegistered() {
346 CriticalSectionScoped lock(*feedback_cs_);
347 return observer_ != NULL;
350 int ViEFilePlayer::RegisterObserver(ViEFileObserver* observer) {
351 CriticalSectionScoped lock(*feedback_cs_);
352 if (observer_) {
353 return -1;
355 observer_ = observer;
356 return 0;
359 int ViEFilePlayer::DeRegisterObserver() {
360 CriticalSectionScoped lock(*feedback_cs_);
361 observer_ = NULL;
362 return 0;
365 int ViEFilePlayer::SendAudioOnChannel(const int audio_channel,
366 bool mix_microphone,
367 float volume_scaling) {
368 if (!voe_file_interface_) {
369 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
370 "%s No VEFile interface.", __FUNCTION__);
371 return -1;
373 if (voe_file_interface_->StartPlayingFileAsMicrophone(audio_channel, this,
374 mix_microphone,
375 kFileFormatPcm16kHzFile,
376 volume_scaling) != 0) {
377 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
378 "ViEFilePlayer::SendAudioOnChannel() "
379 "VE_StartPlayingFileAsMicrophone failed. audio_channel %d, "
380 " mix_microphone %d, volume_scaling %.2f",
381 audio_channel, mix_microphone, volume_scaling);
382 return -1;
384 audio_channels_sending_.insert(audio_channel);
386 CriticalSectionScoped lock(*audio_cs_);
387 audio_clients_++;
388 return 0;
391 int ViEFilePlayer::StopSendAudioOnChannel(const int audio_channel) {
392 int result = 0;
393 if (!voe_file_interface_) {
394 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
395 "ViEFilePlayer::StopSendAudioOnChannel() - no VoE interface");
396 return -1;
398 std::set<int>::iterator it = audio_channels_sending_.find(audio_channel);
399 if (it == audio_channels_sending_.end()) {
400 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
401 "ViEFilePlayer::StopSendAudioOnChannel AudioChannel %d not "
402 "sending", audio_channel);
403 return -1;
405 result = voe_file_interface_->StopPlayingFileAsMicrophone(audio_channel);
406 if (result != 0) {
407 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
408 "ViEFilePlayer::StopSendAudioOnChannel() "
409 "VE_StopPlayingFileAsMicrophone failed. audio_channel %d",
410 audio_channel);
412 audio_channels_sending_.erase(audio_channel);
413 CriticalSectionScoped lock(*audio_cs_);
414 audio_clients_--;
415 assert(audio_clients_ >= 0);
416 return 0;
419 int ViEFilePlayer::PlayAudioLocally(const int audio_channel,
420 float volume_scaling) {
421 if (!voe_file_interface_) {
422 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
423 "%s No VEFile interface.", __FUNCTION__);
424 return -1;
426 if (voe_file_interface_->StartPlayingFileLocally(audio_channel, this,
427 kFileFormatPcm16kHzFile,
428 volume_scaling) != 0) {
429 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
430 "%s VE_StartPlayingFileAsMicrophone failed. audio_channel %d,"
431 " mix_microphone %d, volume_scaling %.2f",
432 __FUNCTION__, audio_channel, volume_scaling);
433 return -1;
436 CriticalSectionScoped lock(*audio_cs_);
437 local_audio_channel_ = audio_channel;
438 audio_clients_++;
439 return 0;
442 int ViEFilePlayer::StopPlayAudioLocally(const int audio_channel) {
443 if (!voe_file_interface_) {
444 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
445 "%s No VEFile interface.", __FUNCTION__);
446 return -1;
448 if (voe_file_interface_->StopPlayingFileLocally(audio_channel) != 0) {
449 WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, id_),
450 "%s VE_StopPlayingFileLocally failed. audio_channel %d.",
451 __FUNCTION__, audio_channel);
452 return -1;
455 CriticalSectionScoped lock(*audio_cs_);
456 local_audio_channel_ = -1;
457 audio_clients_--;
458 return 0;
461 int ViEFilePlayer::GetFileInformation(int engine_id,
462 const char* file_name,
463 VideoCodec& video_codec,
464 CodecInst& audio_codec,
465 const FileFormats file_format) {
466 WEBRTC_TRACE(kTraceInfo, kTraceVideo, engine_id, "%s ", __FUNCTION__);
468 FilePlayer* file_player = FilePlayer::CreateFilePlayer(engine_id,
469 file_format);
470 if (!file_player) {
471 return -1;
474 bool video_only = false;
476 memset(&video_codec, 0, sizeof(video_codec));
477 memset(&audio_codec, 0, sizeof(audio_codec));
479 if (file_player->StartPlayingVideoFile(file_name, false, false) != 0) {
480 video_only = true;
481 if (file_player->StartPlayingVideoFile(file_name, false, true) != 0) {
482 WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id,
483 "%s Failed to open file.", __FUNCTION__);
484 FilePlayer::DestroyFilePlayer(file_player);
485 return -1;
489 if (!video_only && file_player->AudioCodec(audio_codec) != 0) {
490 WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id,
491 "%s Failed to get audio codec.", __FUNCTION__);
492 FilePlayer::DestroyFilePlayer(file_player);
493 return -1;
495 if (file_player->video_codec_info(video_codec) != 0) {
496 WEBRTC_TRACE(kTraceError, kTraceVideo, engine_id,
497 "%s Failed to get video codec.", __FUNCTION__);
498 FilePlayer::DestroyFilePlayer(file_player);
499 return -1;
501 FilePlayer::DestroyFilePlayer(file_player);
502 return 0;
505 } // namespace webrtc