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.
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"
27 const int kThreadWaitTimeMs
= 100;
29 ViEFilePlayer
* ViEFilePlayer::CreateViEFilePlayer(
32 const char* file_nameUTF8
,
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) {
44 ViEFilePlayer::ViEFilePlayer(int Id
,
46 : ViEFrameProviderBase(Id
, engine_id
),
47 play_back_started_(false),
54 local_audio_channel_(-1),
56 voe_file_interface_(NULL
),
57 voe_video_sync_(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_.
73 int ViEFilePlayer::Init(const char* file_nameUTF8
,
75 const FileFormats file_format
,
76 VoiceEngine
* voice_engine
) {
77 feedback_cs_
= CriticalSectionWrapper::CreateCriticalSection();
79 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
80 "ViEFilePlayer::StartPlay() failed to allocate critsect");
84 audio_cs_
= CriticalSectionWrapper::CreateCriticalSection();
86 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
87 "ViEFilePlayer::StartPlay() failed to allocate critsect");
91 decode_event_
= EventWrapper::Create();
93 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
94 "ViEFilePlayer::StartPlay() failed to allocate event");
97 if (strlen(file_nameUTF8
) > FileWrapper::kMaxFileNameSize
) {
98 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
99 "ViEFilePlayer::StartPlay() Too long filename");
102 strncpy(file_name_
, file_nameUTF8
, strlen(file_nameUTF8
) + 1);
104 file_player_
= FilePlayer::CreateFilePlayer(ViEId(engine_id_
, id_
),
107 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
108 "ViEFilePlayer::StartPlay() failed to create file player");
111 if (file_player_
->RegisterModuleFileCallback(this) == -1) {
112 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
113 "ViEFilePlayer::StartPlay() failed to "
114 "RegisterModuleFileCallback");
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.");
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
,
133 // Failed to open the file with audio, try without.
134 error
= file_player_
->StartPlayingVideoFile(file_name_
, loop
, true);
135 audio_stream_
= false;
137 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
138 "ViEFilePlayer::StartPlay() failed to Start play video "
144 audio_stream_
= true;
149 // A VoiceEngine has been provided and we want to play audio on local
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 "
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");
169 // Read audio /(or just video) every 10ms.
170 decode_event_
->StartTimer(true, 10);
174 int ViEFilePlayer::FrameCallbackChanged() {
175 // Starts the decode thread when someone cares.
176 if (ViEFrameProviderBase::NumberOfRegisteredFrameCallbacks() >
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
);
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 "
199 video_clients_
= ViEFrameProviderBase::NumberOfRegisteredFrameCallbacks();
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.
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.
222 if (voe_video_sync_
->GetPlayoutBufferSize(audio_delay
) == 0) {
223 decoded_video_
.SetRenderTime(decoded_video_
.RenderTimeMs() +
227 DeliverFrame(&decoded_video_
);
228 decoded_video_
.SetLength(0);
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_
;
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 "
247 decode_thread_
= NULL
;
249 decode_event_
->StopTimer();
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
;
263 file_player_
->StopPlayingFile();
264 FilePlayer::DestroyFilePlayer(file_player_
);
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();
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) {
298 decoded_audio_length_
= 0;
301 // 2 bytes per sample.
302 decoded_audio_length_
*= 2;
304 audio_channel_buffers_
.push_back(buf
);
307 // No need for new audiobuffer from file, ie the buffer read from file has
308 // not been played on this channel.
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) {
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
) {
326 needs_new_audio
= true;
327 audio_channel_buffers_
.erase(it
);
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_
);
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_
);
355 observer_
= observer
;
359 int ViEFilePlayer::DeRegisterObserver() {
360 CriticalSectionScoped
lock(*feedback_cs_
);
365 int ViEFilePlayer::SendAudioOnChannel(const int audio_channel
,
367 float volume_scaling
) {
368 if (!voe_file_interface_
) {
369 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
370 "%s No VEFile interface.", __FUNCTION__
);
373 if (voe_file_interface_
->StartPlayingFileAsMicrophone(audio_channel
, this,
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
);
384 audio_channels_sending_
.insert(audio_channel
);
386 CriticalSectionScoped
lock(*audio_cs_
);
391 int ViEFilePlayer::StopSendAudioOnChannel(const int audio_channel
) {
393 if (!voe_file_interface_
) {
394 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
395 "ViEFilePlayer::StopSendAudioOnChannel() - no VoE interface");
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
);
405 result
= voe_file_interface_
->StopPlayingFileAsMicrophone(audio_channel
);
407 WEBRTC_TRACE(kTraceError
, kTraceVideo
, ViEId(engine_id_
, id_
),
408 "ViEFilePlayer::StopSendAudioOnChannel() "
409 "VE_StopPlayingFileAsMicrophone failed. audio_channel %d",
412 audio_channels_sending_
.erase(audio_channel
);
413 CriticalSectionScoped
lock(*audio_cs_
);
415 assert(audio_clients_
>= 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__
);
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
);
436 CriticalSectionScoped
lock(*audio_cs_
);
437 local_audio_channel_
= audio_channel
;
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__
);
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
);
455 CriticalSectionScoped
lock(*audio_cs_
);
456 local_audio_channel_
= -1;
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
,
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) {
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
);
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
);
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
);
501 FilePlayer::DestroyFilePlayer(file_player
);
505 } // namespace webrtc