diff options
Diffstat (limited to 'third_party/libwebrtc/webrtc/modules/media_file')
11 files changed, 3204 insertions, 0 deletions
diff --git a/third_party/libwebrtc/webrtc/modules/media_file/BUILD.gn b/third_party/libwebrtc/webrtc/modules/media_file/BUILD.gn new file mode 100644 index 0000000000..77b2637497 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/BUILD.gn @@ -0,0 +1,67 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../webrtc.gni") + +config("media_file_config") { + visibility = [ ":*" ] # Only targets in this file can depend on this. +} + +rtc_static_library("media_file") { + sources = [ + "media_file.h", + "media_file_defines.h", + "media_file_impl.cc", + "media_file_impl.h", + "media_file_utility.cc", + "media_file_utility.h", + ] + + public_configs = [ ":media_file_config" ] + + # TODO(jschuh): Bug 1348: fix this warning. + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + + if (!build_with_chromium && is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] + } + + deps = [ + "..:module_api", + "../..:webrtc_common", + "../../common_audio", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers", + ] +} + +if (rtc_include_tests) { + rtc_source_set("media_file_unittests") { + testonly = true + + sources = [ + "media_file_unittest.cc", + ] + deps = [ + ":media_file", + "../../system_wrappers:system_wrappers", + "../../test:test_support", + ] + if (is_win) { + cflags = [ + # TODO(kjellander): bugs.webrtc.org/261: Fix this warning. + "/wd4373", # virtual function override. + ] + } + if (!build_with_chromium && is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] + } + } +} diff --git a/third_party/libwebrtc/webrtc/modules/media_file/DEPS b/third_party/libwebrtc/webrtc/modules/media_file/DEPS new file mode 100644 index 0000000000..dc17ef44fe --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+common_audio", + "+system_wrappers", +] diff --git a/third_party/libwebrtc/webrtc/modules/media_file/OWNERS b/third_party/libwebrtc/webrtc/modules/media_file/OWNERS new file mode 100644 index 0000000000..f5e93f0138 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/OWNERS @@ -0,0 +1,8 @@ +mflodman@webrtc.org +perkj@webrtc.org +niklas.enbom@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gn=* +per-file *.gni=* diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file.h b/third_party/libwebrtc/webrtc/modules/media_file/media_file.h new file mode 100644 index 0000000000..6a7a02efcc --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_MEDIA_FILE_MEDIA_FILE_H_ +#define MODULES_MEDIA_FILE_MEDIA_FILE_H_ + +#include "common_types.h" // NOLINT(build/include) +#include "modules/include/module.h" +#include "modules/include/module_common_types.h" +#include "modules/media_file/media_file_defines.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { +class MediaFile : public Module +{ +public: + // Factory method. Constructor disabled. id is the identifier for the + // MediaFile instance. + static MediaFile* CreateMediaFile(const int32_t id); + static void DestroyMediaFile(MediaFile* module); + + // Put 10-60ms of audio data from file into the audioBuffer depending on + // codec frame size. dataLengthInBytes is both an input and output + // parameter. As input parameter it indicates the size of audioBuffer. + // As output parameter it indicates the number of bytes written to + // audioBuffer. + // Note: This API only play mono audio but can be used on file containing + // audio with more channels (in which case the audio will be converted to + // mono). + virtual int32_t PlayoutAudioData( + int8_t* audioBuffer, + size_t& dataLengthInBytes) = 0; + + // Put 10-60ms, depending on codec frame size, of audio data from file into + // audioBufferLeft and audioBufferRight. The buffers contain the left and + // right channel of played out stereo audio. + // dataLengthInBytes is both an input and output parameter. As input + // parameter it indicates the size of both audioBufferLeft and + // audioBufferRight. As output parameter it indicates the number of bytes + // written to both audio buffers. + // Note: This API can only be successfully called for WAV files with stereo + // audio. + virtual int32_t PlayoutStereoData( + int8_t* audioBufferLeft, + int8_t* audioBufferRight, + size_t& dataLengthInBytes) = 0; + + // Open the file specified by fileName (relative path is allowed) for + // reading. FileCallback::PlayNotification(..) will be called after + // notificationTimeMs of the file has been played if notificationTimeMs is + // greater than zero. If loop is true the file will be played until + // StopPlaying() is called. When end of file is reached the file is read + // from the start. format specifies the type of file fileName refers to. + // codecInst specifies the encoding of the audio data. Note that + // file formats that contain this information (like WAV files) don't need to + // provide a non-NULL codecInst. startPointMs and stopPointMs, unless zero, + // specify what part of the file should be read. From startPointMs ms to + // stopPointMs ms. + // Note: codecInst.channels should be set to 2 for stereo (and 1 for + // mono). Stereo audio is only supported for WAV files. + virtual int32_t StartPlayingAudioFile( + const char* fileName, + const uint32_t notificationTimeMs = 0, + const bool loop = false, + const FileFormats format = kFileFormatPcm16kHzFile, + const CodecInst* codecInst = NULL, + const uint32_t startPointMs = 0, + const uint32_t stopPointMs = 0) = 0; + + // Prepare for playing audio from stream. + // FileCallback::PlayNotification(..) will be called after + // notificationTimeMs of the file has been played if notificationTimeMs is + // greater than zero. format specifies the type of file fileName refers to. + // codecInst specifies the encoding of the audio data. Note that + // file formats that contain this information (like WAV files) don't need to + // provide a non-NULL codecInst. startPointMs and stopPointMs, unless zero, + // specify what part of the file should be read. From startPointMs ms to + // stopPointMs ms. + // Note: codecInst.channels should be set to 2 for stereo (and 1 for + // mono). Stereo audio is only supported for WAV files. + virtual int32_t StartPlayingAudioStream( + InStream& stream, + const uint32_t notificationTimeMs = 0, + const FileFormats format = kFileFormatPcm16kHzFile, + const CodecInst* codecInst = NULL, + const uint32_t startPointMs = 0, + const uint32_t stopPointMs = 0) = 0; + + // Stop playing from file or stream. + virtual int32_t StopPlaying() = 0; + + // Return true if playing. + virtual bool IsPlaying() = 0; + + + // Set durationMs to the number of ms that has been played from file. + virtual int32_t PlayoutPositionMs( + uint32_t& durationMs) const = 0; + + // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, + // to file. The audio frame size is determined by the codecInst.pacsize + // parameter of the last sucessfull StartRecordingAudioFile(..) call. + // Note: bufferLength must be exactly one frame. + virtual int32_t IncomingAudioData( + const int8_t* audioBuffer, + const size_t bufferLength) = 0; + + // Open/creates file specified by fileName for writing (relative path is + // allowed). FileCallback::RecordNotification(..) will be called after + // notificationTimeMs of audio data has been recorded if + // notificationTimeMs is greater than zero. + // format specifies the type of file that should be created/opened. + // codecInst specifies the encoding of the audio data. maxSizeBytes + // specifies the number of bytes allowed to be written to file if it is + // greater than zero. + // Note: codecInst.channels should be set to 2 for stereo (and 1 for + // mono). Stereo is only supported for WAV files. + virtual int32_t StartRecordingAudioFile( + const char* fileName, + const FileFormats format, + const CodecInst& codecInst, + const uint32_t notificationTimeMs = 0, + const uint32_t maxSizeBytes = 0) = 0; + + // Prepare for recording audio to stream. + // FileCallback::RecordNotification(..) will be called after + // notificationTimeMs of audio data has been recorded if + // notificationTimeMs is greater than zero. + // format specifies the type of file that stream should correspond to. + // codecInst specifies the encoding of the audio data. + // Note: codecInst.channels should be set to 2 for stereo (and 1 for + // mono). Stereo is only supported for WAV files. + virtual int32_t StartRecordingAudioStream( + OutStream& stream, + const FileFormats format, + const CodecInst& codecInst, + const uint32_t notificationTimeMs = 0) = 0; + + // Stop recording to file or stream. + virtual int32_t StopRecording() = 0; + + // Return true if recording. + virtual bool IsRecording() = 0; + + // Set durationMs to the number of ms that has been recorded to file. + virtual int32_t RecordDurationMs(uint32_t& durationMs) = 0; + + // Return true if recording or playing is stereo. + virtual bool IsStereo() = 0; + + // Register callback to receive media file related notifications. Disables + // callbacks if callback is NULL. + virtual int32_t SetModuleFileCallback(FileCallback* callback) = 0; + + // Set durationMs to the size of the file (in ms) specified by fileName. + // format specifies the type of file fileName refers to. freqInHz specifies + // the sampling frequency of the file. + virtual int32_t FileDurationMs( + const char* fileName, + uint32_t& durationMs, + const FileFormats format, + const uint32_t freqInHz = 16000) = 0; + + // Update codecInst according to the current audio codec being used for + // reading or writing. + virtual int32_t codec_info(CodecInst& codecInst) const = 0; + +protected: + MediaFile() {} + virtual ~MediaFile() {} +}; +} // namespace webrtc +#endif // MODULES_MEDIA_FILE_MEDIA_FILE_H_ diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file_defines.h b/third_party/libwebrtc/webrtc/modules/media_file/media_file_defines.h new file mode 100644 index 0000000000..73b3b059ce --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file_defines.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_MEDIA_FILE_MEDIA_FILE_DEFINES_H_ +#define MODULES_MEDIA_FILE_MEDIA_FILE_DEFINES_H_ + +#include "modules/include/module_common_types.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { +// Callback class for the MediaFile class. +class FileCallback +{ +public: + virtual ~FileCallback(){} + + // This function is called by MediaFile when a file has been playing for + // durationMs ms. id is the identifier for the MediaFile instance calling + // the callback. + virtual void PlayNotification(const int32_t id, + const uint32_t durationMs) = 0; + + // This function is called by MediaFile when a file has been recording for + // durationMs ms. id is the identifier for the MediaFile instance calling + // the callback. + virtual void RecordNotification(const int32_t id, + const uint32_t durationMs) = 0; + + // This function is called by MediaFile when a file has been stopped + // playing. id is the identifier for the MediaFile instance calling the + // callback. + virtual void PlayFileEnded(const int32_t id) = 0; + + // This function is called by MediaFile when a file has been stopped + // recording. id is the identifier for the MediaFile instance calling the + // callback. + virtual void RecordFileEnded(const int32_t id) = 0; + +protected: + FileCallback() {} +}; +} // namespace webrtc +#endif // MODULES_MEDIA_FILE_MEDIA_FILE_DEFINES_H_ diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file_gn/moz.build b/third_party/libwebrtc/webrtc/modules/media_file/media_file_gn/moz.build new file mode 100644 index 0000000000..d2969fa6c5 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file_gn/moz.build @@ -0,0 +1,229 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/", + "/third_party/libwebrtc/webrtc/common_audio/resampler/include/", + "/third_party/libwebrtc/webrtc/common_audio/signal_processing/include/", + "/third_party/libwebrtc/webrtc/common_audio/vad/include/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/modules/media_file/media_file_impl.cc", + "/third_party/libwebrtc/webrtc/modules/media_file/media_file_utility.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("media_file_gn") diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file_impl.cc b/third_party/libwebrtc/webrtc/modules/media_file/media_file_impl.cc new file mode 100644 index 0000000000..0fd9d9ee6a --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file_impl.cc @@ -0,0 +1,893 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <assert.h> + +#include "modules/media_file/media_file_impl.h" +#include "rtc_base/format_macros.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/file_wrapper.h" + +namespace webrtc { +MediaFile* MediaFile::CreateMediaFile(const int32_t id) { + return new MediaFileImpl(id); +} + +void MediaFile::DestroyMediaFile(MediaFile* module) { + delete static_cast<MediaFileImpl*>(module); +} + +MediaFileImpl::MediaFileImpl(const int32_t id) + : _id(id), + _ptrFileUtilityObj(NULL), + codec_info_(), + _ptrInStream(NULL), + _ptrOutStream(NULL), + _fileFormat((FileFormats)-1), + _recordDurationMs(0), + _playoutPositionMs(0), + _notificationMs(0), + _playingActive(false), + _recordingActive(false), + _isStereo(false), + _openFile(false), + _fileName(), + _ptrCallback(NULL) { + RTC_LOG(LS_INFO) << "MediaFileImpl()"; + + codec_info_.plname[0] = '\0'; + _fileName[0] = '\0'; +} + +MediaFileImpl::~MediaFileImpl() { + RTC_LOG(LS_INFO) << "~MediaFileImpl()"; + { + rtc::CritScope lock(&_crit); + + if (_playingActive) { + StopPlaying(); + } + + if (_recordingActive) { + StopRecording(); + } + + delete _ptrFileUtilityObj; + + if (_openFile) { + delete _ptrInStream; + _ptrInStream = NULL; + delete _ptrOutStream; + _ptrOutStream = NULL; + } + } +} + +int64_t MediaFileImpl::TimeUntilNextProcess() { + RTC_LOG(LS_WARNING) + << "TimeUntilNextProcess: This method is not used by MediaFile class."; + return -1; +} + +void MediaFileImpl::Process() { + RTC_LOG(LS_WARNING) << "Process: This method is not used by MediaFile class."; +} + +int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer, + size_t& dataLengthInBytes) { + RTC_LOG(LS_INFO) << "MediaFileImpl::PlayoutData(buffer= " + << static_cast<void*>(buffer) + << ", bufLen= " << dataLengthInBytes << ")"; + + const size_t bufferLengthInBytes = dataLengthInBytes; + dataLengthInBytes = 0; + + if (buffer == NULL || bufferLengthInBytes == 0) { + RTC_LOG(LS_ERROR) << "Buffer pointer or length is NULL!"; + return -1; + } + + int32_t bytesRead = 0; + { + rtc::CritScope lock(&_crit); + + if (!_playingActive) { + RTC_LOG(LS_WARNING) << "Not currently playing!"; + return -1; + } + + if (!_ptrFileUtilityObj) { + RTC_LOG(LS_ERROR) << "Playing, but no FileUtility object!"; + StopPlaying(); + return -1; + } + + switch (_fileFormat) { + case kFileFormatPcm48kHzFile: + case kFileFormatPcm32kHzFile: + case kFileFormatPcm16kHzFile: + case kFileFormatPcm8kHzFile: + bytesRead = _ptrFileUtilityObj->ReadPCMData(*_ptrInStream, buffer, + bufferLengthInBytes); + break; + case kFileFormatCompressedFile: + bytesRead = _ptrFileUtilityObj->ReadCompressedData( + *_ptrInStream, buffer, bufferLengthInBytes); + break; + case kFileFormatWavFile: + bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono(*_ptrInStream, buffer, + bufferLengthInBytes); + break; + case kFileFormatPreencodedFile: + bytesRead = _ptrFileUtilityObj->ReadPreEncodedData( + *_ptrInStream, buffer, bufferLengthInBytes); + if (bytesRead > 0) { + dataLengthInBytes = static_cast<size_t>(bytesRead); + return 0; + } + break; + default: { + RTC_LOG(LS_ERROR) << "Invalid file format: " << _fileFormat; + assert(false); + break; + } + } + + if (bytesRead > 0) { + dataLengthInBytes = static_cast<size_t>(bytesRead); + } + } + HandlePlayCallbacks(bytesRead); + return 0; +} + +void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead) { + bool playEnded = false; + uint32_t callbackNotifyMs = 0; + + if (bytesRead > 0) { + // Check if it's time for PlayNotification(..). + _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); + if (_notificationMs) { + if (_playoutPositionMs >= _notificationMs) { + _notificationMs = 0; + callbackNotifyMs = _playoutPositionMs; + } + } + } else { + // If no bytes were read assume end of file. + StopPlaying(); + playEnded = true; + } + + // Only _callbackCrit may and should be taken when making callbacks. + rtc::CritScope lock(&_callbackCrit); + if (_ptrCallback) { + if (callbackNotifyMs) { + _ptrCallback->PlayNotification(_id, callbackNotifyMs); + } + if (playEnded) { + _ptrCallback->PlayFileEnded(_id); + } + } +} + +int32_t MediaFileImpl::PlayoutStereoData(int8_t* bufferLeft, + int8_t* bufferRight, + size_t& dataLengthInBytes) { + RTC_LOG(LS_INFO) << "MediaFileImpl::PlayoutStereoData(Left = " + << static_cast<void*>(bufferLeft) + << ", Right = " << static_cast<void*>(bufferRight) + << ", Len= " << dataLengthInBytes << ")"; + + const size_t bufferLengthInBytes = dataLengthInBytes; + dataLengthInBytes = 0; + + if (bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0) { + RTC_LOG(LS_ERROR) << "A buffer pointer or the length is NULL!"; + return -1; + } + + bool playEnded = false; + uint32_t callbackNotifyMs = 0; + { + rtc::CritScope lock(&_crit); + + if (!_playingActive || !_isStereo) { + RTC_LOG(LS_WARNING) << "Not currently playing stereo!"; + return -1; + } + + if (!_ptrFileUtilityObj) { + RTC_LOG(LS_ERROR) + << "Playing stereo, but the FileUtility objects is NULL!"; + StopPlaying(); + return -1; + } + + // Stereo playout only supported for WAV files. + int32_t bytesRead = 0; + switch (_fileFormat) { + case kFileFormatWavFile: + bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo( + *_ptrInStream, bufferLeft, bufferRight, bufferLengthInBytes); + break; + default: + RTC_LOG(LS_ERROR) + << "Trying to read non-WAV as stereo audio (not supported)"; + break; + } + + if (bytesRead > 0) { + dataLengthInBytes = static_cast<size_t>(bytesRead); + + // Check if it's time for PlayNotification(..). + _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); + if (_notificationMs) { + if (_playoutPositionMs >= _notificationMs) { + _notificationMs = 0; + callbackNotifyMs = _playoutPositionMs; + } + } + } else { + // If no bytes were read assume end of file. + StopPlaying(); + playEnded = true; + } + } + + rtc::CritScope lock(&_callbackCrit); + if (_ptrCallback) { + if (callbackNotifyMs) { + _ptrCallback->PlayNotification(_id, callbackNotifyMs); + } + if (playEnded) { + _ptrCallback->PlayFileEnded(_id); + } + } + return 0; +} + +int32_t MediaFileImpl::StartPlayingAudioFile(const char* fileName, + const uint32_t notificationTimeMs, + const bool loop, + const FileFormats format, + const CodecInst* codecInst, + const uint32_t startPointMs, + const uint32_t stopPointMs) { + if (!ValidFileName(fileName)) { + return -1; + } + if (!ValidFileFormat(format, codecInst)) { + return -1; + } + if (!ValidFilePositions(startPointMs, stopPointMs)) { + return -1; + } + + // Check that the file will play longer than notificationTimeMs ms. + if ((startPointMs && stopPointMs && !loop) && + (notificationTimeMs > (stopPointMs - startPointMs))) { + RTC_LOG(LS_ERROR) << "specified notification time is longer than amount of" + << " ms that will be played"; + return -1; + } + + FileWrapper* inputStream = FileWrapper::Create(); + if (inputStream == NULL) { + RTC_LOG(LS_INFO) << "Failed to allocate input stream for file " << fileName; + return -1; + } + + if (!inputStream->OpenFile(fileName, true)) { + delete inputStream; + RTC_LOG(LS_ERROR) << "Could not open input file " << fileName; + return -1; + } + + if (StartPlayingStream(*inputStream, loop, notificationTimeMs, format, + codecInst, startPointMs, stopPointMs) == -1) { + inputStream->CloseFile(); + delete inputStream; + return -1; + } + + rtc::CritScope lock(&_crit); + _openFile = true; + strncpy(_fileName, fileName, sizeof(_fileName)); + _fileName[sizeof(_fileName) - 1] = '\0'; + return 0; +} + +int32_t MediaFileImpl::StartPlayingAudioStream( + InStream& stream, + const uint32_t notificationTimeMs, + const FileFormats format, + const CodecInst* codecInst, + const uint32_t startPointMs, + const uint32_t stopPointMs) { + return StartPlayingStream(stream, false, notificationTimeMs, format, + codecInst, startPointMs, stopPointMs); +} + +int32_t MediaFileImpl::StartPlayingStream(InStream& stream, + bool loop, + const uint32_t notificationTimeMs, + const FileFormats format, + const CodecInst* codecInst, + const uint32_t startPointMs, + const uint32_t stopPointMs) { + if (!ValidFileFormat(format, codecInst)) { + return -1; + } + + if (!ValidFilePositions(startPointMs, stopPointMs)) { + return -1; + } + + rtc::CritScope lock(&_crit); + if (_playingActive || _recordingActive) { + RTC_LOG(LS_ERROR) + << "StartPlaying called, but already playing or recording file " + << ((_fileName[0] == '\0') ? "(name not set)" : _fileName); + return -1; + } + + if (_ptrFileUtilityObj != NULL) { + RTC_LOG(LS_ERROR) + << "StartPlaying called, but FileUtilityObj already exists!"; + StopPlaying(); + return -1; + } + + _ptrFileUtilityObj = new ModuleFileUtility(); + if (_ptrFileUtilityObj == NULL) { + RTC_LOG(LS_INFO) << "Failed to create FileUtilityObj!"; + return -1; + } + + switch (format) { + case kFileFormatWavFile: { + if (_ptrFileUtilityObj->InitWavReading(stream, startPointMs, + stopPointMs) == -1) { + RTC_LOG(LS_ERROR) << "Not a valid WAV file!"; + StopPlaying(); + return -1; + } + _fileFormat = kFileFormatWavFile; + break; + } + case kFileFormatCompressedFile: { + if (_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs, + stopPointMs) == -1) { + RTC_LOG(LS_ERROR) << "Not a valid Compressed file!"; + StopPlaying(); + return -1; + } + _fileFormat = kFileFormatCompressedFile; + break; + } + case kFileFormatPcm8kHzFile: + case kFileFormatPcm16kHzFile: + case kFileFormatPcm32kHzFile: + case kFileFormatPcm48kHzFile: { + // ValidFileFormat() called in the beginneing of this function + // prevents codecInst from being NULL here. + assert(codecInst != NULL); + if (!ValidFrequency(codecInst->plfreq) || + _ptrFileUtilityObj->InitPCMReading(stream, startPointMs, stopPointMs, + codecInst->plfreq) == -1) { + RTC_LOG(LS_ERROR) << "Not a valid raw 8 or 16 KHz PCM file!"; + StopPlaying(); + return -1; + } + + _fileFormat = format; + break; + } + case kFileFormatPreencodedFile: { + // ValidFileFormat() called in the beginneing of this function + // prevents codecInst from being NULL here. + assert(codecInst != NULL); + if (_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) == -1) { + RTC_LOG(LS_ERROR) << "Not a valid PreEncoded file!"; + StopPlaying(); + return -1; + } + + _fileFormat = kFileFormatPreencodedFile; + break; + } + default: { + RTC_LOG(LS_ERROR) << "Invalid file format: " << format; + assert(false); + break; + } + } + if (_ptrFileUtilityObj->codec_info(codec_info_) == -1) { + RTC_LOG(LS_ERROR) << "Failed to retrieve codec info!"; + StopPlaying(); + return -1; + } + + _isStereo = (codec_info_.channels == 2); + if (_isStereo && (_fileFormat != kFileFormatWavFile)) { + RTC_LOG(LS_WARNING) << "Stereo is only allowed for WAV files"; + StopPlaying(); + return -1; + } + _playingActive = true; + _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); + _ptrInStream = &stream; + _notificationMs = notificationTimeMs; + + return 0; +} + +int32_t MediaFileImpl::StopPlaying() { + rtc::CritScope lock(&_crit); + _isStereo = false; + if (_ptrFileUtilityObj) { + delete _ptrFileUtilityObj; + _ptrFileUtilityObj = NULL; + } + if (_ptrInStream) { + // If MediaFileImpl opened the InStream it must be reclaimed here. + if (_openFile) { + delete _ptrInStream; + _openFile = false; + } + _ptrInStream = NULL; + } + + codec_info_.pltype = 0; + codec_info_.plname[0] = '\0'; + + if (!_playingActive) { + RTC_LOG(LS_WARNING) << "playing is not active!"; + return -1; + } + + _playingActive = false; + return 0; +} + +bool MediaFileImpl::IsPlaying() { + RTC_LOG(LS_VERBOSE) << "MediaFileImpl::IsPlaying()"; + rtc::CritScope lock(&_crit); + return _playingActive; +} + +int32_t MediaFileImpl::IncomingAudioData(const int8_t* buffer, + const size_t bufferLengthInBytes) { + RTC_LOG(LS_INFO) << "MediaFile::IncomingData(buffer= " + << static_cast<const void*>(buffer) + << ", bufLen= " << bufferLengthInBytes << ")"; + + if (buffer == NULL || bufferLengthInBytes == 0) { + RTC_LOG(LS_ERROR) << "Buffer pointer or length is NULL!"; + return -1; + } + + bool recordingEnded = false; + uint32_t callbackNotifyMs = 0; + { + rtc::CritScope lock(&_crit); + + if (!_recordingActive) { + RTC_LOG(LS_WARNING) << "Not currently recording!"; + return -1; + } + if (_ptrOutStream == NULL) { + RTC_LOG(LS_ERROR) << "Recording is active, but output stream is NULL!"; + assert(false); + return -1; + } + + int32_t bytesWritten = 0; + uint32_t samplesWritten = codec_info_.pacsize; + if (_ptrFileUtilityObj) { + switch (_fileFormat) { + case kFileFormatPcm8kHzFile: + case kFileFormatPcm16kHzFile: + case kFileFormatPcm32kHzFile: + case kFileFormatPcm48kHzFile: + bytesWritten = _ptrFileUtilityObj->WritePCMData( + *_ptrOutStream, buffer, bufferLengthInBytes); + + // Sample size is 2 bytes. + if (bytesWritten > 0) { + samplesWritten = bytesWritten / sizeof(int16_t); + } + break; + case kFileFormatCompressedFile: + bytesWritten = _ptrFileUtilityObj->WriteCompressedData( + *_ptrOutStream, buffer, bufferLengthInBytes); + break; + case kFileFormatWavFile: + bytesWritten = _ptrFileUtilityObj->WriteWavData( + *_ptrOutStream, buffer, bufferLengthInBytes); + if (bytesWritten > 0 && + STR_NCASE_CMP(codec_info_.plname, "L16", 4) == 0) { + // Sample size is 2 bytes. + samplesWritten = bytesWritten / sizeof(int16_t); + } + break; + case kFileFormatPreencodedFile: + bytesWritten = _ptrFileUtilityObj->WritePreEncodedData( + *_ptrOutStream, buffer, bufferLengthInBytes); + break; + default: + RTC_LOG(LS_ERROR) << "Invalid file format: " << _fileFormat; + assert(false); + break; + } + } else { + // TODO (hellner): quick look at the code makes me think that this + // code is never executed. Remove? + if (_ptrOutStream) { + if (_ptrOutStream->Write(buffer, bufferLengthInBytes)) { + bytesWritten = static_cast<int32_t>(bufferLengthInBytes); + } + } + } + + _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000); + + // Check if it's time for RecordNotification(..). + if (_notificationMs) { + if (_recordDurationMs >= _notificationMs) { + _notificationMs = 0; + callbackNotifyMs = _recordDurationMs; + } + } + if (bytesWritten < (int32_t)bufferLengthInBytes) { + RTC_LOG(LS_WARNING) << "Failed to write all requested bytes!"; + StopRecording(); + recordingEnded = true; + } + } + + // Only _callbackCrit may and should be taken when making callbacks. + rtc::CritScope lock(&_callbackCrit); + if (_ptrCallback) { + if (callbackNotifyMs) { + _ptrCallback->RecordNotification(_id, callbackNotifyMs); + } + if (recordingEnded) { + _ptrCallback->RecordFileEnded(_id); + return -1; + } + } + return 0; +} + +int32_t MediaFileImpl::StartRecordingAudioFile( + const char* fileName, + const FileFormats format, + const CodecInst& codecInst, + const uint32_t notificationTimeMs, + const uint32_t maxSizeBytes) { + if (!ValidFileName(fileName)) { + return -1; + } + if (!ValidFileFormat(format, &codecInst)) { + return -1; + } + + FileWrapper* outputStream = FileWrapper::Create(); + if (outputStream == NULL) { + RTC_LOG(LS_INFO) << "Failed to allocate memory for output stream"; + return -1; + } + + if (!outputStream->OpenFile(fileName, false)) { + delete outputStream; + RTC_LOG(LS_ERROR) << "Could not open output file '" << fileName + << "' for writing!"; + return -1; + } + + if (maxSizeBytes) { + outputStream->SetMaxFileSize(maxSizeBytes); + } + + if (StartRecordingAudioStream(*outputStream, format, codecInst, + notificationTimeMs) == -1) { + outputStream->CloseFile(); + delete outputStream; + return -1; + } + + rtc::CritScope lock(&_crit); + _openFile = true; + strncpy(_fileName, fileName, sizeof(_fileName)); + _fileName[sizeof(_fileName) - 1] = '\0'; + return 0; +} + +int32_t MediaFileImpl::StartRecordingAudioStream( + OutStream& stream, + const FileFormats format, + const CodecInst& codecInst, + const uint32_t notificationTimeMs) { + // Check codec info + if (!ValidFileFormat(format, &codecInst)) { + return -1; + } + + rtc::CritScope lock(&_crit); + if (_recordingActive || _playingActive) { + RTC_LOG(LS_ERROR) + << "StartRecording called, but already recording or playing file " + << _fileName << "!"; + return -1; + } + + if (_ptrFileUtilityObj != NULL) { + RTC_LOG(LS_ERROR) + << "StartRecording called, but fileUtilityObj already exists!"; + StopRecording(); + return -1; + } + + _ptrFileUtilityObj = new ModuleFileUtility(); + if (_ptrFileUtilityObj == NULL) { + RTC_LOG(LS_INFO) << "Cannot allocate fileUtilityObj!"; + return -1; + } + + CodecInst tmpAudioCodec; + memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst)); + switch (format) { + case kFileFormatWavFile: { + if (_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1) { + RTC_LOG(LS_ERROR) << "Failed to initialize WAV file!"; + delete _ptrFileUtilityObj; + _ptrFileUtilityObj = NULL; + return -1; + } + _fileFormat = kFileFormatWavFile; + break; + } + case kFileFormatCompressedFile: { + // Write compression codec name at beginning of file + if (_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) == -1) { + RTC_LOG(LS_ERROR) << "Failed to initialize Compressed file!"; + delete _ptrFileUtilityObj; + _ptrFileUtilityObj = NULL; + return -1; + } + _fileFormat = kFileFormatCompressedFile; + break; + } + case kFileFormatPcm8kHzFile: + case kFileFormatPcm16kHzFile: + case kFileFormatPcm32kHzFile: + case kFileFormatPcm48kHzFile: { + if (!ValidFrequency(codecInst.plfreq) || + _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) == -1) { + RTC_LOG(LS_ERROR) << "Failed to initialize PCM file!"; + delete _ptrFileUtilityObj; + _ptrFileUtilityObj = NULL; + return -1; + } + _fileFormat = format; + break; + } + case kFileFormatPreencodedFile: { + if (_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) == -1) { + RTC_LOG(LS_ERROR) << "Failed to initialize Pre-Encoded file!"; + delete _ptrFileUtilityObj; + _ptrFileUtilityObj = NULL; + return -1; + } + + _fileFormat = kFileFormatPreencodedFile; + break; + } + default: { + RTC_LOG(LS_ERROR) << "Invalid file format " << format << " specified!"; + delete _ptrFileUtilityObj; + _ptrFileUtilityObj = NULL; + return -1; + } + } + _isStereo = (tmpAudioCodec.channels == 2); + if (_isStereo) { + if (_fileFormat != kFileFormatWavFile) { + RTC_LOG(LS_WARNING) << "Stereo is only allowed for WAV files"; + StopRecording(); + return -1; + } + if ((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) && + (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) && + (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0)) { + RTC_LOG(LS_WARNING) + << "Stereo is only allowed for codec PCMU, PCMA and L16 "; + StopRecording(); + return -1; + } + } + memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst)); + _recordingActive = true; + _ptrOutStream = &stream; + _notificationMs = notificationTimeMs; + _recordDurationMs = 0; + return 0; +} + +int32_t MediaFileImpl::StopRecording() { + rtc::CritScope lock(&_crit); + if (!_recordingActive) { + RTC_LOG(LS_WARNING) << "recording is not active!"; + return -1; + } + + _isStereo = false; + + if (_ptrFileUtilityObj != NULL) { + // Both AVI and WAV header has to be updated before closing the stream + // because they contain size information. + if ((_fileFormat == kFileFormatWavFile) && (_ptrOutStream != NULL)) { + _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream); + } + delete _ptrFileUtilityObj; + _ptrFileUtilityObj = NULL; + } + + if (_ptrOutStream != NULL) { + // If MediaFileImpl opened the OutStream it must be reclaimed here. + if (_openFile) { + delete _ptrOutStream; + _openFile = false; + } + _ptrOutStream = NULL; + } + + _recordingActive = false; + codec_info_.pltype = 0; + codec_info_.plname[0] = '\0'; + + return 0; +} + +bool MediaFileImpl::IsRecording() { + RTC_LOG(LS_VERBOSE) << "MediaFileImpl::IsRecording()"; + rtc::CritScope lock(&_crit); + return _recordingActive; +} + +int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs) { + rtc::CritScope lock(&_crit); + if (!_recordingActive) { + durationMs = 0; + return -1; + } + durationMs = _recordDurationMs; + return 0; +} + +bool MediaFileImpl::IsStereo() { + RTC_LOG(LS_VERBOSE) << "MediaFileImpl::IsStereo()"; + rtc::CritScope lock(&_crit); + return _isStereo; +} + +int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback) { + rtc::CritScope lock(&_callbackCrit); + + _ptrCallback = callback; + return 0; +} + +int32_t MediaFileImpl::FileDurationMs(const char* fileName, + uint32_t& durationMs, + const FileFormats format, + const uint32_t freqInHz) { + if (!ValidFileName(fileName)) { + return -1; + } + if (!ValidFrequency(freqInHz)) { + return -1; + } + + ModuleFileUtility* utilityObj = new ModuleFileUtility(); + if (utilityObj == NULL) { + RTC_LOG(LS_ERROR) << "failed to allocate utility object!"; + return -1; + } + + const int32_t duration = + utilityObj->FileDurationMs(fileName, format, freqInHz); + delete utilityObj; + if (duration == -1) { + durationMs = 0; + return -1; + } + + durationMs = duration; + return 0; +} + +int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const { + rtc::CritScope lock(&_crit); + if (!_playingActive) { + positionMs = 0; + return -1; + } + positionMs = _playoutPositionMs; + return 0; +} + +int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const { + rtc::CritScope lock(&_crit); + if (!_playingActive && !_recordingActive) { + RTC_LOG(LS_ERROR) << "Neither playout nor recording has been initialized!"; + return -1; + } + if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0') { + RTC_LOG(LS_ERROR) << "The CodecInst for " + << (_playingActive ? "Playback" : "Recording") + << " is unknown!"; + return -1; + } + memcpy(&codecInst, &codec_info_, sizeof(CodecInst)); + return 0; +} + +bool MediaFileImpl::ValidFileFormat(const FileFormats format, + const CodecInst* codecInst) { + if (codecInst == NULL) { + if (format == kFileFormatPreencodedFile || + format == kFileFormatPcm8kHzFile || format == kFileFormatPcm16kHzFile || + format == kFileFormatPcm32kHzFile || + format == kFileFormatPcm48kHzFile) { + RTC_LOG(LS_ERROR) << "Codec info required for file format specified!"; + return false; + } + } + return true; +} + +bool MediaFileImpl::ValidFileName(const char* fileName) { + if ((fileName == NULL) || (fileName[0] == '\0')) { + RTC_LOG(LS_ERROR) << "FileName not specified!"; + return false; + } + return true; +} + +bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs, + const uint32_t stopPointMs) { + if (startPointMs == 0 && stopPointMs == 0) // Default values + { + return true; + } + if (stopPointMs && (startPointMs >= stopPointMs)) { + RTC_LOG(LS_ERROR) << "startPointMs must be less than stopPointMs!"; + return false; + } + if (stopPointMs && ((stopPointMs - startPointMs) < 20)) { + RTC_LOG(LS_ERROR) << "minimum play duration for files is 20 ms!"; + return false; + } + return true; +} + +bool MediaFileImpl::ValidFrequency(const uint32_t frequency) { + if ((frequency == 8000) || (frequency == 16000) || (frequency == 32000) || + (frequency == 48000)) { + return true; + } + RTC_LOG(LS_ERROR) << "Frequency should be 8000, 16000, 32000, or 48000 (Hz)"; + return false; +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file_impl.h b/third_party/libwebrtc/webrtc/modules/media_file/media_file_impl.h new file mode 100644 index 0000000000..7716d81078 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file_impl.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_MEDIA_FILE_MEDIA_FILE_IMPL_H_ +#define MODULES_MEDIA_FILE_MEDIA_FILE_IMPL_H_ + +#include "common_types.h" // NOLINT(build/include) +#include "modules/include/module_common_types.h" +#include "modules/media_file/media_file.h" +#include "modules/media_file/media_file_defines.h" +#include "modules/media_file/media_file_utility.h" +#include "rtc_base/criticalsection.h" + +namespace webrtc { +class MediaFileImpl : public MediaFile +{ + +public: + MediaFileImpl(const int32_t id); + ~MediaFileImpl(); + + void Process() override; + int64_t TimeUntilNextProcess() override; + + // MediaFile functions + int32_t PlayoutAudioData(int8_t* audioBuffer, + size_t& dataLengthInBytes) override; + + int32_t PlayoutStereoData(int8_t* audioBufferLeft, + int8_t* audioBufferRight, + size_t& dataLengthInBytes) override; + + int32_t StartPlayingAudioFile( + const char* fileName, + const uint32_t notificationTimeMs = 0, + const bool loop = false, + const FileFormats format = kFileFormatPcm16kHzFile, + const CodecInst* codecInst = NULL, + const uint32_t startPointMs = 0, + const uint32_t stopPointMs = 0) override; + + int32_t StartPlayingAudioStream( + InStream& stream, + const uint32_t notificationTimeMs = 0, + const FileFormats format = kFileFormatPcm16kHzFile, + const CodecInst* codecInst = NULL, + const uint32_t startPointMs = 0, + const uint32_t stopPointMs = 0) override; + + int32_t StopPlaying() override; + + bool IsPlaying() override; + + int32_t PlayoutPositionMs(uint32_t& positionMs) const override; + + int32_t IncomingAudioData(const int8_t* audioBuffer, + const size_t bufferLength) override; + + int32_t StartRecordingAudioFile(const char* fileName, + const FileFormats format, + const CodecInst& codecInst, + const uint32_t notificationTimeMs = 0, + const uint32_t maxSizeBytes = 0) override; + + int32_t StartRecordingAudioStream( + OutStream& stream, + const FileFormats format, + const CodecInst& codecInst, + const uint32_t notificationTimeMs = 0) override; + + int32_t StopRecording() override; + + bool IsRecording() override; + + int32_t RecordDurationMs(uint32_t& durationMs) override; + + bool IsStereo() override; + + int32_t SetModuleFileCallback(FileCallback* callback) override; + + int32_t FileDurationMs(const char* fileName, + uint32_t& durationMs, + const FileFormats format, + const uint32_t freqInHz = 16000) override; + + int32_t codec_info(CodecInst& codecInst) const override; + +private: + // Returns true if the combination of format and codecInst is valid. + static bool ValidFileFormat(const FileFormats format, + const CodecInst* codecInst); + + + // Returns true if the filename is valid + static bool ValidFileName(const char* fileName); + + // Returns true if the combination of startPointMs and stopPointMs is valid. + static bool ValidFilePositions(const uint32_t startPointMs, + const uint32_t stopPointMs); + + // Returns true if frequencyInHz is a supported frequency. + static bool ValidFrequency(const uint32_t frequencyInHz); + + void HandlePlayCallbacks(int32_t bytesRead); + + int32_t StartPlayingStream( + InStream& stream, + bool loop, + const uint32_t notificationTimeMs, + const FileFormats format, + const CodecInst* codecInst, + const uint32_t startPointMs, + const uint32_t stopPointMs); + + int32_t _id; + rtc::CriticalSection _crit; + rtc::CriticalSection _callbackCrit; + + ModuleFileUtility* _ptrFileUtilityObj; + CodecInst codec_info_; + + InStream* _ptrInStream; + OutStream* _ptrOutStream; + + FileFormats _fileFormat; + uint32_t _recordDurationMs; + uint32_t _playoutPositionMs; + uint32_t _notificationMs; + + bool _playingActive; + bool _recordingActive; + bool _isStereo; + bool _openFile; + + char _fileName[512]; + + FileCallback* _ptrCallback; +}; +} // namespace webrtc + +#endif // MODULES_MEDIA_FILE_MEDIA_FILE_IMPL_H_ diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file_unittest.cc b/third_party/libwebrtc/webrtc/modules/media_file/media_file_unittest.cc new file mode 100644 index 0000000000..7df1a68cde --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file_unittest.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/media_file/media_file.h" +#include "system_wrappers/include/sleep.h" +#include "test/gtest.h" +#include "test/testsupport/fileutils.h" + +class MediaFileTest : public testing::Test { + protected: + void SetUp() { + // Use number 0 as the the identifier and pass to CreateMediaFile. + media_file_ = webrtc::MediaFile::CreateMediaFile(0); + ASSERT_TRUE(media_file_ != NULL); + } + void TearDown() { + webrtc::MediaFile::DestroyMediaFile(media_file_); + media_file_ = NULL; + } + webrtc::MediaFile* media_file_; +}; + +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) +#define MAYBE_StartPlayingAudioFileWithoutError \ + DISABLED_StartPlayingAudioFileWithoutError +#else +#define MAYBE_StartPlayingAudioFileWithoutError \ + StartPlayingAudioFileWithoutError +#endif +TEST_F(MediaFileTest, MAYBE_StartPlayingAudioFileWithoutError) { + // TODO(leozwang): Use hard coded filename here, we want to + // loop through all audio files in future + const std::string audio_file = + webrtc::test::ResourcePath("voice_engine/audio_tiny48", "wav"); + ASSERT_EQ(0, media_file_->StartPlayingAudioFile( + audio_file.c_str(), + 0, + false, + webrtc::kFileFormatWavFile)); + + ASSERT_EQ(true, media_file_->IsPlaying()); + + webrtc::SleepMs(1); + + ASSERT_EQ(0, media_file_->StopPlaying()); +} + +#if defined(WEBRTC_IOS) +#define MAYBE_WriteWavFile DISABLED_WriteWavFile +#else +#define MAYBE_WriteWavFile WriteWavFile +#endif +TEST_F(MediaFileTest, MAYBE_WriteWavFile) { + // Write file. + static const size_t kHeaderSize = 44; + static const size_t kPayloadSize = 320; + webrtc::CodecInst codec = { + 0, "L16", 16000, static_cast<int>(kPayloadSize), 1 + }; + std::string outfile = webrtc::test::OutputPath() + "wavtest.wav"; + ASSERT_EQ(0, + media_file_->StartRecordingAudioFile( + outfile.c_str(), webrtc::kFileFormatWavFile, codec)); + static const int8_t kFakeData[kPayloadSize] = {0}; + ASSERT_EQ(0, media_file_->IncomingAudioData(kFakeData, kPayloadSize)); + ASSERT_EQ(0, media_file_->StopRecording()); + + // Check the file we just wrote. + static const uint8_t kExpectedHeader[] = { + 'R', 'I', 'F', 'F', + 0x64, 0x1, 0, 0, // size of whole file - 8: 320 + 44 - 8 + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 0x10, 0, 0, 0, // size of fmt block - 8: 24 - 8 + 0x1, 0, // format: PCM (1) + 0x1, 0, // channels: 1 + 0x80, 0x3e, 0, 0, // sample rate: 16000 + 0, 0x7d, 0, 0, // byte rate: 2 * 16000 + 0x2, 0, // block align: NumChannels * BytesPerSample + 0x10, 0, // bits per sample: 2 * 8 + 'd', 'a', 't', 'a', + 0x40, 0x1, 0, 0, // size of payload: 320 + }; + static_assert(sizeof(kExpectedHeader) == kHeaderSize, "header size"); + + EXPECT_EQ(kHeaderSize + kPayloadSize, webrtc::test::GetFileSize(outfile)); + FILE* f = fopen(outfile.c_str(), "rb"); + ASSERT_TRUE(f); + + uint8_t header[kHeaderSize]; + ASSERT_EQ(1u, fread(header, kHeaderSize, 1, f)); + EXPECT_EQ(0, memcmp(kExpectedHeader, header, kHeaderSize)); + + uint8_t payload[kPayloadSize]; + ASSERT_EQ(1u, fread(payload, kPayloadSize, 1, f)); + EXPECT_EQ(0, memcmp(kFakeData, payload, kPayloadSize)); + + EXPECT_EQ(0, fclose(f)); +} diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file_utility.cc b/third_party/libwebrtc/webrtc/modules/media_file/media_file_utility.cc new file mode 100644 index 0000000000..f4ccba10df --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file_utility.cc @@ -0,0 +1,1238 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/media_file/media_file_utility.h" + +#include <assert.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <limits> + +#include "common_audio/wav_header.h" +#include "common_types.h" // NOLINT(build/include) +#include "modules/include/module_common_types.h" +#include "rtc_base/format_macros.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/file_wrapper.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace { + +// First 16 bytes the WAVE header. ckID should be "RIFF", wave_ckID should be +// "WAVE" and ckSize is the chunk size (4 + n) +struct WAVE_RIFF_header { + int8_t ckID[4]; + int32_t ckSize; + int8_t wave_ckID[4]; +}; + +// First 8 byte of the format chunk. fmt_ckID should be "fmt ". fmt_ckSize is +// the chunk size (16, 18 or 40 byte) +struct WAVE_CHUNK_header { + int8_t fmt_ckID[4]; + uint32_t fmt_ckSize; +}; +} // unnamed namespace + +namespace webrtc { +ModuleFileUtility::ModuleFileUtility() + : _wavFormatObj(), + _dataSize(0), + _readSizeBytes(0), + _stopPointInMs(0), + _startPointInMs(0), + _playoutPositionMs(0), + _bytesWritten(0), + codec_info_(), + _codecId(kCodecNoCodec), + _bytesPerSample(0), + _readPos(0), + _reading(false), + _writing(false), + _tempData() { + RTC_LOG(LS_INFO) << "ModuleFileUtility::ModuleFileUtility()"; + memset(&codec_info_, 0, sizeof(CodecInst)); + codec_info_.pltype = -1; +} + +ModuleFileUtility::~ModuleFileUtility() { + RTC_LOG(LS_INFO) << "ModuleFileUtility::~ModuleFileUtility()"; +} + +int32_t ModuleFileUtility::ReadWavHeader(InStream& wav) { + WAVE_RIFF_header RIFFheaderObj; + WAVE_CHUNK_header CHUNKheaderObj; + // TODO (hellner): tmpStr and tmpStr2 seems unnecessary here. + char tmpStr[6] = "FOUR"; + unsigned char tmpStr2[4]; + size_t i; + bool dataFound = false; + bool fmtFound = false; + int8_t dummyRead; + + _dataSize = 0; + int len = wav.Read(&RIFFheaderObj, sizeof(WAVE_RIFF_header)); + if (len != static_cast<int>(sizeof(WAVE_RIFF_header))) { + RTC_LOG(LS_ERROR) << "Not a wave file (too short)"; + return -1; + } + + for (i = 0; i < 4; i++) { + tmpStr[i] = RIFFheaderObj.ckID[i]; + } + if (strcmp(tmpStr, "RIFF") != 0) { + RTC_LOG(LS_ERROR) << "Not a wave file (does not have RIFF)"; + return -1; + } + for (i = 0; i < 4; i++) { + tmpStr[i] = RIFFheaderObj.wave_ckID[i]; + } + if (strcmp(tmpStr, "WAVE") != 0) { + RTC_LOG(LS_ERROR) << "Not a wave file (does not have WAVE)"; + return -1; + } + + len = wav.Read(&CHUNKheaderObj, sizeof(WAVE_CHUNK_header)); + + // WAVE files are stored in little endian byte order. Make sure that the + // data can be read on big endian as well. + // TODO (hellner): little endian to system byte order should be done in + // in a subroutine. + memcpy(tmpStr2, &CHUNKheaderObj.fmt_ckSize, 4); + CHUNKheaderObj.fmt_ckSize = + (uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8) + + (((uint32_t)tmpStr2[2]) << 16) + (((uint32_t)tmpStr2[3]) << 24); + + memcpy(tmpStr, CHUNKheaderObj.fmt_ckID, 4); + + while ((len == static_cast<int>(sizeof(WAVE_CHUNK_header))) && + (!fmtFound || !dataFound)) { + if (strcmp(tmpStr, "fmt ") == 0) { + len = wav.Read(&_wavFormatObj, sizeof(WAVE_FMTINFO_header)); + + memcpy(tmpStr2, &_wavFormatObj.formatTag, 2); + _wavFormatObj.formatTag = + (uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8); + memcpy(tmpStr2, &_wavFormatObj.nChannels, 2); + _wavFormatObj.nChannels = + (int16_t)((uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8)); + memcpy(tmpStr2, &_wavFormatObj.nSamplesPerSec, 4); + _wavFormatObj.nSamplesPerSec = (int32_t)( + (uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8) + + (((uint32_t)tmpStr2[2]) << 16) + (((uint32_t)tmpStr2[3]) << 24)); + memcpy(tmpStr2, &_wavFormatObj.nAvgBytesPerSec, 4); + _wavFormatObj.nAvgBytesPerSec = (int32_t)( + (uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8) + + (((uint32_t)tmpStr2[2]) << 16) + (((uint32_t)tmpStr2[3]) << 24)); + memcpy(tmpStr2, &_wavFormatObj.nBlockAlign, 2); + _wavFormatObj.nBlockAlign = + (int16_t)((uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8)); + memcpy(tmpStr2, &_wavFormatObj.nBitsPerSample, 2); + _wavFormatObj.nBitsPerSample = + (int16_t)((uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8)); + + if (CHUNKheaderObj.fmt_ckSize < sizeof(WAVE_FMTINFO_header)) { + RTC_LOG(LS_ERROR) << "Chunk size is too small"; + return -1; + } + for (i = 0; i < CHUNKheaderObj.fmt_ckSize - sizeof(WAVE_FMTINFO_header); + i++) { + len = wav.Read(&dummyRead, 1); + if (len != 1) { + RTC_LOG(LS_ERROR) << "File corrupted, reached EOF (reading fmt)"; + return -1; + } + } + fmtFound = true; + } else if (strcmp(tmpStr, "data") == 0) { + _dataSize = CHUNKheaderObj.fmt_ckSize; + dataFound = true; + break; + } else { + for (i = 0; i < CHUNKheaderObj.fmt_ckSize; i++) { + len = wav.Read(&dummyRead, 1); + if (len != 1) { + RTC_LOG(LS_ERROR) << "File corrupted, reached EOF (reading other)"; + return -1; + } + } + } + + len = wav.Read(&CHUNKheaderObj, sizeof(WAVE_CHUNK_header)); + + memcpy(tmpStr2, &CHUNKheaderObj.fmt_ckSize, 4); + CHUNKheaderObj.fmt_ckSize = + (uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8) + + (((uint32_t)tmpStr2[2]) << 16) + (((uint32_t)tmpStr2[3]) << 24); + + memcpy(tmpStr, CHUNKheaderObj.fmt_ckID, 4); + } + + // Either a proper format chunk has been read or a data chunk was come + // across. + if ((_wavFormatObj.formatTag != kWavFormatPcm) && + (_wavFormatObj.formatTag != kWavFormatALaw) && + (_wavFormatObj.formatTag != kWavFormatMuLaw)) { + RTC_LOG(LS_ERROR) << "Coding formatTag value=" << _wavFormatObj.formatTag + << " not supported!"; + return -1; + } + if ((_wavFormatObj.nChannels < 1) || (_wavFormatObj.nChannels > 2)) { + RTC_LOG(LS_ERROR) << "nChannels value=" << _wavFormatObj.nChannels + << " not supported!"; + return -1; + } + + if ((_wavFormatObj.nBitsPerSample != 8) && + (_wavFormatObj.nBitsPerSample != 16)) { + RTC_LOG(LS_ERROR) << "nBitsPerSample value=" << _wavFormatObj.nBitsPerSample + << " not supported!"; + return -1; + } + + // Calculate the number of bytes that 10 ms of audio data correspond to. + size_t samples_per_10ms = + ((_wavFormatObj.formatTag == kWavFormatPcm) && + (_wavFormatObj.nSamplesPerSec == 44100)) + ? 440 + : static_cast<size_t>(_wavFormatObj.nSamplesPerSec / 100); + _readSizeBytes = samples_per_10ms * _wavFormatObj.nChannels * + (_wavFormatObj.nBitsPerSample / 8); + return 0; +} + +int32_t ModuleFileUtility::InitWavCodec(uint32_t samplesPerSec, + size_t channels, + uint32_t bitsPerSample, + uint32_t formatTag) { + codec_info_.pltype = -1; + codec_info_.plfreq = samplesPerSec; + codec_info_.channels = channels; + codec_info_.rate = bitsPerSample * samplesPerSec; + + // Calculate the packet size for 10ms frames + switch (formatTag) { + case kWavFormatALaw: + strcpy(codec_info_.plname, "PCMA"); + _codecId = kCodecPcma; + codec_info_.pltype = 8; + codec_info_.pacsize = codec_info_.plfreq / 100; + break; + case kWavFormatMuLaw: + strcpy(codec_info_.plname, "PCMU"); + _codecId = kCodecPcmu; + codec_info_.pltype = 0; + codec_info_.pacsize = codec_info_.plfreq / 100; + break; + case kWavFormatPcm: + codec_info_.pacsize = (bitsPerSample * (codec_info_.plfreq / 100)) / 8; + if (samplesPerSec == 8000) { + strcpy(codec_info_.plname, "L16"); + _codecId = kCodecL16_8Khz; + } else if (samplesPerSec == 16000) { + strcpy(codec_info_.plname, "L16"); + _codecId = kCodecL16_16kHz; + } else if (samplesPerSec == 32000) { + strcpy(codec_info_.plname, "L16"); + _codecId = kCodecL16_32Khz; + } + // Set the packet size for "odd" sampling frequencies so that it + // properly corresponds to _readSizeBytes. + else if (samplesPerSec == 11025) { + strcpy(codec_info_.plname, "L16"); + _codecId = kCodecL16_16kHz; + codec_info_.pacsize = 110; + codec_info_.plfreq = 11000; + } else if (samplesPerSec == 22050) { + strcpy(codec_info_.plname, "L16"); + _codecId = kCodecL16_16kHz; + codec_info_.pacsize = 220; + codec_info_.plfreq = 22000; + } else if (samplesPerSec == 44100) { + strcpy(codec_info_.plname, "L16"); + _codecId = kCodecL16_16kHz; + codec_info_.pacsize = 441; + codec_info_.plfreq = 44100; + } else if (samplesPerSec == 48000) { + strcpy(codec_info_.plname, "L16"); + _codecId = kCodecL16_16kHz; + codec_info_.pacsize = 480; + codec_info_.plfreq = 48000; + } else { + RTC_LOG(LS_ERROR) << "Unsupported PCM frequency!"; + return -1; + } + break; + default: + RTC_LOG(LS_ERROR) << "unknown WAV format TAG!"; + return -1; + break; + } + return 0; +} + +int32_t ModuleFileUtility::InitWavReading(InStream& wav, + const uint32_t start, + const uint32_t stop) { + _reading = false; + + if (ReadWavHeader(wav) == -1) { + RTC_LOG(LS_ERROR) << "failed to read WAV header!"; + return -1; + } + + _playoutPositionMs = 0; + _readPos = 0; + + if (start > 0) { + uint8_t dummy[WAV_MAX_BUFFER_SIZE]; + int readLength; + if (_readSizeBytes <= WAV_MAX_BUFFER_SIZE) { + while (_playoutPositionMs < start) { + readLength = wav.Read(dummy, _readSizeBytes); + if (readLength == static_cast<int>(_readSizeBytes)) { + _readPos += _readSizeBytes; + _playoutPositionMs += 10; + } else // Must have reached EOF before start position! + { + RTC_LOG(LS_ERROR) << "InitWavReading(), EOF before start position"; + return -1; + } + } + } else { + return -1; + } + } + if (InitWavCodec(_wavFormatObj.nSamplesPerSec, _wavFormatObj.nChannels, + _wavFormatObj.nBitsPerSample, + _wavFormatObj.formatTag) != 0) { + return -1; + } + _bytesPerSample = static_cast<size_t>(_wavFormatObj.nBitsPerSample / 8); + + _startPointInMs = start; + _stopPointInMs = stop; + _reading = true; + return 0; +} + +int32_t ModuleFileUtility::ReadWavDataAsMono(InStream& wav, + int8_t* outData, + const size_t bufferSize) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadWavDataAsMono(wav= " << &wav + << ", outData= " << static_cast<void*>(outData) + << ", bufSize= " << bufferSize << ")"; + + // The number of bytes that should be read from file. + const size_t totalBytesNeeded = _readSizeBytes; + // The number of bytes that will be written to outData. + const size_t bytesRequested = + (codec_info_.channels == 2) ? totalBytesNeeded >> 1 : totalBytesNeeded; + if (bufferSize < bytesRequested) { + RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: output buffer is too short!"; + return -1; + } + if (outData == NULL) { + RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: output buffer NULL!"; + return -1; + } + + if (!_reading) { + RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: no longer reading file."; + return -1; + } + + int32_t bytesRead = ReadWavData( + wav, (codec_info_.channels == 2) ? _tempData : (uint8_t*)outData, + totalBytesNeeded); + if (bytesRead == 0) { + return 0; + } + if (bytesRead < 0) { + RTC_LOG(LS_ERROR) + << "ReadWavDataAsMono: failed to read data from WAV file."; + return -1; + } + // Output data is should be mono. + if (codec_info_.channels == 2) { + for (size_t i = 0; i < bytesRequested / _bytesPerSample; i++) { + // Sample value is the average of left and right buffer rounded to + // closest integer value. Note samples can be either 1 or 2 byte. + if (_bytesPerSample == 1) { + _tempData[i] = ((_tempData[2 * i] + _tempData[(2 * i) + 1] + 1) >> 1); + } else { + int16_t* sampleData = (int16_t*)_tempData; + sampleData[i] = + ((sampleData[2 * i] + sampleData[(2 * i) + 1] + 1) >> 1); + } + } + memcpy(outData, _tempData, bytesRequested); + } + return static_cast<int32_t>(bytesRequested); +} + +int32_t ModuleFileUtility::ReadWavDataAsStereo(InStream& wav, + int8_t* outDataLeft, + int8_t* outDataRight, + const size_t bufferSize) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadWavDataAsStereo(wav= " << &wav + << ", outLeft= " << static_cast<void*>(outDataLeft) + << ", outRight= " << static_cast<void*>(outDataRight) + << ", bufSize= " << bufferSize << ")"; + + if ((outDataLeft == NULL) || (outDataRight == NULL)) { + RTC_LOG(LS_ERROR) << "ReadWavDataAsStereo: an input buffer is NULL!"; + return -1; + } + if (codec_info_.channels != 2) { + RTC_LOG(LS_ERROR) + << "ReadWavDataAsStereo: WAV file does not contain stereo data!"; + return -1; + } + if (!_reading) { + RTC_LOG(LS_ERROR) << "ReadWavDataAsStereo: no longer reading file."; + return -1; + } + + // The number of bytes that should be read from file. + const size_t totalBytesNeeded = _readSizeBytes; + // The number of bytes that will be written to the left and the right + // buffers. + const size_t bytesRequested = totalBytesNeeded >> 1; + if (bufferSize < bytesRequested) { + RTC_LOG(LS_ERROR) << "ReadWavDataAsStereo: Output buffers are too short!"; + assert(false); + return -1; + } + + int32_t bytesRead = ReadWavData(wav, _tempData, totalBytesNeeded); + if (bytesRead <= 0) { + RTC_LOG(LS_ERROR) + << "ReadWavDataAsStereo: failed to read data from WAV file."; + return -1; + } + + // Turn interleaved audio to left and right buffer. Note samples can be + // either 1 or 2 bytes + if (_bytesPerSample == 1) { + for (size_t i = 0; i < bytesRequested; i++) { + outDataLeft[i] = _tempData[2 * i]; + outDataRight[i] = _tempData[(2 * i) + 1]; + } + } else if (_bytesPerSample == 2) { + int16_t* sampleData = reinterpret_cast<int16_t*>(_tempData); + int16_t* outLeft = reinterpret_cast<int16_t*>(outDataLeft); + int16_t* outRight = reinterpret_cast<int16_t*>(outDataRight); + + // Bytes requested to samples requested. + size_t sampleCount = bytesRequested >> 1; + for (size_t i = 0; i < sampleCount; i++) { + outLeft[i] = sampleData[2 * i]; + outRight[i] = sampleData[(2 * i) + 1]; + } + } else { + RTC_LOG(LS_ERROR) << "ReadWavStereoData: unsupported sample size " + << _bytesPerSample << "!"; + assert(false); + return -1; + } + return static_cast<int32_t>(bytesRequested); +} + +int32_t ModuleFileUtility::ReadWavData(InStream& wav, + uint8_t* buffer, + size_t dataLengthInBytes) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadWavData(wav= " << &wav + << ", buffer= " << static_cast<void*>(buffer) + << ", dataLen= " << dataLengthInBytes << ")"; + + if (buffer == NULL) { + RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: output buffer NULL!"; + return -1; + } + + // Make sure that a read won't return too few samples. + // TODO (hellner): why not read the remaining bytes needed from the start + // of the file? + if (_dataSize < (_readPos + dataLengthInBytes)) { + // Rewind() being -1 may be due to the file not supposed to be looped. + if (wav.Rewind() == -1) { + _reading = false; + return 0; + } + if (InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1) { + _reading = false; + return -1; + } + } + + int32_t bytesRead = wav.Read(buffer, dataLengthInBytes); + if (bytesRead < 0) { + _reading = false; + return -1; + } + + // This should never happen due to earlier sanity checks. + // TODO (hellner): change to an assert and fail here since this should + // never happen... + if (bytesRead < (int32_t)dataLengthInBytes) { + if ((wav.Rewind() == -1) || + (InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1)) { + _reading = false; + return -1; + } else { + bytesRead = wav.Read(buffer, dataLengthInBytes); + if (bytesRead < (int32_t)dataLengthInBytes) { + _reading = false; + return -1; + } + } + } + + _readPos += bytesRead; + + // TODO (hellner): Why is dataLengthInBytes let dictate the number of bytes + // to read when exactly 10ms should be read?! + _playoutPositionMs += 10; + if ((_stopPointInMs > 0) && (_playoutPositionMs >= _stopPointInMs)) { + if ((wav.Rewind() == -1) || + (InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1)) { + _reading = false; + } + } + return bytesRead; +} + +int32_t ModuleFileUtility::InitWavWriting(OutStream& wav, + const CodecInst& codecInst) { + if (set_codec_info(codecInst) != 0) { + RTC_LOG(LS_ERROR) << "codecInst identifies unsupported codec!"; + return -1; + } + _writing = false; + size_t channels = (codecInst.channels == 0) ? 1 : codecInst.channels; + + if (STR_CASE_CMP(codecInst.plname, "PCMU") == 0) { + _bytesPerSample = 1; + if (WriteWavHeader(wav, 8000, _bytesPerSample, channels, kWavFormatMuLaw, + 0) == -1) { + return -1; + } + } else if (STR_CASE_CMP(codecInst.plname, "PCMA") == 0) { + _bytesPerSample = 1; + if (WriteWavHeader(wav, 8000, _bytesPerSample, channels, kWavFormatALaw, + 0) == -1) { + return -1; + } + } else if (STR_CASE_CMP(codecInst.plname, "L16") == 0) { + _bytesPerSample = 2; + if (WriteWavHeader(wav, codecInst.plfreq, _bytesPerSample, channels, + kWavFormatPcm, 0) == -1) { + return -1; + } + } else { + RTC_LOG(LS_ERROR) << "codecInst identifies unsupported codec for WAV file!"; + return -1; + } + _writing = true; + _bytesWritten = 0; + return 0; +} + +int32_t ModuleFileUtility::WriteWavData(OutStream& out, + const int8_t* buffer, + const size_t dataLength) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WriteWavData(out= " << &out + << ", buf= " << static_cast<const void*>(buffer) + << ", dataLen= " << dataLength << ")"; + + if (buffer == NULL) { + RTC_LOG(LS_ERROR) << "WriteWavData: input buffer NULL!"; + return -1; + } + + if (!out.Write(buffer, dataLength)) { + return -1; + } + _bytesWritten += dataLength; + return static_cast<int32_t>(dataLength); +} + +int32_t ModuleFileUtility::WriteWavHeader(OutStream& wav, + uint32_t freq, + size_t bytesPerSample, + size_t channels, + uint32_t format, + size_t lengthInBytes) { + // Frame size in bytes for 10 ms of audio. + // TODO (hellner): 44.1 kHz has 440 samples frame size. Doesn't seem to + // be taken into consideration here! + const size_t frameSize = (freq / 100) * channels; + + // Calculate the number of full frames that the wave file contain. + const size_t dataLengthInBytes = frameSize * (lengthInBytes / frameSize); + + uint8_t buf[kWavHeaderSize]; + webrtc::WriteWavHeader(buf, channels, freq, static_cast<WavFormat>(format), + bytesPerSample, dataLengthInBytes / bytesPerSample); + wav.Write(buf, kWavHeaderSize); + return 0; +} + +int32_t ModuleFileUtility::UpdateWavHeader(OutStream& wav) { + int32_t res = -1; + if (wav.Rewind() == -1) { + return -1; + } + size_t channels = (codec_info_.channels == 0) ? 1 : codec_info_.channels; + + if (STR_CASE_CMP(codec_info_.plname, "L16") == 0) { + res = WriteWavHeader(wav, codec_info_.plfreq, 2, channels, kWavFormatPcm, + _bytesWritten); + } else if (STR_CASE_CMP(codec_info_.plname, "PCMU") == 0) { + res = + WriteWavHeader(wav, 8000, 1, channels, kWavFormatMuLaw, _bytesWritten); + } else if (STR_CASE_CMP(codec_info_.plname, "PCMA") == 0) { + res = WriteWavHeader(wav, 8000, 1, channels, kWavFormatALaw, _bytesWritten); + } else { + // Allow calling this API even if not writing to a WAVE file. + // TODO (hellner): why?! + return 0; + } + return res; +} + +int32_t ModuleFileUtility::InitPreEncodedReading(InStream& in, + const CodecInst& cinst) { + uint8_t preEncodedID; + in.Read(&preEncodedID, 1); + + MediaFileUtility_CodecType codecType = + (MediaFileUtility_CodecType)preEncodedID; + + if (set_codec_info(cinst) != 0) { + RTC_LOG(LS_ERROR) << "Pre-encoded file send codec mismatch!"; + return -1; + } + if (codecType != _codecId) { + RTC_LOG(LS_ERROR) << "Pre-encoded file format codec mismatch!"; + return -1; + } + memcpy(&codec_info_, &cinst, sizeof(CodecInst)); + _reading = true; + return 0; +} + +int32_t ModuleFileUtility::ReadPreEncodedData(InStream& in, + int8_t* outData, + const size_t bufferSize) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadPreEncodedData(in= " << &in + << ", outData= " << static_cast<void*>(outData) + << ", bufferSize= " << bufferSize << ")"; + + if (outData == NULL) { + RTC_LOG(LS_ERROR) << "output buffer NULL"; + } + + size_t frameLen; + uint8_t buf[64]; + // Each frame has a two byte header containing the frame length. + int32_t res = in.Read(buf, 2); + if (res != 2) { + if (!in.Rewind()) { + // The first byte is the codec identifier. + in.Read(buf, 1); + res = in.Read(buf, 2); + } else { + return -1; + } + } + frameLen = buf[0] + buf[1] * 256; + if (bufferSize < frameLen) { + RTC_LOG(LS_ERROR) << "buffer not large enough to read " << frameLen + << " bytes of pre-encoded data!"; + return -1; + } + return in.Read(outData, frameLen); +} + +int32_t ModuleFileUtility::InitPreEncodedWriting(OutStream& out, + const CodecInst& codecInst) { + if (set_codec_info(codecInst) != 0) { + RTC_LOG(LS_ERROR) << "CodecInst not recognized!"; + return -1; + } + _writing = true; + _bytesWritten = 1; + out.Write(&_codecId, 1); + return 0; +} + +int32_t ModuleFileUtility::WritePreEncodedData(OutStream& out, + const int8_t* buffer, + const size_t dataLength) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WritePreEncodedData(out= " << &out + << " , inData= " << static_cast<const void*>(buffer) + << ", dataLen= " << dataLength << ")"; + + if (buffer == NULL) { + RTC_LOG(LS_ERROR) << "buffer NULL"; + } + + size_t bytesWritten = 0; + // The first two bytes is the size of the frame. + int16_t lengthBuf; + lengthBuf = (int16_t)dataLength; + if (dataLength > static_cast<size_t>(std::numeric_limits<int16_t>::max()) || + !out.Write(&lengthBuf, 2)) { + return -1; + } + bytesWritten = 2; + + if (!out.Write(buffer, dataLength)) { + return -1; + } + bytesWritten += dataLength; + return static_cast<int32_t>(bytesWritten); +} + +int32_t ModuleFileUtility::InitCompressedReading(InStream& in, + const uint32_t start, + const uint32_t stop) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::InitCompressedReading(in= " << &in + << ", start= " << start << ", stop= " << stop << ")"; + +#if defined(WEBRTC_CODEC_ILBC) + int16_t read_len = 0; +#endif + _codecId = kCodecNoCodec; + _playoutPositionMs = 0; + _reading = false; + + _startPointInMs = start; + _stopPointInMs = stop; + + // Read the codec name + int32_t cnt = 0; + char buf[64]; + do { + in.Read(&buf[cnt++], 1); + } while ((buf[cnt - 1] != '\n') && (64 > cnt)); + + if (cnt == 64) { + return -1; + } + buf[cnt] = 0; + +#ifdef WEBRTC_CODEC_ILBC + if (!strcmp("#!iLBC20\n", buf)) { + codec_info_.pltype = 102; + strcpy(codec_info_.plname, "ilbc"); + codec_info_.plfreq = 8000; + codec_info_.pacsize = 160; + codec_info_.channels = 1; + codec_info_.rate = 13300; + _codecId = kCodecIlbc20Ms; + + if (_startPointInMs > 0) { + while (_playoutPositionMs <= _startPointInMs) { + read_len = in.Read(buf, 38); + if (read_len != 38) { + return -1; + } + _playoutPositionMs += 20; + } + } + } + + if (!strcmp("#!iLBC30\n", buf)) { + codec_info_.pltype = 102; + strcpy(codec_info_.plname, "ilbc"); + codec_info_.plfreq = 8000; + codec_info_.pacsize = 240; + codec_info_.channels = 1; + codec_info_.rate = 13300; + _codecId = kCodecIlbc30Ms; + + if (_startPointInMs > 0) { + while (_playoutPositionMs <= _startPointInMs) { + read_len = in.Read(buf, 50); + if (read_len != 50) { + return -1; + } + _playoutPositionMs += 20; + } + } + } +#endif + if (_codecId == kCodecNoCodec) { + return -1; + } + _reading = true; + return 0; +} + +int32_t ModuleFileUtility::ReadCompressedData(InStream& in, + int8_t* outData, + size_t bufferSize) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadCompressedData(in=" << &in + << ", outData=" << static_cast<void*>(outData) + << ", bytes=" << bufferSize << ")"; + + int bytesRead = 0; + + if (!_reading) { + RTC_LOG(LS_ERROR) << "not currently reading!"; + return -1; + } + +#ifdef WEBRTC_CODEC_ILBC + if ((_codecId == kCodecIlbc20Ms) || (_codecId == kCodecIlbc30Ms)) { + size_t byteSize = 0; + if (_codecId == kCodecIlbc30Ms) { + byteSize = 50; + } + if (_codecId == kCodecIlbc20Ms) { + byteSize = 38; + } + if (bufferSize < byteSize) { + RTC_LOG(LS_ERROR) + << "output buffer is too short to read ILBC compressed data."; + assert(false); + return -1; + } + + bytesRead = in.Read(outData, byteSize); + if (bytesRead != static_cast<int>(byteSize)) { + if (!in.Rewind()) { + InitCompressedReading(in, _startPointInMs, _stopPointInMs); + bytesRead = in.Read(outData, byteSize); + if (bytesRead != static_cast<int>(byteSize)) { + _reading = false; + return -1; + } + } else { + _reading = false; + return -1; + } + } + } +#endif + if (bytesRead == 0) { + RTC_LOG(LS_ERROR) + << "ReadCompressedData() no bytes read, codec not supported"; + return -1; + } + + _playoutPositionMs += 20; + if ((_stopPointInMs > 0) && (_playoutPositionMs >= _stopPointInMs)) { + if (!in.Rewind()) { + InitCompressedReading(in, _startPointInMs, _stopPointInMs); + } else { + _reading = false; + } + } + return bytesRead; +} + +int32_t ModuleFileUtility::InitCompressedWriting(OutStream& out, + const CodecInst& codecInst) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::InitCompressedWriting(out= " + << &out << ", codecName= " << codecInst.plname << ")"; + + _writing = false; + +#ifdef WEBRTC_CODEC_ILBC + if (STR_CASE_CMP(codecInst.plname, "ilbc") == 0) { + if (codecInst.pacsize == 160) { + _codecId = kCodecIlbc20Ms; + out.Write("#!iLBC20\n", 9); + } else if (codecInst.pacsize == 240) { + _codecId = kCodecIlbc30Ms; + out.Write("#!iLBC30\n", 9); + } else { + RTC_LOG(LS_ERROR) << "codecInst defines unsupported compression codec!"; + return -1; + } + memcpy(&codec_info_, &codecInst, sizeof(CodecInst)); + _writing = true; + return 0; + } +#endif + + RTC_LOG(LS_ERROR) << "codecInst defines unsupported compression codec!"; + return -1; +} + +int32_t ModuleFileUtility::WriteCompressedData(OutStream& out, + const int8_t* buffer, + const size_t dataLength) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WriteCompressedData(out= " << &out + << ", buf= " << static_cast<const void*>(buffer) + << ", dataLen= " << dataLength << ")"; + + if (buffer == NULL) { + RTC_LOG(LS_ERROR) << "buffer NULL"; + } + + if (!out.Write(buffer, dataLength)) { + return -1; + } + return static_cast<int32_t>(dataLength); +} + +int32_t ModuleFileUtility::InitPCMReading(InStream& pcm, + const uint32_t start, + const uint32_t stop, + uint32_t freq) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::InitPCMReading(pcm= " << &pcm + << ", start=" << start << ", stop=" << stop + << ", freq=" << freq << ")"; + + int8_t dummy[320]; + int read_len; + + _playoutPositionMs = 0; + _startPointInMs = start; + _stopPointInMs = stop; + _reading = false; + + if (freq == 8000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 8000; + codec_info_.pacsize = 160; + codec_info_.channels = 1; + codec_info_.rate = 128000; + _codecId = kCodecL16_8Khz; + } else if (freq == 16000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 16000; + codec_info_.pacsize = 320; + codec_info_.channels = 1; + codec_info_.rate = 256000; + _codecId = kCodecL16_16kHz; + } else if (freq == 32000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 32000; + codec_info_.pacsize = 320; + codec_info_.channels = 1; + codec_info_.rate = 512000; + _codecId = kCodecL16_32Khz; + } else if (freq == 48000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 48000; + codec_info_.pacsize = 480; + codec_info_.channels = 1; + codec_info_.rate = 768000; + _codecId = kCodecL16_48Khz; + } + + // Readsize for 10ms of audio data (2 bytes per sample). + _readSizeBytes = 2 * codec_info_.plfreq / 100; + if (_startPointInMs > 0) { + while (_playoutPositionMs < _startPointInMs) { + read_len = pcm.Read(dummy, _readSizeBytes); + if (read_len != static_cast<int>(_readSizeBytes)) { + return -1; // Must have reached EOF before start position! + } + _playoutPositionMs += 10; + } + } + _reading = true; + return 0; +} + +int32_t ModuleFileUtility::ReadPCMData(InStream& pcm, + int8_t* outData, + size_t bufferSize) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadPCMData(pcm= " << &pcm + << ", outData= " << static_cast<void*>(outData) + << ", bufSize= " << bufferSize << ")"; + + if (outData == NULL) { + RTC_LOG(LS_ERROR) << "buffer NULL"; + } + + // Readsize for 10ms of audio data (2 bytes per sample). + size_t bytesRequested = static_cast<size_t>(2 * codec_info_.plfreq / 100); + if (bufferSize < bytesRequested) { + RTC_LOG(LS_ERROR) + << "ReadPCMData: buffer not long enough for a 10ms frame."; + assert(false); + return -1; + } + + int bytesRead = pcm.Read(outData, bytesRequested); + if (bytesRead < static_cast<int>(bytesRequested)) { + if (pcm.Rewind() == -1) { + _reading = false; + } else { + if (InitPCMReading(pcm, _startPointInMs, _stopPointInMs, + codec_info_.plfreq) == -1) { + _reading = false; + } else { + size_t rest = bytesRequested - bytesRead; + int len = pcm.Read(&(outData[bytesRead]), rest); + if (len == static_cast<int>(rest)) { + bytesRead += len; + } else { + _reading = false; + } + } + if (bytesRead <= 0) { + RTC_LOG(LS_ERROR) << "ReadPCMData: Failed to rewind audio file."; + return -1; + } + } + } + + if (bytesRead <= 0) { + RTC_LOG(LS_VERBOSE) << "ReadPCMData: end of file"; + return -1; + } + _playoutPositionMs += 10; + if (_stopPointInMs && _playoutPositionMs >= _stopPointInMs) { + if (!pcm.Rewind()) { + if (InitPCMReading(pcm, _startPointInMs, _stopPointInMs, + codec_info_.plfreq) == -1) { + _reading = false; + } + } + } + return bytesRead; +} + +int32_t ModuleFileUtility::InitPCMWriting(OutStream& out, uint32_t freq) { + if (freq == 8000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 8000; + codec_info_.pacsize = 160; + codec_info_.channels = 1; + codec_info_.rate = 128000; + + _codecId = kCodecL16_8Khz; + } else if (freq == 16000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 16000; + codec_info_.pacsize = 320; + codec_info_.channels = 1; + codec_info_.rate = 256000; + + _codecId = kCodecL16_16kHz; + } else if (freq == 32000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 32000; + codec_info_.pacsize = 320; + codec_info_.channels = 1; + codec_info_.rate = 512000; + + _codecId = kCodecL16_32Khz; + } else if (freq == 48000) { + strcpy(codec_info_.plname, "L16"); + codec_info_.pltype = -1; + codec_info_.plfreq = 48000; + codec_info_.pacsize = 480; + codec_info_.channels = 1; + codec_info_.rate = 768000; + + _codecId = kCodecL16_48Khz; + } + if ((_codecId != kCodecL16_8Khz) && (_codecId != kCodecL16_16kHz) && + (_codecId != kCodecL16_32Khz) && (_codecId != kCodecL16_48Khz)) { + RTC_LOG(LS_ERROR) << "CodecInst is not 8KHz, 16KHz, 32kHz or 48kHz PCM!"; + return -1; + } + _writing = true; + _bytesWritten = 0; + return 0; +} + +int32_t ModuleFileUtility::WritePCMData(OutStream& out, + const int8_t* buffer, + const size_t dataLength) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WritePCMData(out= " << &out + << ", buf= " << static_cast<const void*>(buffer) + << ", dataLen= " << dataLength << ")"; + + if (buffer == NULL) { + RTC_LOG(LS_ERROR) << "buffer NULL"; + } + + if (!out.Write(buffer, dataLength)) { + return -1; + } + + _bytesWritten += dataLength; + return static_cast<int32_t>(dataLength); +} + +int32_t ModuleFileUtility::codec_info(CodecInst& codecInst) { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::codec_info(codecInst= " + << &codecInst << ")"; + + if (!_reading && !_writing) { + RTC_LOG(LS_ERROR) << "CodecInst: not currently reading audio file!"; + return -1; + } + memcpy(&codecInst, &codec_info_, sizeof(CodecInst)); + return 0; +} + +int32_t ModuleFileUtility::set_codec_info(const CodecInst& codecInst) { + _codecId = kCodecNoCodec; + if (STR_CASE_CMP(codecInst.plname, "PCMU") == 0) { + _codecId = kCodecPcmu; + } else if (STR_CASE_CMP(codecInst.plname, "PCMA") == 0) { + _codecId = kCodecPcma; + } else if (STR_CASE_CMP(codecInst.plname, "L16") == 0) { + if (codecInst.plfreq == 8000) { + _codecId = kCodecL16_8Khz; + } else if (codecInst.plfreq == 16000) { + _codecId = kCodecL16_16kHz; + } else if (codecInst.plfreq == 32000) { + _codecId = kCodecL16_32Khz; + } else if (codecInst.plfreq == 48000) { + _codecId = kCodecL16_48Khz; + } + } +#ifdef WEBRTC_CODEC_ILBC + else if (STR_CASE_CMP(codecInst.plname, "ilbc") == 0) { + if (codecInst.pacsize == 160) { + _codecId = kCodecIlbc20Ms; + } else if (codecInst.pacsize == 240) { + _codecId = kCodecIlbc30Ms; + } + } +#endif +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + else if (STR_CASE_CMP(codecInst.plname, "isac") == 0) { + if (codecInst.plfreq == 16000) { + _codecId = kCodecIsac; + } else if (codecInst.plfreq == 32000) { + _codecId = kCodecIsacSwb; + } + } +#endif + else if (STR_CASE_CMP(codecInst.plname, "G722") == 0) { + _codecId = kCodecG722; + } + if (_codecId == kCodecNoCodec) { + return -1; + } + memcpy(&codec_info_, &codecInst, sizeof(CodecInst)); + return 0; +} + +int32_t ModuleFileUtility::FileDurationMs(const char* fileName, + const FileFormats fileFormat, + const uint32_t freqInHz) { + if (fileName == NULL) { + RTC_LOG(LS_ERROR) << "filename NULL"; + return -1; + } + + int32_t time_in_ms = -1; + struct stat file_size; + if (stat(fileName, &file_size) == -1) { + RTC_LOG(LS_ERROR) << "failed to retrieve file size with stat!"; + return -1; + } + FileWrapper* inStreamObj = FileWrapper::Create(); + if (inStreamObj == NULL) { + RTC_LOG(LS_INFO) << "failed to create InStream object!"; + return -1; + } + if (!inStreamObj->OpenFile(fileName, true)) { + delete inStreamObj; + RTC_LOG(LS_ERROR) << "failed to open file " << fileName << "!"; + return -1; + } + + switch (fileFormat) { + case kFileFormatWavFile: { + if (ReadWavHeader(*inStreamObj) == -1) { + RTC_LOG(LS_ERROR) << "failed to read WAV file header!"; + return -1; + } + time_in_ms = + ((file_size.st_size - 44) / (_wavFormatObj.nAvgBytesPerSec / 1000)); + break; + } + case kFileFormatPcm16kHzFile: { + // 16 samples per ms. 2 bytes per sample. + int32_t denominator = 16 * 2; + time_in_ms = (file_size.st_size) / denominator; + break; + } + case kFileFormatPcm8kHzFile: { + // 8 samples per ms. 2 bytes per sample. + int32_t denominator = 8 * 2; + time_in_ms = (file_size.st_size) / denominator; + break; + } + case kFileFormatCompressedFile: { + int32_t cnt = 0; + int read_len = 0; + char buf[64]; + do { + read_len = inStreamObj->Read(&buf[cnt++], 1); + if (read_len != 1) { + return -1; + } + } while ((buf[cnt - 1] != '\n') && (64 > cnt)); + + if (cnt == 64) { + return -1; + } else { + buf[cnt] = 0; + } +#ifdef WEBRTC_CODEC_ILBC + if (!strcmp("#!iLBC20\n", buf)) { + // 20 ms is 304 bits + time_in_ms = ((file_size.st_size) * 160) / 304; + break; + } + if (!strcmp("#!iLBC30\n", buf)) { + // 30 ms takes 400 bits. + // file size in bytes * 8 / 400 is the number of + // 30 ms frames in the file -> + // time_in_ms = file size * 8 / 400 * 30 + time_in_ms = ((file_size.st_size) * 240) / 400; + break; + } +#endif + break; + } + case kFileFormatPreencodedFile: { + RTC_LOG(LS_ERROR) << "cannot determine duration of Pre-Encoded file!"; + break; + } + default: + RTC_LOG(LS_ERROR) << "unsupported file format " << fileFormat << "!"; + break; + } + inStreamObj->CloseFile(); + delete inStreamObj; + return time_in_ms; +} + +uint32_t ModuleFileUtility::PlayoutPositionMs() { + RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::PlayoutPosition()"; + + return _reading ? _playoutPositionMs : 0; +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/modules/media_file/media_file_utility.h b/third_party/libwebrtc/webrtc/modules/media_file/media_file_utility.h new file mode 100644 index 0000000000..94785be64c --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/media_file/media_file_utility.h @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Note: the class cannot be used for reading and writing at the same time. +#ifndef MODULES_MEDIA_FILE_MEDIA_FILE_UTILITY_H_ +#define MODULES_MEDIA_FILE_MEDIA_FILE_UTILITY_H_ + +#include <stdio.h> + +#include "common_types.h" // NOLINT(build/include) +#include "modules/media_file/media_file_defines.h" + +namespace webrtc { +class InStream; +class OutStream; + +class ModuleFileUtility +{ +public: + + ModuleFileUtility(); + ~ModuleFileUtility(); + + // Prepare for playing audio from stream. + // startPointMs and stopPointMs, unless zero, specify what part of the file + // should be read. From startPointMs ms to stopPointMs ms. + int32_t InitWavReading(InStream& stream, + const uint32_t startPointMs = 0, + const uint32_t stopPointMs = 0); + + // Put 10-60ms of audio data from stream into the audioBuffer depending on + // codec frame size. dataLengthInBytes indicates the size of audioBuffer. + // The return value is the number of bytes written to audioBuffer. + // Note: This API only play mono audio but can be used on file containing + // audio with more channels (in which case the audio will be converted to + // mono). + int32_t ReadWavDataAsMono(InStream& stream, int8_t* audioBuffer, + const size_t dataLengthInBytes); + + // Put 10-60ms, depending on codec frame size, of audio data from file into + // audioBufferLeft and audioBufferRight. The buffers contain the left and + // right channel of played out stereo audio. + // dataLengthInBytes indicates the size of both audioBufferLeft and + // audioBufferRight. + // The return value is the number of bytes read for each buffer. + // Note: This API can only be successfully called for WAV files with stereo + // audio. + int32_t ReadWavDataAsStereo(InStream& wav, + int8_t* audioBufferLeft, + int8_t* audioBufferRight, + const size_t bufferLength); + + // Prepare for recording audio to stream. + // codecInst specifies the encoding of the audio data. + // Note: codecInst.channels should be set to 2 for stereo (and 1 for + // mono). Stereo is only supported for WAV files. + int32_t InitWavWriting(OutStream& stream, const CodecInst& codecInst); + + // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, + // to file. The audio frame size is determined by the codecInst.pacsize + // parameter of the last sucessfull StartRecordingAudioFile(..) call. + // The return value is the number of bytes written to audioBuffer. + int32_t WriteWavData(OutStream& stream, + const int8_t* audioBuffer, + const size_t bufferLength); + + // Finalizes the WAV header so that it is correct if nothing more will be + // written to stream. + // Note: this API must be called before closing stream to ensure that the + // WAVE header is updated with the file size. Don't call this API + // if more samples are to be written to stream. + int32_t UpdateWavHeader(OutStream& stream); + + // Prepare for playing audio from stream. + // startPointMs and stopPointMs, unless zero, specify what part of the file + // should be read. From startPointMs ms to stopPointMs ms. + // freqInHz is the PCM sampling frequency. + // NOTE, allowed frequencies are 8000, 16000 and 32000 (Hz) + int32_t InitPCMReading(InStream& stream, + const uint32_t startPointMs = 0, + const uint32_t stopPointMs = 0, + const uint32_t freqInHz = 16000); + + // Put 10-60ms of audio data from stream into the audioBuffer depending on + // codec frame size. dataLengthInBytes indicates the size of audioBuffer. + // The return value is the number of bytes written to audioBuffer. + int32_t ReadPCMData(InStream& stream, int8_t* audioBuffer, + const size_t dataLengthInBytes); + + // Prepare for recording audio to stream. + // freqInHz is the PCM sampling frequency. + // NOTE, allowed frequencies are 8000, 16000 and 32000 (Hz) + int32_t InitPCMWriting(OutStream& stream, const uint32_t freqInHz = 16000); + + // Write one 10ms audio frame, i.e. the bufferLength first bytes of + // audioBuffer, to file. The audio frame size is determined by the freqInHz + // parameter of the last sucessfull InitPCMWriting(..) call. + // The return value is the number of bytes written to audioBuffer. + int32_t WritePCMData(OutStream& stream, + const int8_t* audioBuffer, + size_t bufferLength); + + // Prepare for playing audio from stream. + // startPointMs and stopPointMs, unless zero, specify what part of the file + // should be read. From startPointMs ms to stopPointMs ms. + int32_t InitCompressedReading(InStream& stream, + const uint32_t startPointMs = 0, + const uint32_t stopPointMs = 0); + + // Put 10-60ms of audio data from stream into the audioBuffer depending on + // codec frame size. dataLengthInBytes indicates the size of audioBuffer. + // The return value is the number of bytes written to audioBuffer. + int32_t ReadCompressedData(InStream& stream, + int8_t* audioBuffer, + const size_t dataLengthInBytes); + + // Prepare for recording audio to stream. + // codecInst specifies the encoding of the audio data. + int32_t InitCompressedWriting(OutStream& stream, + const CodecInst& codecInst); + + // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, + // to file. The audio frame size is determined by the codecInst.pacsize + // parameter of the last sucessfull InitCompressedWriting(..) call. + // The return value is the number of bytes written to stream. + // Note: bufferLength must be exactly one frame. + int32_t WriteCompressedData(OutStream& stream, + const int8_t* audioBuffer, + const size_t bufferLength); + + // Prepare for playing audio from stream. + // codecInst specifies the encoding of the audio data. + int32_t InitPreEncodedReading(InStream& stream, + const CodecInst& codecInst); + + // Put 10-60ms of audio data from stream into the audioBuffer depending on + // codec frame size. dataLengthInBytes indicates the size of audioBuffer. + // The return value is the number of bytes written to audioBuffer. + int32_t ReadPreEncodedData(InStream& stream, + int8_t* audioBuffer, + const size_t dataLengthInBytes); + + // Prepare for recording audio to stream. + // codecInst specifies the encoding of the audio data. + int32_t InitPreEncodedWriting(OutStream& stream, + const CodecInst& codecInst); + + // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, + // to stream. The audio frame size is determined by the codecInst.pacsize + // parameter of the last sucessfull InitPreEncodedWriting(..) call. + // The return value is the number of bytes written to stream. + // Note: bufferLength must be exactly one frame. + int32_t WritePreEncodedData(OutStream& stream, + const int8_t* inData, + const size_t dataLengthInBytes); + + // Set durationMs to the size of the file (in ms) specified by fileName. + // freqInHz specifies the sampling frequency of the file. + int32_t FileDurationMs(const char* fileName, + const FileFormats fileFormat, + const uint32_t freqInHz = 16000); + + // Return the number of ms that have been played so far. + uint32_t PlayoutPositionMs(); + + // Update codecInst according to the current audio codec being used for + // reading or writing. + int32_t codec_info(CodecInst& codecInst); + +private: + // Biggest WAV frame supported is 10 ms at 48kHz of 2 channel, 16 bit audio. + static const size_t WAV_MAX_BUFFER_SIZE = 480 * 2 * 2; + + + int32_t InitWavCodec(uint32_t samplesPerSec, + size_t channels, + uint32_t bitsPerSample, + uint32_t formatTag); + + // Parse the WAV header in stream. + int32_t ReadWavHeader(InStream& stream); + + // Update the WAV header. freqInHz, bytesPerSample, channels, format, + // lengthInBytes specify characterists of the audio data. + // freqInHz is the sampling frequency. bytesPerSample is the sample size in + // bytes. channels is the number of channels, e.g. 1 is mono and 2 is + // stereo. format is the encode format (e.g. PCMU, PCMA, PCM etc). + // lengthInBytes is the number of bytes the audio samples are using up. + int32_t WriteWavHeader(OutStream& stream, + uint32_t freqInHz, + size_t bytesPerSample, + size_t channels, + uint32_t format, + size_t lengthInBytes); + + // Put dataLengthInBytes of audio data from stream into the audioBuffer. + // The return value is the number of bytes written to audioBuffer. + int32_t ReadWavData(InStream& stream, uint8_t* audioBuffer, + size_t dataLengthInBytes); + + // Update the current audio codec being used for reading or writing + // according to codecInst. + int32_t set_codec_info(const CodecInst& codecInst); + + struct WAVE_FMTINFO_header + { + int16_t formatTag; + int16_t nChannels; + int32_t nSamplesPerSec; + int32_t nAvgBytesPerSec; + int16_t nBlockAlign; + int16_t nBitsPerSample; + }; + // Identifiers for preencoded files. + enum MediaFileUtility_CodecType + { + kCodecNoCodec = 0, + kCodecIsac, + kCodecIsacSwb, + kCodecIsacLc, + kCodecL16_8Khz, + kCodecL16_16kHz, + kCodecL16_32Khz, + kCodecL16_48Khz, + kCodecPcmu, + kCodecPcma, + kCodecIlbc20Ms, + kCodecIlbc30Ms, + kCodecG722, + kCodecG722_1_32Kbps, + kCodecG722_1_24Kbps, + kCodecG722_1_16Kbps, + kCodecG722_1c_48, + kCodecG722_1c_32, + kCodecG722_1c_24, + kCodecAmr, + kCodecAmrWb, + kCodecG729, + kCodecG729_1, + kCodecG726_40, + kCodecG726_32, + kCodecG726_24, + kCodecG726_16 + }; + + // TODO (hellner): why store multiple formats. Just store either codec_info_ + // or _wavFormatObj and supply conversion functions. + WAVE_FMTINFO_header _wavFormatObj; + size_t _dataSize; // Chunk size if reading a WAV file + // Number of bytes to read. I.e. frame size in bytes. May be multiple + // chunks if reading WAV. + size_t _readSizeBytes; + + uint32_t _stopPointInMs; + uint32_t _startPointInMs; + uint32_t _playoutPositionMs; + size_t _bytesWritten; + + CodecInst codec_info_; + MediaFileUtility_CodecType _codecId; + + // The amount of bytes, on average, used for one audio sample. + size_t _bytesPerSample; + size_t _readPos; + + // Only reading or writing can be enabled, not both. + bool _reading; + bool _writing; + + // Scratch buffer used for turning stereo audio to mono. + uint8_t _tempData[WAV_MAX_BUFFER_SIZE]; +}; +} // namespace webrtc +#endif // MODULES_MEDIA_FILE_MEDIA_FILE_UTILITY_H_ |