diff --git a/CMakeLists.txt b/CMakeLists.txt index d368b34..8977d6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,10 +102,8 @@ if ("${CLANG_TIDY_SUPPORT}" MATCHES "true") message(FATAL_ERROR "You set support clang-tidy, but clang-tidy not found. Check path ${LLVM_PATH}/build/bin, weather clang-tidy exist. How to install tools? -See ${IPC_SDK_PATH}/doc/clang-tidy_user_guide.md -How to disable clang-tidy tool? -Modify: set(CLANG_TIDY_SUPPORT \"false\") -See:${IPC_SDK_PATH}/builde/global_config.cmake") +Execute : make compile_llvm (in the project root directory) +Or see ${CMAKE_SOURCE_DIR_IPCSDK}/doc/clang-tidy_user_guide.md") endif() endif() # find the clang-format tools diff --git a/README.md b/README.md index 7440f11..8d65bdd 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ```code # 项目根目录执行: -$ make install-cmake +$ make install_cmake ``` ### 1.2.2. llvm工具安装 diff --git a/application/MissionManager/CMakeLists.txt b/application/MissionManager/CMakeLists.txt index 8c8f30a..ee63ff1 100644 --- a/application/MissionManager/CMakeLists.txt +++ b/application/MissionManager/CMakeLists.txt @@ -20,6 +20,7 @@ include_directories( ${MIDDLEWARE_SOURCE_PATH}/McuAskBase/include ${MIDDLEWARE_SOURCE_PATH}/HuntingUpgrade/include ${MIDDLEWARE_SOURCE_PATH}/DeviceManager/include + ${MIDDLEWARE_SOURCE_PATH}/IpcConfig/include ) #do not rely on any other library #link_directories( @@ -30,7 +31,7 @@ aux_source_directory(./src SRC_FILES) set(TARGET_NAME MissionManager) add_library(${TARGET_NAME} STATIC ${SRC_FILES}) -target_link_libraries(${TARGET_NAME} McuAskBase StateMachine MediaManager StorageManager DeviceManager HuntingUpgrade KeyControl LedControl StatusCode Log) +target_link_libraries(${TARGET_NAME} McuAskBase StateMachine MediaManager StorageManager DeviceManager HuntingUpgrade KeyControl LedControl IpcConfig StatusCode Log) add_custom_target( MissionManager_code_check diff --git a/application/MissionManager/README.md b/application/MissionManager/README.md index a68c1a9..09371c6 100644 --- a/application/MissionManager/README.md +++ b/application/MissionManager/README.md @@ -21,8 +21,9 @@ stateDiagram-v2 [*] --> TopState TopState --> PowerOff TopState --> MSDCState -TopState --> DeviceAbnormal TopState --> MissionState +TopState --> DeviceAbnormal +TopState --> FormattingSDCard MissionState --> 空闲 MissionState --> 存储管理 存储管理 --> EMMC @@ -79,4 +80,10 @@ end ## 1.4. MCU监视器 -  MCU监视器必须由其中一个状态继承,只有状态机运行之后才能处理串口命令。 \ No newline at end of file +  MCU监视器必须由其中一个状态继承,只有状态机运行之后才能处理串口命令。 + +## 1.5. 测试启动模式(TestMissionState) + +  面向用户开放的测试模式。 + +1. 此状态下,先按下format按键不放,再按下reset按键不放可以关闭喂狗功能; diff --git a/application/MissionManager/src/AppMonitor.cpp b/application/MissionManager/src/AppMonitor.cpp index 873a0d5..5dee328 100644 --- a/application/MissionManager/src/AppMonitor.cpp +++ b/application/MissionManager/src/AppMonitor.cpp @@ -14,8 +14,10 @@ */ #include "AppMonitor.h" #include "IAppManager.h" +#include "IFilesManager.h" #include "ILog.h" #include "IStorageManager.h" +#include "MediaTask.h" #include "StatusCode.h" #include AppMonitor::AppMonitor() : mMicStatus(SwitchStatus::END) @@ -100,21 +102,42 @@ StatusCode AppMonitor::GetStorageInfo(std::vector ¶m) } StatusCode AppMonitor::GetStorageFileList(const AppGetFileInfo &fileInfo, std::vector ¶m) { - if (StorageFileEvent::LOOP == fileInfo.mEvent) { + std::vector info; + if (StorageFileEvent::ALL == fileInfo.mEvent) { + IFilesManager::GetInstance()->GetAllFiles(info); + } + else { + std::vector types; + if (StorageFileEvent::LOOP == fileInfo.mEvent) { + types.push_back(FileCreateType::TIMED); + types.push_back(FileCreateType::MANUAL_TEST); + types.push_back(FileCreateType::MANUAL_PHONE); + types.push_back(FileCreateType::TIMED); + } + if (StorageFileEvent::EMR == fileInfo.mEvent) { + types.push_back(FileCreateType::PIR); + } + if (StorageFileEvent::EVENT == fileInfo.mEvent) { + types.push_back(FileCreateType::MANUAL_TEST); + types.push_back(FileCreateType::MANUAL_PHONE); + } + if (StorageFileEvent::PARK == fileInfo.mEvent) { + types.push_back(FileCreateType::TIMED); + } + IFilesManager::GetInstance()->GetFiles(types, info); + } + const std::string webServerDocumentRoot = IAppManager::GetInstance()->GetFilesSavingRootPath(); + LogInfo("GetStorageFileList: file size = %d, event = %s.\n", + info.size(), + IAppManager::GetInstance()->StorageFileEventToString(fileInfo.mEvent)); + for (auto &oneFileInfo : info) { AppGetFileList file; - file.mCreateTime_s = 123456789; - file.mDuration = 182; - file.mName = "/DCIM/2024/01/15/20240115140207-20240115140509.mp4"; - file.mSize = 1024 * 182; + file.mCreateTime_s = oneFileInfo.mCreateTime_s; + file.mDuration = oneFileInfo.mFileDuration; + file.mName = RemovePrefix(oneFileInfo.mFileName, webServerDocumentRoot); + file.mSize = oneFileInfo.mFileSize; file.mType = StorageFileType::VIDEO; param.push_back(file); - AppGetFileList file2; - file2.mCreateTime_s = 123456789; - file2.mDuration = 0; - file2.mName = "/34a396526922a33e97906920dbfef2a5.jpg"; - file2.mSize = 1024; - file2.mType = StorageFileType::PICTURE; - param.push_back(file2); } return CreateStatusCode(STATUS_CODE_OK); } @@ -148,7 +171,10 @@ StatusCode AppMonitor::UploadFile(AppUploadFile ¶m) } StatusCode AppMonitor::GetThumbnail(AppGetThumbnail ¶m) { - param.mThumbnail = "./34a396526922a33e97906920dbfef2a5.jpg"; + // param.mThumbnail = "./34a396526922a33e97906920dbfef2a5.jpg"; + const std::string webServerDocumentRoot = IAppManager::GetInstance()->GetFilesSavingRootPath(); + const std::string thumbnailFile = MediaTask::GetThumbnailNameByTargetName(param.mFile); + param.mThumbnail = thumbnailFile; return CreateStatusCode(STATUS_CODE_OK); } SdCardStatus AppMonitor::SdCardStatusConvert(const StorageEvent &event) @@ -163,4 +189,12 @@ SdCardStatus AppMonitor::SdCardStatusConvert(const StorageEvent &event) default: return SdCardStatus::END; } +} +std::string AppMonitor::RemovePrefix(const std::string &originalStr, const std::string &prefix) +{ + if (originalStr.compare(0, prefix.length(), prefix) == 0) { + return originalStr.substr(prefix.length()); + } + LogWarning("Something wrong happened, prefix is %s.\n", prefix.c_str()); + return originalStr; } \ No newline at end of file diff --git a/application/MissionManager/src/AppMonitor.h b/application/MissionManager/src/AppMonitor.h index c4e48e9..5f7538c 100644 --- a/application/MissionManager/src/AppMonitor.h +++ b/application/MissionManager/src/AppMonitor.h @@ -41,6 +41,7 @@ public: private: SdCardStatus SdCardStatusConvert(const StorageEvent &event); + std::string RemovePrefix(const std::string &originalStr, const std::string &prefix); private: SwitchStatus mMicStatus; // TODO: improve delete. diff --git a/application/MissionManager/src/DataProcessing.h b/application/MissionManager/src/DataProcessing.h index 1b9ecb5..cb60930 100644 --- a/application/MissionManager/src/DataProcessing.h +++ b/application/MissionManager/src/DataProcessing.h @@ -24,12 +24,13 @@ using DataProcessingFunc = std::function; enum class InternalStateEvent { STORAGE_HANDLE_STATE_INIT = static_cast(MissionEvent::END), - SD_CARD_HANDLE_STATE_SD_STATUS_REPORTED, - ANY_STATE_SD_STATUS_PERORIED, + SD_CARD_HANDLE_STATE_SD_STATUS_REPORTED, ///< Only SdCardHandleState can process this message. + ANY_STATE_SD_STATUS_PERORIED, ///< Use it to notify other statuses. CHECK_UPGRADE_FILE, - MEDIA_REPORT_EVENT, + MEDIA_REPORT_EVENT, ///< Media events proactively reported by the bottom layer. KEY_EVENT_HANDLE, RESET_KEY_MEDIA_TASK, + FORMAT_KEY_FORMAT_SD_CARD, MEDIA_HANDLE_STATE_TASK_TIME_OUT, MEDIA_HANDLE_STATE_TASK_FINISHED, END diff --git a/application/MissionManager/src/FormattingState.cpp b/application/MissionManager/src/FormattingState.cpp new file mode 100644 index 0000000..3b9b620 --- /dev/null +++ b/application/MissionManager/src/FormattingState.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FormattingState.h" +#include "DataProcessing.h" +#include "IFilesManager.h" +#include "ILog.h" +#include "IMissionManager.h" +#include "IStateMachine.h" +#include "IStorageManager.h" +#include "LedControl.h" +#include "LedsHandle.h" +#include "MissionStateMachine.h" +#include +#include +using std::placeholders::_1; +FormattingState::FormattingState() : State("FormattingState"), mFormatting(false) +{ + mEventHandle[InternalStateEvent::FORMAT_KEY_FORMAT_SD_CARD] = + std::bind(&FormattingState::FormatKeyFormattingSDCardHandle, this, _1); + mEventHandle[InternalStateEvent::SD_CARD_HANDLE_STATE_SD_STATUS_REPORTED] = + std::bind(&FormattingState::ComfirmFormatResult, this, _1); +} +void FormattingState::GoInState() +{ + LogInfo(" ========== FormattingState::GoInState.\n"); +} +void FormattingState::GoOutState() +{ + LogInfo(" ========== FormattingState::GoOutState.\n"); + if (mFormattingThread.joinable()) { + mFormattingThread.join(); + } + mFormatting = false; + LedsHandle::DeleteAllLeds(); +} +bool FormattingState::ExecuteStateMsg(VStateMachineData *msg) +{ + return DataProcessing::EventHandle(msg); +} +void FormattingState::StateInit(void) +{ +} +void FormattingState::StateUnInit(void) +{ + if (mFormattingThread.joinable()) { + mFormattingThread.join(); + } +} +bool FormattingState::FormatKeyFormattingSDCardHandle(VStateMachineData *msg) +{ + LogInfo("Now formatting SD card.\n"); + auto formatting = [](std::shared_ptr impl) { + impl->FormattingThread(); + }; + mFormatting = true; + mFormattingThread = std::thread(formatting, shared_from_this()); + return EXECUTED; +} +bool FormattingState::ComfirmFormatResult(VStateMachineData *msg) +{ + std::shared_ptr message = std::dynamic_pointer_cast(msg->GetMessageObj()); + std::shared_ptr> data = + std::dynamic_pointer_cast>(message->mMissionData); + LogInfo(" SdCardEventHandle event:%s.\n", IStorageManager::GetInstance()->PrintStringStorageEvent(data->mData)); + if (StorageEvent::SD_CARD_INSERT == data->mData) { + LogInfo(" SD card inserted. Format sd card final finished.\n"); + // MissionStateMachine::GetInstance()->SwitchState(SystemState::IDLE_STATE); + MissionStateMachine::GetInstance()->DelayMessage(msg); + MissionStateMachine::GetInstance()->SwitchState(SystemState::SD_CARD_HANDLE_STATE); + } + return EXECUTED; +} +void FormattingState::FormattingThread(void) +{ + LedsHandle::ControlDeviceStatusLed(DeviceStatus::FORMATTING, KEEP_ALIVE_FOREVER, BLINKING_FAST_MS); + IFilesManager::GetInstance()->UnInit(); + IStorageManager::GetInstance()->FormatSDCardNow(); + // IFilesManager::GetInstance()->Init(); + // MissionStateMachine::GetInstance()->SwitchState(SystemState::IDLE_STATE); + LogInfo("Formatting SD card done.\n"); +} \ No newline at end of file diff --git a/application/MissionManager/src/FormattingState.h b/application/MissionManager/src/FormattingState.h new file mode 100644 index 0000000..fa0dcda --- /dev/null +++ b/application/MissionManager/src/FormattingState.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FORMATTING_STATE_H +#define FORMATTING_STATE_H +#include "DataProcessing.h" +#include "IFilesManager.h" +#include "IMediaManager.h" +#include "IStateMachine.h" +#include "IStorageManager.h" +#include "LedsHandle.h" +#include "VStateBase.h" +#include +class FormattingState : public State, + public DataProcessing, + public VStateBase, + public LedsHandle, + public std::enable_shared_from_this +{ +public: + FormattingState(); + virtual ~FormattingState() = default; + void GoInState() override; + void GoOutState() override; + bool ExecuteStateMsg(VStateMachineData *msg) override; + +protected: + void StateInit(void) override; + void StateUnInit(void) override; + bool FormatKeyFormattingSDCardHandle(VStateMachineData *msg); + bool ComfirmFormatResult(VStateMachineData *msg); + void FormattingThread(void); + +private: + bool mFormatting; + std::thread mFormattingThread; +}; + +#endif \ No newline at end of file diff --git a/application/MissionManager/src/IdleState.cpp b/application/MissionManager/src/IdleState.cpp index 74bd9ef..e6508a1 100644 --- a/application/MissionManager/src/IdleState.cpp +++ b/application/MissionManager/src/IdleState.cpp @@ -20,10 +20,9 @@ #include IdleState::IdleState() : State("IdleState") { - // mEventHandle[InternalStateEvent::MEDIA_REPORT_EVENT] = std::bind(&IdleState::MediaReportHandle, this, _1); - // mEventHandle[InternalStateEvent::SD_CARD_HANDLE_STATE_SD_STATUS_REPORTED] = - // std::bind(&IdleState::SdCardEventHandle, this, _1); mEventHandle[InternalStateEvent::RESET_KEY_MEDIA_TASK] = std::bind(&IdleState::ResetKeyMediaTaskHandle, this, _1); + mEventHandle[InternalStateEvent::FORMAT_KEY_FORMAT_SD_CARD] = + std::bind(&IdleState::FormatKeyFormattingSDCardHandle, this, _1); } void IdleState::GoInState() { @@ -38,6 +37,12 @@ bool IdleState::ExecuteStateMsg(VStateMachineData *msg) return DataProcessing::EventHandle(msg); } bool IdleState::ResetKeyMediaTaskHandle(VStateMachineData *msg) +{ + MissionStateMachine::GetInstance()->DelayMessage(msg); + MissionStateMachine::GetInstance()->SwitchState(SystemState::STORAGE_HANDLE_STATE); + return EXECUTED; +} +bool IdleState::FormatKeyFormattingSDCardHandle(VStateMachineData *msg) { MissionStateMachine::GetInstance()->DelayMessage(msg); MissionStateMachine::GetInstance()->SwitchState(SystemState::STORAGE_HANDLE_STATE); diff --git a/application/MissionManager/src/IdleState.h b/application/MissionManager/src/IdleState.h index 0f35b69..037534d 100644 --- a/application/MissionManager/src/IdleState.h +++ b/application/MissionManager/src/IdleState.h @@ -27,6 +27,7 @@ public: private: bool ResetKeyMediaTaskHandle(VStateMachineData *msg); + bool FormatKeyFormattingSDCardHandle(VStateMachineData *msg); private: }; diff --git a/application/MissionManager/src/LedsHandle.cpp b/application/MissionManager/src/LedsHandle.cpp index 3f9eb0f..6fd059e 100644 --- a/application/MissionManager/src/LedsHandle.cpp +++ b/application/MissionManager/src/LedsHandle.cpp @@ -16,22 +16,57 @@ #include "ILog.h" #include "LedControl.h" #include "SetLedState.h" +LedsHandle::LedsHandle() : mDeviceStatusFlag(DeviceStatus::END) +{ +} void LedsHandle::ControlDeviceStatusLed(const DeviceStatus &status, const long int &keepAliveTime, const unsigned int &blinkPeriod) { + if (KEEP_ALIVE_FOREVER == keepAliveTime) { + /** + * @brief For a specific state instance, when controlling the same indicator light, if the permanently + * maintained state changes, the original state needs to be overwritten. + */ + DeleteDeviceStatusLed(); + } switch (status) { case DeviceStatus::NORMAL: mDeviceStatus = SetLedState::ControlLed("device_status", LedState::GREEN, keepAliveTime, blinkPeriod); break; + case DeviceStatus::FORMAT_KEY_HOLD_DOWN_TIPS: + // if (DeviceStatus::FORMAT_KEY_HOLD_DOWN_TIPS == mDeviceStatusFlag) { + // break; + // } + mDeviceStatus = SetLedState::ControlLed("device_status", LedState::RED, keepAliveTime, blinkPeriod); + break; + case DeviceStatus::FORMATTING: + mDeviceStatus = SetLedState::ControlLed("device_status", LedState::RED, keepAliveTime, blinkPeriod); + break; + case DeviceStatus::SD_CARD_REMOVE: + mDeviceStatus = SetLedState::ControlLed("device_status", LedState::GREEN, keepAliveTime, blinkPeriod); + break; + case DeviceStatus::SD_CARD_ABNORMAL: + mDeviceStatus = SetLedState::ControlLed("device_status", LedState::RED, keepAliveTime, blinkPeriod); + break; + case DeviceStatus::TAKING_PICTURE_OR_VIDEO: + mDeviceStatus = SetLedState::ControlLed("device_status", LedState::GREEN, keepAliveTime, blinkPeriod); + break; + case DeviceStatus::CLOSE_WATCHDOG: + mDeviceStatus = SetLedState::ControlLed("device_status", LedState::RED, keepAliveTime, blinkPeriod); + break; default: LogWarning("unknow device status.\n"); break; } + mDeviceStatusFlag = status; } -void inline LedsHandle::DeleteDeviceStatusLed(void) +void LedsHandle::DeleteDeviceStatusLed(void) { - mDeviceStatus->DeleteState(); + if (mDeviceStatus) { + mDeviceStatus->DeleteState(); + mDeviceStatus.reset(); + } } void LedsHandle::DeleteAllLeds(void) { diff --git a/application/MissionManager/src/LedsHandle.h b/application/MissionManager/src/LedsHandle.h index e394d5b..2818bc1 100644 --- a/application/MissionManager/src/LedsHandle.h +++ b/application/MissionManager/src/LedsHandle.h @@ -19,12 +19,21 @@ enum class DeviceStatus { NORMAL = 0, TAKING_PICTURE_OR_VIDEO, + FORMATTING, ///< Prompt the user that the formatting operation is being performed. + FORMAT_KEY_HOLD_DOWN_TIPS, ///< Prompt the user that the format button has been pressed. + SD_CARD_REMOVE, + SD_CARD_INSERT, + SD_CARD_ABNORMAL, + CLOSE_WATCHDOG, END }; +/** + * @brief LedsHandle-LED light control class, mainly used to inherit LED light control functions in different states. + */ class LedsHandle { public: - LedsHandle() = default; + LedsHandle(); virtual ~LedsHandle() = default; protected: @@ -41,6 +50,12 @@ protected: void DeleteAllLeds(void); private: - std::shared_ptr mDeviceStatus; + /** + * @brief Definitions of various LED indicators. + * NOTE: When indicator lights with different functions switch states, the previous light state should be cleared by + * calling the DeleteState function. + */ + std::shared_ptr mDeviceStatus; ///< Device status indicator. + DeviceStatus mDeviceStatusFlag; }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/MediaHandleState.cpp b/application/MissionManager/src/MediaHandleState.cpp index df03864..ccdb185 100644 --- a/application/MissionManager/src/MediaHandleState.cpp +++ b/application/MissionManager/src/MediaHandleState.cpp @@ -19,6 +19,8 @@ #include "IMediaManager.h" #include "IMissionManager.h" #include "IStateMachine.h" +#include "LedControl.h" +#include "LedsHandle.h" #include "MediaTask.h" #include "MediaTaskHandle.h" #include "MissionStateMachine.h" @@ -42,6 +44,7 @@ void MediaHandleState::GoInState() void MediaHandleState::GoOutState() { LogInfo(" ========== MediaHandleState::GoOutState.\n"); + LedsHandle::DeleteAllLeds(); } bool MediaHandleState::ExecuteStateMsg(VStateMachineData *msg) { @@ -56,6 +59,8 @@ void MediaHandleState::TaskResponse(const MediaTaskInfo &taskinfo) bool MediaHandleState::ResetKeyMediaTaskHandle(VStateMachineData *msg) { MediaTaskHandle::MakeSingleTask(InternalStateEvent::RESET_KEY_MEDIA_TASK, shared_from_this()); + LedsHandle::ControlDeviceStatusLed( + DeviceStatus::TAKING_PICTURE_OR_VIDEO, KEEP_ALIVE_FOREVER, BLINKING_SUPER_FAST_MS); return EXECUTED; } bool MediaHandleState::MediaTaskFinishedHandle(VStateMachineData *msg) @@ -66,9 +71,18 @@ bool MediaHandleState::MediaTaskFinishedHandle(VStateMachineData *msg) LogInfo("response files = %d.\n", data->mData.mResponse.size()); std::vector files; for (auto &response : data->mData.mResponse) { - SyncFileInfo file(data->mData.mSerialNumber, response.mFileName, 0, 0, FileCreateType::END, FileStatus::END); + auto fileSize = MediaTask::GetFileSize_KB(response.mFileName.c_str()); + SyncFileInfo file(data->mData.mSerialNumber, + response.mFileName, + fileSize, + response.mDuration_ms, + data->mData.mCreateTime_s, + FileCreateType::MANUAL_TEST, + FileStatus::FINISHED_RECORD); files.push_back(file); } IFilesManager::GetInstance()->SaveFiles(files); + LedsHandle::DeleteDeviceStatusLed(); + MissionStateMachine::GetInstance()->SwitchState(SystemState::IDLE_STATE); return EXECUTED; } \ No newline at end of file diff --git a/application/MissionManager/src/MediaHandleState.h b/application/MissionManager/src/MediaHandleState.h index e68349a..06c158d 100644 --- a/application/MissionManager/src/MediaHandleState.h +++ b/application/MissionManager/src/MediaHandleState.h @@ -16,11 +16,13 @@ #define MEDIA_HANDLE_STATE_H #include "DataProcessing.h" #include "IStateMachine.h" +#include "LedsHandle.h" #include "MediaTaskHandle.h" class MediaHandleState : public State, public DataProcessing, public MediaTaskHandle, public VMediaTaskIniator, + public LedsHandle, public std::enable_shared_from_this { public: diff --git a/application/MissionManager/src/MediaTask.cpp b/application/MissionManager/src/MediaTask.cpp index 76cf51f..b0ff461 100644 --- a/application/MissionManager/src/MediaTask.cpp +++ b/application/MissionManager/src/MediaTask.cpp @@ -14,6 +14,7 @@ */ #include "MediaTask.h" #include "DataProcessing.h" +#include "IIpcConfig.h" #include "ILog.h" #include "IMediaManager.h" #include @@ -25,32 +26,43 @@ #include #include #include +#include #include MediaTask::MediaTask(const MediaTaskType &type, const InternalStateEvent &bindEvent, const std::weak_ptr &iniator, const unsigned long &serialNumber, const std::string &savePath) - : mType(type), mBindEvent(bindEvent), mIniator(iniator), mSerialNumber(serialNumber), mSavePath(savePath) + : mType(type), mBindEvent(bindEvent), mIniator(iniator), mSerialNumber(serialNumber), mSavePath(savePath), + mCreateTime_s(0) { mResponseData.reset(); + mTargetName.clear(); } unsigned int MediaTask::GetTaskTimeOutMs(void) { return MEDIA_TASK_TIMEOUT_MS; } +const MediaTaskType MediaTask::GetTaskType(void) +{ + return mType; +} std::string MediaTask::GetTargetNameForSaving(void) { + if (!mTargetName.empty()) { + return mTargetName; + } auto now = std::chrono::system_clock::now(); - time_t t_now = std::chrono::system_clock::to_time_t(now); - struct tm tm_now = *std::localtime(&t_now); - int hour = tm_now.tm_hour; - int minute = tm_now.tm_min; - int second = tm_now.tm_sec; - - std::ostringstream pathStream; - pathStream << mSavePath << std::setw(2) << std::setfill('0') << hour << std::setw(2) << std::setfill('0') << minute - << std::setw(2) << std::setfill('0') << second << ".mp4"; - return pathStream.str(); + mCreateTime_s = std::chrono::duration_cast(now.time_since_epoch()).count(); + const std::string fileType = MediaTaskType::TAKE_VIDEO == mType ? ".mp4" : ".jpeg"; + return CreateFileName(mSerialNumber, mSavePath, fileType); +} +std::string MediaTask::GetThumbnailNameForSaving(const std::string &targetName) +{ + return MediaTask::GetThumbnailNameByTargetName(targetName); +} +int MediaTask::GetVideoDuration_ms(void) +{ + return DEFAULT_VIDEO_DURATION_MS; } void MediaTask::Response(const std::vector &response) { @@ -63,6 +75,87 @@ void MediaTask::Response(const std::vector &response) MediaTaskInfo info = { .mResponse = response, .mSerialNumber = mSerialNumber, + .mCreateTime_s = mCreateTime_s, }; iniator->TaskResponse(info); +} +std::string MediaTask::GetThumbnailNameByTargetName(const std::string &targetName) +{ + // const std::string fileType = MediaTaskType::TAKE_VIDEO == mType ? ".mp4" : ".jpeg"; + std::string thumbnailName = targetName; + size_t dot_pos = thumbnailName.find_last_of('.'); + if (dot_pos != std::string::npos) { + std::string extension = thumbnailName.substr(dot_pos); + if (extension == ".mp4" || extension == ".jpeg") { + thumbnailName.replace(dot_pos, extension.length(), "-thumbnail.jpeg"); + LogInfo("GetThumbnailNameForSaving: %s\n", thumbnailName.c_str()); + return thumbnailName; + } + } + LogError("TargetName is not a mp4 file.\n"); + std::string unknowFile = "unknow"; + return unknowFile; +} +long long MediaTask::GetCreateTime_s(void) +{ + auto now = std::chrono::system_clock::now(); + // time_t t_now = std::chrono::system_clock::to_time_t(now); + // struct tm tm_now = *std::localtime(&t_now); + + return std::chrono::duration_cast(now.time_since_epoch()).count(); +} +int MediaTask::GetFileSize_KB(const char *filePath) +{ + struct stat fileInfo; + if (stat(filePath, &fileInfo) != 0) { + LogError("stat failed.\n"); + return FILE_SIZE_ERROR; + } + return static_cast(fileInfo.st_size) / 1024.0; +} +MediaTaskType MediaTask::WorkModeConvert(const WorkMode &mode) +{ + switch (mode) { + case WorkMode::MODE_PIC: + return MediaTaskType::TAKE_PICTURE; + case WorkMode::MODE_VIDEO: + return MediaTaskType::TAKE_VIDEO; + case WorkMode::MODE_PIC_VIDEO: + return MediaTaskType::TAKE_PICTURE_AND_VIDEO; + + default: + LogWarning("unknow work mode.\n"); + return MediaTaskType::TAKE_PICTURE_AND_VIDEO; + } +} +std::string MediaTask::GetFileNameBySeriaNumber(const unsigned long &serialNumber, const std::string &sourceFile, + const std::string &savePaht) +{ + std::string fileType = ""; + size_t dot_pos = sourceFile.find_last_of('.'); + if (dot_pos != std::string::npos) { + std::string extension = sourceFile.substr(dot_pos); + fileType = extension; + } + return CreateFileName(serialNumber, savePaht, fileType); +} +std::string MediaTask::CreateFileName(const unsigned long &serialNumber, const std::string &savePaht, + const std::string &fileType) +{ + auto now = std::chrono::system_clock::now(); + time_t t_now = std::chrono::system_clock::to_time_t(now); + struct tm tm_now = *std::localtime(&t_now); + /** + * @brief When the sequence number is 0, it indicates an invalid sequence number and is replaced by "???". + * + */ + const std::string serialNumberString = 0 == serialNumber ? "???" : std::to_string(serialNumber); + int hour = tm_now.tm_hour; + int minute = tm_now.tm_min; + int second = tm_now.tm_sec; + std::ostringstream pathStream; + pathStream << savePaht << "xak47-" << serialNumberString << "-" << std::setw(2) << std::setfill('0') << hour + << std::setw(2) << std::setfill('0') << minute << std::setw(2) << std::setfill('0') << second + << fileType; + return pathStream.str(); } \ No newline at end of file diff --git a/application/MissionManager/src/MediaTask.h b/application/MissionManager/src/MediaTask.h index ca6381a..989b8a8 100644 --- a/application/MissionManager/src/MediaTask.h +++ b/application/MissionManager/src/MediaTask.h @@ -15,13 +15,18 @@ #ifndef MEDIA_TASK_H #define MEDIA_TASK_H #include "DataProcessing.h" +#include "IIpcConfig.h" #include "IMediaManager.h" #include +#include +constexpr int FILE_SIZE_ERROR = -1; constexpr unsigned int MEDIA_TASK_TIMEOUT_MS = 1000 * 60; +constexpr int DEFAULT_VIDEO_DURATION_MS = 1000 * 10; typedef struct media_task_info { const std::vector mResponse; const unsigned long mSerialNumber; + const long long mCreateTime_s; } MediaTaskInfo; class VMediaTaskIniator { @@ -38,9 +43,22 @@ public: const std::string &savePath); virtual ~MediaTask() = default; virtual unsigned int GetTaskTimeOutMs(void); + const MediaTaskType GetTaskType(void) override; std::string GetTargetNameForSaving(void) override; + std::string GetThumbnailNameForSaving(const std::string &targetName) override; + int GetVideoDuration_ms(void) override; void Response(const std::vector &response) override; +public: + static std::string GetThumbnailNameByTargetName(const std::string &targetName); + static long long GetCreateTime_s(void); + static int GetFileSize_KB(const char *filePath); + static MediaTaskType WorkModeConvert(const WorkMode &mode); + static std::string GetFileNameBySeriaNumber(const unsigned long &serialNumber, const std::string &sourceFile, + const std::string &savePaht); + static std::string CreateFileName(const unsigned long &serialNumber, const std::string &savePaht, + const std::string &fileType); + private: const MediaTaskType mType; const InternalStateEvent mBindEvent; @@ -49,5 +67,8 @@ private: const std::string mSavePath; bool mFinished = false; std::shared_ptr mResponseData; + std::string mTargetName; + long long mCreateTime_s; + std::vector mTargetNameList; }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/MediaTaskHandle.cpp b/application/MissionManager/src/MediaTaskHandle.cpp index dd6da5f..32e83f3 100644 --- a/application/MissionManager/src/MediaTaskHandle.cpp +++ b/application/MissionManager/src/MediaTaskHandle.cpp @@ -15,6 +15,7 @@ #include "MediaTaskHandle.h" #include "DataProcessing.h" #include "IFilesManager.h" +#include "IIpcConfig.h" #include "ILog.h" #include "IMediaManager.h" #include "IMissionManager.h" @@ -40,8 +41,11 @@ void MediaTaskHandle::MakeSingleTask(const InternalStateEvent &bindEvent, const std::shared_ptr &iniator) { InfoToBeSaved info = IFilesManager::GetInstance()->GetInfoForSavingFiles(1); + WorkMode workMode = IIpcConfig::GetInstance()->GetWorkMode(); + MediaTaskType taskType = MediaTask::WorkModeConvert(workMode); + LogInfo("taskType: %s\n", IMediaManager::PrintfTaskType(taskType)); std::shared_ptr task = - std::make_shared(MediaTaskType::END, bindEvent, iniator, info.mSerialNumber, info.mSavingPath); + std::make_shared(taskType, bindEvent, iniator, info.mSerialNumber, info.mSavingPath); if (!mMediaHandle) { LogError("MediaHandle is null"); return; diff --git a/application/MissionManager/src/MediaTaskHandle.h b/application/MissionManager/src/MediaTaskHandle.h index 4017049..ebb5a59 100644 --- a/application/MissionManager/src/MediaTaskHandle.h +++ b/application/MissionManager/src/MediaTaskHandle.h @@ -15,6 +15,7 @@ #ifndef MEDIA_TASK_HANDLE_H #define MEDIA_TASK_HANDLE_H #include "DataProcessing.h" +#include "IIpcConfig.h" #include "IMediaManager.h" #include "MediaTask.h" class MediaTaskHandle diff --git a/application/MissionManager/src/MissionManagerMakePtr.cpp b/application/MissionManager/src/MissionManagerMakePtr.cpp index 13a53e0..dff2fa5 100644 --- a/application/MissionManager/src/MissionManagerMakePtr.cpp +++ b/application/MissionManager/src/MissionManagerMakePtr.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ #include "MissionManagerMakePtr.h" +#include "FormattingState.h" #include "ILog.h" #include "IMcuManager.h" #include "IMissionManager.h" @@ -120,4 +121,9 @@ std::shared_ptr MissionManagerMakePtr::CreateIdleState(void) { std::shared_ptr state = std::make_shared(); return state; +} +std::shared_ptr MissionManagerMakePtr::CreateFormattingState(void) +{ + std::shared_ptr state = std::make_shared(); + return state; } \ No newline at end of file diff --git a/application/MissionManager/src/MissionManagerMakePtr.h b/application/MissionManager/src/MissionManagerMakePtr.h index 6bc2ded..534a923 100644 --- a/application/MissionManager/src/MissionManagerMakePtr.h +++ b/application/MissionManager/src/MissionManagerMakePtr.h @@ -33,5 +33,6 @@ public: virtual std::shared_ptr CreateUpgradeState(void); virtual std::shared_ptr CreateMediaHandleState(void); virtual std::shared_ptr CreateIdleState(void); + virtual std::shared_ptr CreateFormattingState(void); }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/MissionState.cpp b/application/MissionManager/src/MissionState.cpp index cfe1e23..cf29a2c 100644 --- a/application/MissionManager/src/MissionState.cpp +++ b/application/MissionManager/src/MissionState.cpp @@ -42,6 +42,25 @@ bool MissionState::ExecuteStateMsg(VStateMachineData *msg) } bool MissionState::MediaReportHandle(VStateMachineData *msg) { + LogInfo("Get media file from chip.\n"); + // std::shared_ptr message = std::dynamic_pointer_cast(msg->GetMessageObj()); + // std::shared_ptr> data = + // std::dynamic_pointer_cast>(message->mMissionData); + // if (!data) { + // LogError("nullptr pointer.\n"); + // return NOT_EXECUTED; + // } + // auto fileSize = MediaTask::GetFileSize_KB(data->mData.mFileName.c_str()); + // auto createTime = MediaTask::GetCreateTime_s(); + // auto createType = MissionStateMachine::GetInstance()->GetFileCreateType(); + // SyncFileInfo file(UNDEFINE_SERIAL_NUMBER, + // data->mData.mFileName, + // fileSize, + // data->mData.mDuration_ms, + // createTime, + // createType, + // FileStatus::FINISHED_RECORD); + // mFastStartMediaFiles.push_back(file); MissionStateMachine::GetInstance()->DelayMessage(msg); MissionStateMachine::GetInstance()->SwitchState(SystemState::STORAGE_HANDLE_STATE); return EXECUTED; diff --git a/application/MissionManager/src/MissionState.h b/application/MissionManager/src/MissionState.h index 2f5fcca..a62ed47 100644 --- a/application/MissionManager/src/MissionState.h +++ b/application/MissionManager/src/MissionState.h @@ -15,6 +15,7 @@ #ifndef MISSION_STATE_H #define MISSION_STATE_H #include "DataProcessing.h" +#include "IFilesManager.h" #include "IStateMachine.h" #include "LedsHandle.h" class MissionState : public State, @@ -33,7 +34,17 @@ protected: bool SdCardEventReportHandle(VStateMachineData *msg); private: + /** + * @brief Processes media events actively reported by the bottom layer, and refers to snapshot or video recording + * during quick start. + * @param msg + * @return true + * @return false + */ bool MediaReportHandle(VStateMachineData *msg); bool CheckUpgradeFileHandle(VStateMachineData *msg); + +private: + std::vector mFastStartMediaFiles; }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/MissionStateMachine.cpp b/application/MissionManager/src/MissionStateMachine.cpp index e79ad38..092d1c2 100644 --- a/application/MissionManager/src/MissionStateMachine.cpp +++ b/application/MissionManager/src/MissionStateMachine.cpp @@ -14,6 +14,7 @@ */ #include "MissionStateMachine.h" #include "DataProcessing.h" +#include "IFilesManager.h" #include "ILog.h" #include "IMcuManager.h" #include "IMissionManager.h" @@ -82,6 +83,19 @@ void MissionStateMachine::SwitchState(const SystemState &state) { mStateMachine->SwitchState(mStateTree[state].get()); } +FileCreateType MissionStateMachine::GetFileCreateType(void) +{ + switch (mStartMission) { + case IpcMission::TEST: + return FileCreateType::MANUAL_TEST; + case IpcMission::PIR_TRIGGERED: + return FileCreateType::PIR; + + default: + LogError("unknow ipcmission.\n"); + return FileCreateType::END; + } +} IpcMission MissionStateMachine::GetStartMission(void) { class McuAskIpcMission : public McuAsk, public McuAskBase @@ -108,8 +122,10 @@ void MissionStateMachine::RunStateMachine(const IpcMission &mission) mStateTree[SystemState::UPGRADE_STATE] = MissionManagerMakePtr::GetInstance()->CreateUpgradeState(); mStateTree[SystemState::MEDIA_HANDLE_STATE] = MissionManagerMakePtr::GetInstance()->CreateMediaHandleState(); mStateTree[SystemState::IDLE_STATE] = MissionManagerMakePtr::GetInstance()->CreateIdleState(); + mStateTree[SystemState::FORMATTING_STATE] = MissionManagerMakePtr::GetInstance()->CreateFormattingState(); mStateMachine->StatePlus(mStateTree[SystemState::TOP_STATE].get(), nullptr); mStateMachine->StatePlus(mStateTree[SystemState::MISSION_STATE].get(), mStateTree[SystemState::TOP_STATE].get()); + mStateMachine->StatePlus(mStateTree[SystemState::FORMATTING_STATE].get(), mStateTree[SystemState::TOP_STATE].get()); mStateMachine->StatePlus(mStateTree[SystemState::STORAGE_HANDLE_STATE].get(), mStateTree[SystemState::MISSION_STATE].get()); mStateMachine->StatePlus(mStateTree[SystemState::SD_CARD_HANDLE_STATE].get(), @@ -121,13 +137,6 @@ void MissionStateMachine::RunStateMachine(const IpcMission &mission) mStateMachine->StatePlus(mStateTree[SystemState::IDLE_STATE].get(), mStateTree[SystemState::MISSION_STATE].get()); mStateMachine->SetCurrentState(mStateTree[SystemState::TOP_STATE].get()); mStateMachine->StartStateMachine(); - // /** - // * @brief The business can only be processed after the state machine is started. - // * - // */ - // std::shared_ptr message = - // std::make_shared(static_cast(InternalStateEvent::STORAGE_HANDLE_STATE_INIT)); - // SendStateMessage(message); InitAllState(); } void MissionStateMachine::InitAllState(void) diff --git a/application/MissionManager/src/MissionStateMachine.h b/application/MissionManager/src/MissionStateMachine.h index 12fd0b2..125d203 100644 --- a/application/MissionManager/src/MissionStateMachine.h +++ b/application/MissionManager/src/MissionStateMachine.h @@ -15,6 +15,7 @@ #ifndef MISSION_STATE_MACHINE_H #define MISSION_STATE_MACHINE_H // #include "IDeviceManager.h" +#include "IFilesManager.h" #include "IMcuManager.h" #include "IMissionManager.h" #include "IStateMachine.h" @@ -33,6 +34,7 @@ enum class SystemState UPGRADE_STATE, MEDIA_HANDLE_STATE, IDLE_STATE, + FORMATTING_STATE, END }; class MissionStateMachine @@ -47,6 +49,12 @@ public: StatusCode MessageExecutedLater(const std::shared_ptr &message, long long &delayTimeMs); void DelayMessage(VStateMachineData *msg); void SwitchState(const SystemState &state); + /** + * @brief Get the type of quick snapshot or video file based on the startup task parameters. + * + * @return FileCreateType + */ + FileCreateType GetFileCreateType(void); private: IpcMission GetStartMission(void); diff --git a/application/MissionManager/src/OnMissionState.cpp b/application/MissionManager/src/OnMissionState.cpp index 7e497b9..1f52925 100644 --- a/application/MissionManager/src/OnMissionState.cpp +++ b/application/MissionManager/src/OnMissionState.cpp @@ -19,8 +19,6 @@ #include "MissionStateMachine.h" OnMissionState::OnMissionState() : MissionState("OnMissionState") { - // mEventHandle[InternalStateEvent::ANY_STATE_SD_STATUS_PERORIED] = - // std::bind(&OnMissionState::SdCardEventReportSendToApp, this, _1); } void OnMissionState::GoInState() { diff --git a/application/MissionManager/src/PirTrggeredMissionState.cpp b/application/MissionManager/src/PirTrggeredMissionState.cpp index 7ab21f9..4d5190b 100644 --- a/application/MissionManager/src/PirTrggeredMissionState.cpp +++ b/application/MissionManager/src/PirTrggeredMissionState.cpp @@ -19,8 +19,6 @@ #include "PirTriggeredMissionState.h" PirTriggeredMissionState::PirTriggeredMissionState() : MissionState("PirTriggeredMissionState") { - // mEventHandle[InternalStateEvent::ANY_STATE_SD_STATUS_PERORIED] = - // std::bind(&PirTriggeredMissionState::SdCardEventReportSendToApp, this, _1); } void PirTriggeredMissionState::GoInState() { diff --git a/application/MissionManager/src/SdCardHandleState.cpp b/application/MissionManager/src/SdCardHandleState.cpp index 2ef6b08..c2a8485 100644 --- a/application/MissionManager/src/SdCardHandleState.cpp +++ b/application/MissionManager/src/SdCardHandleState.cpp @@ -20,10 +20,14 @@ #include "IMissionManager.h" #include "IStateMachine.h" #include "IStorageManager.h" +#include "LedControl.h" +#include "LedsHandle.h" +#include "MediaTask.h" #include "MissionStateMachine.h" #include "StatusCode.h" #include #include +#include using std::placeholders::_1; SdCardHandleState::SdCardHandleState() : State("SdCardHandleState"), mSdCardStatus(StorageEvent::END) { @@ -32,6 +36,8 @@ SdCardHandleState::SdCardHandleState() : State("SdCardHandleState"), mSdCardStat std::bind(&SdCardHandleState::SdCardEventHandle, this, _1); mEventHandle[InternalStateEvent::RESET_KEY_MEDIA_TASK] = std::bind(&SdCardHandleState::ResetKeyMediaTaskHandle, this, _1); + mEventHandle[InternalStateEvent::FORMAT_KEY_FORMAT_SD_CARD] = + std::bind(&SdCardHandleState::FormatKeyFormattingSDCardHandle, this, _1); } void SdCardHandleState::GoInState() { @@ -51,6 +57,7 @@ void SdCardHandleState::StateInit(void) void SdCardHandleState::StateUnInit(void) { IFilesManager::GetInstance()->UnInit(); + LedsHandle::DeleteAllLeds(); } bool SdCardHandleState::MediaReportHandle(VStateMachineData *msg) { @@ -62,20 +69,37 @@ bool SdCardHandleState::MediaReportHandle(VStateMachineData *msg) LogError("nullptr pointer.\n"); return NOT_EXECUTED; } + InfoToBeSaved info = IFilesManager::GetInstance()->GetInfoForSavingFiles(1); + auto targetFile = MediaTask::GetFileNameBySeriaNumber(info.mSerialNumber, data->mData.mFileName, info.mSavingPath); + auto fileSize = MediaTask::GetFileSize_KB(data->mData.mFileName.c_str()); + auto createTime = MediaTask::GetCreateTime_s(); + auto createType = MissionStateMachine::GetInstance()->GetFileCreateType(); + SyncFileInfo file(info.mSerialNumber, + // data->mData.mFileName, + targetFile, + fileSize, + data->mData.mDuration_ms, + createTime, + createType, + FileStatus::FINISHED_RECORD); + file.mSourceFile = data->mData.mFileName; if (StorageEvent::SD_CARD_INSERT != mSdCardStatus) { /** * @brief The SD card has not been mounted yet, cache the data for the quick start first. * */ LogWarning("Sd card is not inserted, cache data.\n"); - mFileNeedToSave = std::make_shared(data->mData.mFileName); + // mFileNeedToSave = std::make_shared(data->mData.mFileName); + mFastStartMediaFiles.push_back(file); return EXECUTED; } LogInfo("file = %s.\n", data->mData.mFileName.c_str()); - SaveFileInfo saveFileInfo(data->mData.mFileName); - StatusCode code = IFilesManager::GetInstance()->SaveFile(saveFileInfo); + std::vector files; + files.push_back(file); + // SaveFileInfo saveFileInfo(data->mData.mFileName); + StatusCode code = IFilesManager::GetInstance()->SaveFiles(files); if (IsCodeOK(code) == false) { - LogError("SaveFile failed.\n"); + LogError("SaveFiles failed.\n"); } return EXECUTED; } @@ -85,21 +109,41 @@ bool SdCardHandleState::SdCardEventHandle(VStateMachineData *msg) std::shared_ptr> data = std::dynamic_pointer_cast>(message->mMissionData); LogInfo(" SdCardEventHandle event:%s.\n", IStorageManager::GetInstance()->PrintStringStorageEvent(data->mData)); - if (mFileNeedToSave && StorageEvent::SD_CARD_INSERT == data->mData) { - LogInfo("Sd card is inserted for the first time.\n"); - StatusCode code = IFilesManager::GetInstance()->SaveFile(*(mFileNeedToSave.get())); - if (IsCodeOK(code) == false) { - LogError("SaveFile failed.\n"); - } - mFileNeedToSave.reset(); - } + // if (!mFastStartMediaFiles.empty() && StorageEvent::SD_CARD_INSERT == data->mData) { + // LogInfo("Sd card is inserted for the first time.\n"); + // // StatusCode code = IFilesManager::GetInstance()->SaveFile(*(mFileNeedToSave.get())); + // StatusCode code = IFilesManager::GetInstance()->SaveFiles(mFastStartMediaFiles); + // if (IsCodeOK(code) == false) { + // LogError("SaveFile failed.\n"); + // } + // // mFileNeedToSave.reset(); + // mFastStartMediaFiles.clear(); + // } mSdCardStatus = data->mData; + /** + * @brief The file management uses a database. When the SD card is inserted or removed, the card's file data needs + * to be reinitialized. + */ if (StorageEvent::SD_CARD_INSERT == mSdCardStatus) { IFilesManager::GetInstance()->Init(); std::shared_ptr message = std::make_shared(static_cast(InternalStateEvent::CHECK_UPGRADE_FILE)); MissionStateMachine::GetInstance()->SendStateMessage(message); + if (!mFastStartMediaFiles.empty()) { + LogInfo("Sd card is inserted for the first time.\n"); + // StatusCode code = IFilesManager::GetInstance()->SaveFile(*(mFileNeedToSave.get())); + StatusCode code = IFilesManager::GetInstance()->SaveFiles(mFastStartMediaFiles); + if (IsCodeOK(code) == false) { + LogError("SaveFile failed.\n"); + } + // mFileNeedToSave.reset(); + mFastStartMediaFiles.clear(); + } } + else { + IFilesManager::GetInstance()->UnInit(); + } + SetSdCardLedsStatus(mSdCardStatus); return EXECUTED; } bool SdCardHandleState::ResetKeyMediaTaskHandle(VStateMachineData *msg) @@ -109,4 +153,40 @@ bool SdCardHandleState::ResetKeyMediaTaskHandle(VStateMachineData *msg) } LogWarning("Sd card is not inserted, ignore reset key media task.\n"); return HANDLE_HELPESS; +} +/** + * @brief Why do we need to switch status and go through the formatting process here? + * This is because the guarantee business requires that the SD card be formatted safely under the condition that the SD + * card is not occupied. + * @param msg + * @return true + * @return false + */ +bool SdCardHandleState::FormatKeyFormattingSDCardHandle(VStateMachineData *msg) +{ + if (StorageEvent::SD_CARD_INSERT == mSdCardStatus || StorageEvent::SD_ABNORMAL == mSdCardStatus) { + LogInfo("FormatKeyFormattingSDCardHandle.\n"); + MissionStateMachine::GetInstance()->DelayMessage(msg); + MissionStateMachine::GetInstance()->SwitchState(SystemState::FORMATTING_STATE); + return EXECUTED; + } + LogWarning("Sd card is not inserted, ignore format key.\n"); + return EXECUTED; +} +void SdCardHandleState::SetSdCardLedsStatus(const StorageEvent &event) +{ + switch (event) { + case StorageEvent::SD_CARD_REMOVE: + LedsHandle::ControlDeviceStatusLed(DeviceStatus::SD_CARD_REMOVE, KEEP_ALIVE_FOREVER, BLINKING_SLOW_MS); + break; + case StorageEvent::SD_CARD_INSERT: + LedsHandle::DeleteDeviceStatusLed(); + break; + case StorageEvent::SD_ABNORMAL: + LedsHandle::ControlDeviceStatusLed(DeviceStatus::SD_CARD_ABNORMAL, KEEP_ALIVE_FOREVER, BLINKING_SUPER_FAST_MS); + break; + + default: + break; + } } \ No newline at end of file diff --git a/application/MissionManager/src/SdCardHandleState.h b/application/MissionManager/src/SdCardHandleState.h index 9b350b9..f8d11ad 100644 --- a/application/MissionManager/src/SdCardHandleState.h +++ b/application/MissionManager/src/SdCardHandleState.h @@ -19,9 +19,11 @@ #include "IMediaManager.h" #include "IStateMachine.h" #include "IStorageManager.h" +#include "LedsHandle.h" #include "VStateBase.h" class SdCardHandleState : public State, public DataProcessing, + public LedsHandle, public VStateBase, public std::enable_shared_from_this { @@ -38,9 +40,12 @@ protected: bool MediaReportHandle(VStateMachineData *msg); bool SdCardEventHandle(VStateMachineData *msg); bool ResetKeyMediaTaskHandle(VStateMachineData *msg); + bool FormatKeyFormattingSDCardHandle(VStateMachineData *msg); + void SetSdCardLedsStatus(const StorageEvent &event); private: StorageEvent mSdCardStatus; - std::shared_ptr mFileNeedToSave; + std::shared_ptr mFileNeedToSave; // TODO: delete. + std::vector mFastStartMediaFiles; }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/TestMissionState.cpp b/application/MissionManager/src/TestMissionState.cpp index ec17f71..0348b58 100644 --- a/application/MissionManager/src/TestMissionState.cpp +++ b/application/MissionManager/src/TestMissionState.cpp @@ -16,24 +16,35 @@ #include "DataProcessing.h" #include "IAppManager.h" #include "ILog.h" +#include "IMcuManager.h" #include "IMissionManager.h" #include "IStateMachine.h" #include "IStorageManager.h" +#include "LedControl.h" #include "LedsHandle.h" +#include "McuAskBase.h" #include "MissionState.h" #include "MissionStateMachine.h" #include #include -TestMissionState::TestMissionState() : MissionState("TestMissionState") +TestMissionState::TestMissionState() + : MissionState("TestMissionState"), mFormattingSDCard(SD_CARD_IS_NOT_FORMATTING), mResetKeyHoldTime(0), + mFormatKeyHoldTime(0) { mEventHandle[InternalStateEvent::ANY_STATE_SD_STATUS_PERORIED] = std::bind(&TestMissionState::SdCardEventReportSendToApp, this, _1); mEventHandle[InternalStateEvent::RESET_KEY_MEDIA_TASK] = std::bind(&TestMissionState::ResetKeyMediaTaskHandle, this, _1); mKeyClickHandle["reset"] = std::bind(&TestMissionState::ClickResetKey, this, _1); + mKeyHoldDownHandle["reset"] = std::bind(&TestMissionState::HoldDownResetKey, this, _1); + mKeyHoldDownHandle["format"] = std::bind(&TestMissionState::HoldDownFormatKey, this, _1); + mKeyHoldUpHandle["format"] = std::bind(&TestMissionState::HoldUpFormatKey, this, _1); } void TestMissionState::GoInState() { + mFormattingSDCard = SD_CARD_IS_NOT_FORMATTING; + mFormatKeyHoldTime = 0; + mResetKeyHoldTime = 0; MissionState::GoInState(); LogInfo(" ========== TestMissionState::GoInState.\n"); AppParam mAppParam(APP_MANAGER_DEVICE_IP, APP_MANAGER_HTTP_SERVER_PORT, APP_MANAGER_TCP_SERVER_PORT); // TODO: @@ -41,10 +52,11 @@ void TestMissionState::GoInState() std::shared_ptr monitor = std::dynamic_pointer_cast(MissionState::shared_from_this()); IAppManager::GetInstance()->SetAppMonitor(monitor); - ControlDeviceStatusLed(DeviceStatus::NORMAL); + LedsHandle::ControlDeviceStatusLed(DeviceStatus::NORMAL, KEEP_ALIVE_FOREVER, BLINKING_SLOW_MS); } void TestMissionState::GoOutState() { + IAppManager::GetInstance()->UnInit(); MissionState::GoOutState(); LogInfo(" ========== TestMissionState::GoOutState.\n"); } @@ -67,14 +79,86 @@ bool TestMissionState::ResetKeyMediaTaskHandle(VStateMachineData *msg) bool TestMissionState::ClickResetKey(const KeyEventData &data) { LogInfo("reset key click:make a media task.\n"); - // std::shared_ptr message = std::make_shared>( - // static_cast(InternalStateEvent::MEDIA_REPORT_EVENT), event); - // MissionStateMachine::GetInstance()->SendStateMessage(message); std::shared_ptr message = std::make_shared(static_cast(InternalStateEvent::RESET_KEY_MEDIA_TASK)); MissionStateMachine::GetInstance()->SendStateMessage(message); return EXECUTED; } +bool TestMissionState::HoldDownResetKey(const KeyEventData &data) +{ + constexpr int CLOSE_WATCH_DOG_PRESSING_TIME_MS = 1000 * 3; + if (mFormatKeyHoldTime > data.mHoldTime && data.mHoldTime >= CLOSE_WATCH_DOG_PRESSING_TIME_MS && + mResetKeyHoldTime == 0) { + LogWarning("Close watch dog.\n"); + constexpr int KEEP_BLINKING_FAST_MS = 1000 * 3; + LedsHandle::ControlDeviceStatusLed(DeviceStatus::CLOSE_WATCHDOG, KEEP_BLINKING_FAST_MS, BLINKING_FAST_MS); + CloseWatchDog(); + } + if (CLOSE_WATCH_DOG_PRESSING_TIME_MS > data.mHoldTime) { + return NOT_EXECUTED; + } + mFormattingSDCard = DO_NOT_FORMAT_SDCARD_ANYMORE; + mResetKeyHoldTime = data.mHoldTime; + return EXECUTED; +} +bool TestMissionState::HoldUpResetKey(const KeyEventData &data) +{ + mFormattingSDCard = SD_CARD_IS_NOT_FORMATTING; + mResetKeyHoldTime = 0; + return EXECUTED; +} +bool TestMissionState::HoldDownFormatKey(const KeyEventData &data) +{ + constexpr int FORMAT_SD_CARD_PRESSING_TIME_MS = 1000 * 15; + constexpr int MEANS_REALLY_HOLD_DOWN = 1000; + if (FORMAT_SD_CARD_PRESSING_TIME_MS <= data.mHoldTime && SD_CARD_IS_NOT_FORMATTING == mFormattingSDCard) { + LogInfo("format key down.\n"); + mFormattingSDCard = DO_NOT_FORMAT_SDCARD_ANYMORE; + std::shared_ptr message = + std::make_shared(static_cast(InternalStateEvent::FORMAT_KEY_FORMAT_SD_CARD)); + MissionStateMachine::GetInstance()->SendStateMessage(message); + return EXECUTED; + } + if (MEANS_REALLY_HOLD_DOWN > data.mHoldTime) { + return NOT_EXECUTED; + } + if (0 == mFormatKeyHoldTime) { + constexpr int KEEP_MS = 1000 * 5; + LedsHandle::ControlDeviceStatusLed(DeviceStatus::FORMAT_KEY_HOLD_DOWN_TIPS, KEEP_MS, LED_NOT_BLINK); + } + mFormatKeyHoldTime = data.mHoldTime; + return NOT_EXECUTED; +} +bool TestMissionState::HoldUpFormatKey(const KeyEventData &data) +{ + // mFormattingSDCard = false; + mFormatKeyHoldTime = 0; + return EXECUTED; +} +void inline TestMissionState::CloseWatchDog(void) +{ + class McuAskCloseWatchDog : public McuAsk, public McuAskBase + { + public: + McuAskCloseWatchDog() : McuAskBase(McuAskBlock::NOT_BLOCK, McuAskReply::NEED_REPLY) + { + } // using McuAskBlock::NOT_BLOCK + virtual ~McuAskCloseWatchDog() = default; + void ReplyFinished(const bool result) override + { + McuAskBase::ReplyFinished(result); + if (result) { + LogInfo("Ask data succeed, mDataReply = %d.\n", static_cast(mDataReply)); + // Do something here. + } + else { + LogError("Ask data falied.\n"); + } + } + }; + std::shared_ptr ask = std::make_shared(); + IMcuManager::GetInstance()->SetFeedingCycleForWatchDog(ask, CLOSE_WATCH_DOG); +} SdCardStatus TestMissionState::SdCardStatusConvert(const StorageEvent &event) { switch (event) { diff --git a/application/MissionManager/src/TestMissionState.h b/application/MissionManager/src/TestMissionState.h index 52aa089..9fb60c2 100644 --- a/application/MissionManager/src/TestMissionState.h +++ b/application/MissionManager/src/TestMissionState.h @@ -18,6 +18,8 @@ #include "IStateMachine.h" #include "IStorageManager.h" #include "MissionState.h" +constexpr bool DO_NOT_FORMAT_SDCARD_ANYMORE = true; +constexpr bool SD_CARD_IS_NOT_FORMATTING = false; class TestMissionState : public MissionState, public AppMonitor { public: @@ -30,8 +32,20 @@ private: bool SdCardEventReportSendToApp(VStateMachineData *msg); bool ResetKeyMediaTaskHandle(VStateMachineData *msg); bool ClickResetKey(const KeyEventData &data); + bool HoldDownResetKey(const KeyEventData &data); + bool HoldUpResetKey(const KeyEventData &data); + bool HoldDownFormatKey(const KeyEventData &data); + bool HoldUpFormatKey(const KeyEventData &data); + +private: + void CloseWatchDog(void); private: SdCardStatus SdCardStatusConvert(const StorageEvent &event); + +private: + bool mFormattingSDCard; + unsigned int mResetKeyHoldTime; + unsigned int mFormatKeyHoldTime; }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/TopState.cpp b/application/MissionManager/src/TopState.cpp index df07de1..86685af 100644 --- a/application/MissionManager/src/TopState.cpp +++ b/application/MissionManager/src/TopState.cpp @@ -51,7 +51,7 @@ bool TopState::ExecuteStateMsg(VStateMachineData *msg) } StatusCode TopState::ReportEvent(const MediaReportEvent &event) { - LogInfo(" ReportEvent:\n"); + LogInfo("Application recv media event(fast start).\n"); std::shared_ptr message = std::make_shared>( static_cast(InternalStateEvent::MEDIA_REPORT_EVENT), event); MissionStateMachine::GetInstance()->SendStateMessage(message); diff --git a/build/cmake/toolchain/linux.toolchain.cmake b/build/cmake/toolchain/linux.toolchain.cmake index 2f4b9a0..81f01b8 100755 --- a/build/cmake/toolchain/linux.toolchain.cmake +++ b/build/cmake/toolchain/linux.toolchain.cmake @@ -48,7 +48,7 @@ set(LOG_SUPPORT "true") # ------------ build log end ------------ # # ------------ build GoAhead ------------ # -set(GOAHEAD_DOCUMENTS_PATH "web") +set(GOAHEAD_DOCUMENTS_PATH "./sdcard") # web服务器配置文件的根目录,可以访问文件,例如:mp4,jpg,html等 set(GOAHEAD_UPLOAD_TMP_PATH "./goahead") set(GOAHEAD_UPLOAD_PATH "${GOAHEAD_UPLOAD_TMP_PATH}") set(GOAHEAD_LIMIT_POST "335544320") # If not defined means using default setting. See goahead-linux-static-fancy.mk diff --git a/build/global_config.cmake b/build/global_config.cmake index 232f384..c5d3b4f 100755 --- a/build/global_config.cmake +++ b/build/global_config.cmake @@ -14,6 +14,9 @@ set(UTILS_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/utils") set(HAL_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/hal") set(TEST_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/test") set(EXTERNAL_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/external") +# TODO: 这里设置CMAKE_INSTALL_PREFIX不是非常合理, +# 由于CMAKE_INSTALL_PREFIX被莫名修改成其它值,在这里设置作为临时解决方案 +set(CMAKE_INSTALL_PREFIX "${PLATFORM_PATH}/${PROJECT_OUTPUT_FOLDER}") # -------------------------- clang-tidy tools -------------------------- # set(CLANG_TIDY_CHECKS "-*,\ diff --git a/doc/gdb_coredump_guide.md b/doc/gdb_coredump_guide.md index b7de2ad..04175f5 100644 --- a/doc/gdb_coredump_guide.md +++ b/doc/gdb_coredump_guide.md @@ -1,10 +1,10 @@ # 1. gdb coredump分析 -  本文介绍ubuntu系统环境下coredump文件的分析方法。 +  本文介绍ubuntu系统环境下coredump文件的分析方法。**重要:此文的场景在Ubuntu每次开机后需要重复设置coredump调试环境,例如开启coredump和设置coredump文件生成路径。** **一个coredump示例:** -``` +```code $ ../output_files/test/bin/McuManagerTest --gtest_filter=McuManagerTest.RH_INTEGRATION_McuManager_AUTO_OtherSideSendIpcMission Note: Google Test filter = McuManagerTest.RH_INTEGRATION_McuManager_AUTO_OtherSideSendIpcMission [==========] Running 1 test from 1 test suite. @@ -33,29 +33,31 @@ Aborted (core dumped) ## 1.1. coredump文件生成路径查询 -``` +```code $ cat /proc/sys/kernel/core_pattern |/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E ``` 修改coredump文件生成路径: -``` -$ sudo sh -c 'echo /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/cmake-shell/core.%e.%p > /proc/sys/kernel/core_pattern' +```code +$ sudo sh -c 'echo /home/xiaojiazhu/project/ipc-sdk/ipc/cmake-shell/core.%e.%p > /proc/sys/kernel/core_pattern' $ cat /proc/sys/kernel/core_pattern /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/cmake-shell/core.%e.%p ``` + 命令: sudo sh -c 'echo /path/core.%e.%p > /proc/sys/kernel/core_pattern' -例如: -``` +例如(根据自己的实际情况写路径参数): + +```code sudo sh -c 'echo /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/cmake-shell/core.%e.%p > /proc/sys/kernel/core_pattern' ``` **发现路径下并未生成coredump文件** -``` +```code $ ulimit -a core file size (blocks, -c) 0 // 0表示终端未开启core dump data seg size (kbytes, -d) unlimited @@ -76,12 +78,11 @@ file locks (-x) unlimited ``` 解决: -``` + +```code $ ulimit -c unlimited ``` -sudo echo /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/cmake-shell/core.%e.%p > /proc/sys/kernel/core_pattern - ## 1.2. gdb查看堆栈信息 **Ubuntu系统需要加sudo执行程序才会生成coredump文件。** @@ -90,7 +91,7 @@ sudo echo /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/cmake-shell/   在gdb中无法识别coredump文件,如下所示: -``` +```code $ sudo gdb ../output_files/test/bin/McuManagerTest core.smbd.3390383 [sudo] password for xiaojiazhu: Sorry, try again. @@ -123,11 +124,11 @@ Program terminated with signal SIGABRT, Aborted. #2 0x0000000000000000 in ?? () ``` -由于gdb和asan同时启用会冲突,导致无法识别coredump文件。解决办法如下: +**由于gdb和asan同时启用会冲突,导致无法识别coredump文件。解决办法如下:** 修改://build/sdk_config.cmake -``` +```code # Gdb debug if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) message("---------------------------Debug mode.-----------------------") @@ -149,7 +150,7 @@ endif() 解决后: -``` +```code $ sudo gdb ../output_files/test/bin/McuManagerTest core.McuManagerTest.3406751 GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2 Copyright (C) 2020 Free Software Foundation, Inc. @@ -206,4 +207,4 @@ Program terminated with signal SIGABRT, Aborted. #17 main (argc=, argv=) at /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/test/middleware/McuManager/mainTest.cpp:22 (gdb) -``` \ No newline at end of file +``` diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index b2a6e12..b40fa9e 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -26,4 +26,5 @@ add_subdirectory(libhv/libhv-1.3.2) add_subdirectory(ffmpeg) add_subdirectory(fdk-aac) # ================= ffmpeg related end ================= # -add_subdirectory(libconfig) \ No newline at end of file +add_subdirectory(libconfig) +# add_subdirectory(libfaac) \ No newline at end of file diff --git a/external/ffmpeg/CMakeLists.txt b/external/ffmpeg/CMakeLists.txt index 0113194..59b8cd0 100644 --- a/external/ffmpeg/CMakeLists.txt +++ b/external/ffmpeg/CMakeLists.txt @@ -15,11 +15,11 @@ set(FFMPEG_COMMON_CONFIG "--enable-cross-compile --target-os=linux \ --disable-mipsdsp --disable-mipsdspr2 \ --disable-encoders \ --disable-muxers --enable-muxer=mov --enable-muxer=mp4 --enable-encoder=mpeg4 \ ---enable-decoder=aac --enable-encoder=aac --enable-decoder=pcm_alaw --enable-encoder=pcm_alaw \ +--enable-decoder=aac --enable-encoder=aac --enable-encoder=libfdk_aac --enable-decoder=pcm_alaw --enable-encoder=pcm_alaw \ --enable-demuxer=mov \ --disable-protocols --enable-protocol=file \ --disable-bsfs --enable-bsf=aac_adtstoasc --enable-bsf=h264_mp4toannexb --enable-bsf=hevc_mp4toannexb \ ---disable-indevs --disable-outdevs \ +--disable-indevs --disable-outdevs --enable-muxer=image2 --enable-encoder=mjpeg \ --extra-libs=-lm \ --extra-cflags=\"-I${EXTERNAL_LIBS_OUTPUT_PATH}/fdk-aac/include\" \ --extra-ldflags=\"-L${EXTERNAL_LIBS_OUTPUT_PATH}/fdk-aac/lib\"") @@ -51,6 +51,7 @@ add_custom_target( add_custom_target( remove_ffmpeg_source_files COMMAND rm -rf ffmpeg-6.1.1 + COMMAND rm -rf ${FFMPEG_INSTALL_PATH} WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/ffmpeg/ ) diff --git a/external/ffmpeg/README.md b/external/ffmpeg/README.md index fb01809..66e5980 100644 --- a/external/ffmpeg/README.md +++ b/external/ffmpeg/README.md @@ -21,6 +21,66 @@ $ ffplay video.h264 $ fmpeg -f mulaw -ar 8000 -i audio.g711a audio.wav ``` +* 播放pcm文件 + +```code +$ ffplay -f s16le -ar 8000 -ac 1 test.pcm +``` + +* 生成jpeg + +```code +$ ./ffmpeg -i test.h264 -vframes 1 -vf "scale=640:480:force_original_aspect_ratio=decrease" -f image2 output.jpeg +``` + +```code +void FfmpegThumbnail::GetDecodeDataCallback(AVFrame *frame) +{ + LogInfo("GetDecodeDataCallback frame->width = %d, frame->height = %d\n", frame->width, frame->height); + + // Allocate output frame for YUV420P format + AVFrame *output_frame = av_frame_alloc(); + output_frame->format = AV_PIX_FMT_YUV420P; + output_frame->width = 640; + output_frame->height = 480; + + // Calculate buffer size for YUV420P + int yuv_buf_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 640, 480, 1); + uint8_t *yuv_buf = (uint8_t *)av_malloc(yuv_buf_size); + + // Fill output frame with YUV420P buffer + av_image_fill_arrays(output_frame->data, output_frame->linesize, yuv_buf, AV_PIX_FMT_YUV420P, 640, 480, 1); + + // Create SwsContext for pixel format conversion from YUV420P (1920x2160) to YUV420P (640x480) + SwsContext *sws_ctx = sws_getContext(frame->width, frame->height, static_cast(frame->format), + output_frame->width, output_frame->height, AV_PIX_FMT_YUV420P, + SWS_BILINEAR, nullptr, nullptr, nullptr); + if (!sws_ctx) { + LogError("Failed to create SwsContext for pixel format conversion\n"); + av_frame_free(&output_frame); + av_free(yuv_buf); + return; + } + + // Perform pixel format conversion and scaling + sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, + output_frame->data, output_frame->linesize); + + // Clean up SwsContext + sws_freeContext(sws_ctx); + + // Encode the YUV420P frame to JPEG using mEncoder + if (mEncoder) { + mEncoder->EncodeData(output_frame, mStream, mEncodeCallback); + } + + // Free allocated resources + av_frame_free(&output_frame); + av_free(yuv_buf); +} + +``` + * 将h264和wav文件合成mp4文件 **注意:未发现可以将h264和g711a文件合成mp4文件** diff --git a/external/libfaac/CMakeLists.txt b/external/libfaac/CMakeLists.txt new file mode 100755 index 0000000..76de15b --- /dev/null +++ b/external/libfaac/CMakeLists.txt @@ -0,0 +1,12 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${EXTERNAL_LIBS_OUTPUT_PATH}) +INCLUDE_DIRECTORIES(libfaac) +INCLUDE_DIRECTORIES(./) + +AUX_SOURCE_DIRECTORY(. MODULE_SRC) +AUX_SOURCE_DIRECTORY(libfaac MODULE_SRC) + +ADD_LIBRARY(faac STATIC ${MODULE_SRC}) + + diff --git a/external/libfaac/G711AToPcm.cpp b/external/libfaac/G711AToPcm.cpp new file mode 100644 index 0000000..92815ab --- /dev/null +++ b/external/libfaac/G711AToPcm.cpp @@ -0,0 +1,31 @@ + +#include "G711AToPcm.h" + +#include "g711.h" + + +G711AToPcm::G711AToPcm(void) +{ +} + + +G711AToPcm::~G711AToPcm(void) +{ +} +unsigned short G711AToPcm::DecodeOneChar(unsigned char data) +{ + return (int16_t)alaw2linear(data); +} +//------------------------------------------- +G711UToPcm::G711UToPcm(void) +{ +} + + +G711UToPcm::~G711UToPcm(void) +{ +} +unsigned short G711UToPcm::DecodeOneChar(unsigned char data) +{ + return (int16_t)ulaw2linear(data); +} diff --git a/external/libfaac/G711AToPcm.h b/external/libfaac/G711AToPcm.h new file mode 100644 index 0000000..d975667 --- /dev/null +++ b/external/libfaac/G711AToPcm.h @@ -0,0 +1,24 @@ + +#pragma once +#include "IDecodeToPcm.h" + +class G711AToPcm : + public DecodeToPcmBase +{ +public: + G711AToPcm(void); + virtual ~G711AToPcm(void); +public: + virtual unsigned short DecodeOneChar(unsigned char data); +}; + + +class G711UToPcm : + public DecodeToPcmBase +{ +public: + G711UToPcm(void); + virtual ~G711UToPcm(void); +public: + virtual unsigned short DecodeOneChar(unsigned char data); +}; diff --git a/external/libfaac/IDecodeToPcm.cpp b/external/libfaac/IDecodeToPcm.cpp new file mode 100644 index 0000000..2dbeedc --- /dev/null +++ b/external/libfaac/IDecodeToPcm.cpp @@ -0,0 +1,90 @@ + +#include "IDecodeToPcm.h" +#include "audio_buffer.h" +#include +#include +#include +#include + + +IDecodeToPcm::IDecodeToPcm(void) +{ +} + + +IDecodeToPcm::~IDecodeToPcm(void) +{ + +} +//------------------------------------------------------------------------------------------------------------------------ +InAudioInfo::InAudioInfo() +{ + InitParam& initParam = m_initparam; + initParam.u32AudioSamplerate=8000; + initParam.ucAudioChannel=1; + initParam.u32PCMBitSize=16; + initParam.ucAudioCodec = Law_ALaw; +} +InAudioInfo::InAudioInfo(InitParam param):m_initparam(param) +{ + +} +//------------------------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------- +DecodeToPcmBase::DecodeToPcmBase(void) +{ + +} + + +DecodeToPcmBase::~DecodeToPcmBase(void) +{ + +} + +int DecodeToPcmBase::Init(InAudioInfo info) +{ + m_g7FrameSize = G711_ONE_LEN; + return 0; +} +int DecodeToPcmBase::PCMSize() +{ + return CON_PCM_SIZE; +} +int DecodeToPcmBase::G7FrameSize() +{ + return m_g7FrameSize; +} +int DecodeToPcmBase::Decode(unsigned char* pout_buf, unsigned int* pout_len , unsigned char* pin_buf, unsigned int in_len) +{ + int16_t *dst = (int16_t *) pout_buf; + uint8_t *src = (uint8_t *) pin_buf; + uint32_t i = 0; + int Ret = 0; + + if ((NULL == pout_buf) || \ + (NULL == pout_len) || \ + (NULL == pin_buf) || \ + (0 == in_len)) + { + return -1; + } + + if (*pout_len < 2 * in_len) + { + return -2; + } + //---{{{ + + for (i = 0; i < in_len; i++) + { + *(dst++) = (int16_t)DecodeOneChar(*(src++)); + } + + //---}}} + *pout_len = 2 * in_len; + + Ret = 2 * in_len; + return Ret; + return 0; +} diff --git a/external/libfaac/IDecodeToPcm.h b/external/libfaac/IDecodeToPcm.h new file mode 100644 index 0000000..a10462c --- /dev/null +++ b/external/libfaac/IDecodeToPcm.h @@ -0,0 +1,100 @@ + +#pragma once + +#ifndef _IDECODETOPCM_H +#define _IDECODETOPCM_H + +#include "MtAACEncoderAPI.h" + +#define USE_SHOUT_G711PACK 1 + +#if USE_SHOUT_G711PACK +#define G711_ONE_LEN 160 +#define G711_ONE_OFFSET 0 + +#else +#define G711_ONE_LEN 164 +#define G711_ONE_OFFSET 4 +#endif + +const int CON_PCM_SIZE = 320; + +class audio_buffer; + +class InAudioInfo +{ +public: + InAudioInfo(); + InAudioInfo(InitParam param); + ~InAudioInfo(){;} + + unsigned int CodecType() + { + return m_initparam.ucAudioCodec; + //return m_u32AudioCodec; + } + unsigned int Samplerate() + { + return m_initparam.u32AudioSamplerate; + //return m_u32AudioSamplerate; + } + unsigned int Channel() + { + return m_initparam.ucAudioChannel; + //return m_u32AudioChannel; + } + unsigned int PCMBitSize() + { + return m_initparam.u32PCMBitSize; + //return m_nPCMBitSize; + } + unsigned char G726RateBits() + { + return m_initparam.g726param.ucRateBits; + } +private: + unsigned int m_u32AudioCodec; + unsigned int m_u32AudioSamplerate; + unsigned int m_u32AudioChannel; + + unsigned int m_nPCMBitSize; + + InitParam m_initparam; +}; +//---------------------------------------------------------- +class IDecodeToPcm +{ +public: + IDecodeToPcm(void); + virtual ~IDecodeToPcm(void); + +public: + virtual int Init(InAudioInfo info)=0; + virtual int Decode( unsigned char* outbuf, unsigned int* outlen , unsigned char* inbuf, unsigned int inlen)=0; + virtual int PCMSize()=0; + virtual int G7FrameSize()=0; +}; +//---------------------------------------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------------------------------------------- +class DecodeToPcmBase:public IDecodeToPcm +{ +public: + DecodeToPcmBase(); + virtual ~DecodeToPcmBase(); + + int Init(InAudioInfo info); + +public: + virtual int Decode(unsigned char* outbuf, unsigned int* outlen , unsigned char* inbuf, unsigned int inlen); + virtual int PCMSize(); + virtual int G7FrameSize(); + + virtual unsigned short DecodeOneChar(unsigned char data)=0; + +private: + int m_g7FrameSize; +}; + +#endif + diff --git a/external/libfaac/MtAACEncoder.cpp b/external/libfaac/MtAACEncoder.cpp new file mode 100644 index 0000000..30e9bb7 --- /dev/null +++ b/external/libfaac/MtAACEncoder.cpp @@ -0,0 +1,190 @@ + +#include +#include +#include +#include + +#include "MtAACEncoder.h" +#include "MtAACEncoderAPI.h" +#include "outDebug.h" +#include "G711AToPcm.h" +#include "condef.h" + +G7ToAac::G7ToAac() +{ + m_pbPCMBuffer = NULL; + m_pbAACBuffer = NULL; + m_pbG7FrameBuffer = NULL; + m_pbPCMTmpBuffer = NULL; + m_audio_buffer_ = NULL; + m_pDecodeToPcm = NULL; + m_pPCMToAAC = NULL; +} + +G7ToAac::~G7ToAac() +{ + /*free the source of malloc*/ + SAFE_FREE_BUF(m_pbPCMBuffer); + SAFE_FREE_BUF(m_pbAACBuffer); + SAFE_FREE_BUF(m_pbG7FrameBuffer); + SAFE_FREE_BUF(m_pbPCMTmpBuffer); + + SAFE_DELETE_OBJ(m_audio_buffer_); + SAFE_DELETE_OBJ(m_pDecodeToPcm); + SAFE_DELETE_OBJ(m_pPCMToAAC); +} +bool G7ToAac::init() +{ + nRet = 0; + nTmp = 0; + nCount = 0; + nStatus = 0; + nPCMRead = 0; + + CreateBuffer(); + return true; +} + +bool G7ToAac::init(InAudioInfo info) +{ + m_inAudioInfo = info; + + bool ret=false; + ret = CreateDecodePcm(); + + ret = CreateEncodeAac(); + if (!ret) + { + return false; + } + return init(); +} +bool G7ToAac::CreateDecodePcm() +{ + if ( Law_ALaw == m_inAudioInfo.CodecType()) + { + m_pDecodeToPcm = new G711AToPcm(); + }else if ( Law_ULaw == m_inAudioInfo.CodecType() ) + { + m_pDecodeToPcm = new G711UToPcm(); + } + else + { + m_pDecodeToPcm = new G711AToPcm(); + } + m_pDecodeToPcm->Init(m_inAudioInfo); + + return true; +} +bool G7ToAac::CreateEncodeAac() +{ + m_pPCMToAAC = new PcmToAac(); + bool ret = m_pPCMToAAC->Init(&m_inAudioInfo); + + return ret; +} +bool G7ToAac::CreateBuffer() +{ + m_nPCMBufferSize = m_pPCMToAAC->GetPCMBufferSize(); + m_pbPCMBuffer = (unsigned char*) malloc(m_nPCMBufferSize * sizeof (unsigned char)); + memset(m_pbPCMBuffer, 0, m_nPCMBufferSize); + + m_nMaxOutputBytes = m_pPCMToAAC->GetMaxOutputBytes(); + m_pbAACBuffer = (unsigned char*) malloc(m_nMaxOutputBytes * sizeof (unsigned char)); + memset(m_pbAACBuffer, 0, m_nMaxOutputBytes); + + m_nG7FrameBufferSize = m_pDecodeToPcm->G7FrameSize(); + m_pbG7FrameBuffer = (unsigned char *) malloc(m_nG7FrameBufferSize * sizeof (unsigned char)); + memset(m_pbG7FrameBuffer, 0, m_nG7FrameBufferSize); + + m_nPCMSize = m_pDecodeToPcm->PCMSize(); + m_pbPCMTmpBuffer = (unsigned char *) malloc(m_nPCMSize * sizeof (unsigned char)); + memset(m_pbPCMTmpBuffer, 0, m_nPCMSize); + + m_audio_buffer_ = new audio_buffer(); + + return true; +} +int G7ToAac::aac_encode(unsigned char* inbuf, unsigned int inlen, unsigned char* outbuf, unsigned int* outlen) +{ + int encodeLen = 0; + + if (NULL != m_pDecodeToPcm) + { + encodeLen = aac_encode_obj(inbuf , inlen , outbuf , outlen); + } + + return encodeLen; +} + +int G7ToAac::aac_encode_obj(unsigned char* inbuf, unsigned int inlen, unsigned char* outbuf, unsigned int* outlen ) +{ + m_audio_buffer_->write_data(inbuf, inlen); + int buffer_len = 0; + *outlen = 0; + int nPCMSize = 0; + //while ((buffer_len = audio_buffer_->get_data(pbG711ABuffer, /*164*/G711_ONE_LEN)) > 0) + while ((buffer_len = m_audio_buffer_->get_data(m_pbG7FrameBuffer, m_nG7FrameBufferSize)) > 0) + { + if (buffer_len <= 0) + { + if(AAC_DEBUG) printf("%s:[%d] G711A -> PCM no G711 data !\n", __FUNCTION__, __LINE__); + //Sleep(100); + return -1; + } + + nStatus = 0; + memset(m_pbPCMTmpBuffer, 0, m_nPCMSize); + nPCMSize = m_nPCMSize; + //if ((nPCMRead = m_pDecodeToPcm->Decode(pbPCMTmpBuffer, (unsigned int*)&PCMSize, pbG711ABuffer+/*4*/G711_ONE_OFFSET, buffer_len-/*4*/G711_ONE_OFFSET )) < 0) // TODO: skip 4 byte? + if ((nPCMRead = m_pDecodeToPcm->Decode(m_pbPCMTmpBuffer, (unsigned int*)&nPCMSize, m_pbG7FrameBuffer, buffer_len )) < 0) // TODO: skip 4 byte? + { + if(AAC_DEBUG) printf("%s:[%d] G711A -> PCM failed buffer_len = %d !\n", __FUNCTION__, __LINE__, buffer_len); + return -1; + } + //if(AAC_DEBUG) printf("nPCMRead = %d, PCMSize = %d\n", nPCMRead, PCMSize); + + if ((m_nPCMBufferSize - nCount) < nPCMRead) + { + //if(AAC_DEBUG) printf("nPCMBufferSize = %d, nCount = %d, nPCMRead = %d\n", nPCMBufferSize, nCount, nPCMRead); + nStatus = 1; + memset(m_pbAACBuffer, 0, m_nMaxOutputBytes); + memcpy(m_pbPCMBuffer + nCount, m_pbPCMTmpBuffer, (m_nPCMBufferSize - nCount)); + + nRet = m_pPCMToAAC->Encode( (int32_t*)m_pbPCMBuffer , m_nPCMBufferSize , m_pbAACBuffer, m_nMaxOutputBytes); + + + memcpy(outbuf + *outlen, m_pbAACBuffer, nRet); + *outlen += nRet; + + nTmp = nPCMRead - (m_nPCMBufferSize - nCount); + memset(m_pbPCMBuffer, 0, m_nPCMBufferSize); + memcpy(m_pbPCMBuffer, m_pbPCMTmpBuffer + (m_nPCMBufferSize - nCount), nTmp); + if(AAC_DEBUG) printf("%s:[%d] G711A -> PCM (nPCMBufferSize - nCount) < nPCMRead\n", __FUNCTION__, __LINE__); + nCount = 0; + nCount += nTmp; + } + + if (nStatus == 0) + { + if(AAC_DEBUG) printf("%s:[%d] G711A -> PCM nStatus = 0...\n", __FUNCTION__, __LINE__); + memcpy(m_pbPCMBuffer + nCount, m_pbPCMTmpBuffer, nPCMRead); + if(AAC_DEBUG) printf("%s:[%d] G711A -> PCM nStatus = 0\n", __FUNCTION__, __LINE__); + nCount += nPCMRead; + } + + if (nPCMRead < /*320*/CON_PCM_SIZE) + { + if(AAC_DEBUG) printf("nPCMRead = %d\n", nPCMRead); + + nRet = m_pPCMToAAC->Encode((int*) m_pbPCMBuffer, nCount , m_pbAACBuffer, m_nMaxOutputBytes); + + + memcpy(outbuf + *outlen, m_pbAACBuffer, nRet); + *outlen += nRet; + + INFO_D(AAC_DEBUG , "G711A -> PCM nPCMRead < 320"); + } + } + return *outlen; +} diff --git a/external/libfaac/MtAACEncoder.h b/external/libfaac/MtAACEncoder.h new file mode 100644 index 0000000..23d53a2 --- /dev/null +++ b/external/libfaac/MtAACEncoder.h @@ -0,0 +1,55 @@ +#ifndef MtAACEncoder_H +#define MtAACEncoder_H + +#include "audio_buffer.h" +#include "IDecodeToPcm.h" +#include "PcmToAac.h" + +class G7ToAac +{ +public: + G7ToAac(); + virtual ~G7ToAac(); + + bool init(); + bool init(InAudioInfo info); + + int aac_encode(unsigned char* inbuf, unsigned int inlen, unsigned char* outbuf, unsigned int* outlen); + +private: + int aac_encode_obj(unsigned char* inbuf, unsigned int inlen, unsigned char* outbuf, unsigned int* outlen ); + + bool CreateDecodePcm(); + bool CreateEncodeAac(); + bool CreateBuffer(); +private: + int nRet; + int nTmp; + int nCount; + int nStatus; + int nPCMRead; + + + + int m_nPCMBufferSize; + unsigned char *m_pbPCMBuffer; + + unsigned long m_nMaxOutputBytes; + unsigned char *m_pbAACBuffer; + + int m_nPCMSize; + unsigned char *m_pbPCMTmpBuffer; + + unsigned char *m_pbG7FrameBuffer; + unsigned long m_nG7FrameBufferSize; + + audio_buffer *m_audio_buffer_; + //------ + InAudioInfo m_inAudioInfo; + + IDecodeToPcm* m_pDecodeToPcm; + PcmToAac* m_pPCMToAAC; +}; + +#endif /* MtAACEncoder_H */ + diff --git a/external/libfaac/MtAACEncoderAPI.cpp b/external/libfaac/MtAACEncoderAPI.cpp new file mode 100644 index 0000000..fe44dfe --- /dev/null +++ b/external/libfaac/MtAACEncoderAPI.cpp @@ -0,0 +1,34 @@ +#include "MtAACEncoderAPI.h" +#include "MtAACEncoder.h" +#include "condef.h" + +Mt_API MtAACEncoder_Handle Mt_APICALL Mt_AACEncoder_Init(InitParam initPar) +{ + G7ToAac *encoder = new G7ToAac(); + InAudioInfo info(initPar ); + bool ret = encoder->init(info); + if (!ret) + { + SAFE_DELETE_OBJ(encoder); + } + return encoder; +} + +Mt_API int Mt_APICALL Mt_AACEncoder_Encode(MtAACEncoder_Handle handle, unsigned char* inbuf, unsigned int inlen, unsigned char* outbuf, unsigned int* outlen) +{ + if(handle == NULL) + { + return -1; + } + return ((G7ToAac*)handle)->aac_encode(inbuf, inlen, outbuf, outlen); +} + +Mt_API void Mt_APICALL Mt_AACEncoder_Release(MtAACEncoder_Handle handle) +{ + if(handle != NULL) + { + delete ((G7ToAac*)handle); + } +} + + diff --git a/external/libfaac/MtAACEncoderAPI.h b/external/libfaac/MtAACEncoderAPI.h new file mode 100644 index 0000000..95c5060 --- /dev/null +++ b/external/libfaac/MtAACEncoderAPI.h @@ -0,0 +1,75 @@ + +#ifndef MtAACEncoder_API_H +#define MtAACEncoder_API_H + +#ifdef _WIN32 +#define Mt_API __declspec(dllexport) +#define Mt_APICALL __stdcall +#else +#define Mt_API +#define Mt_APICALL +#endif + +#define Mt_Handle void* +#define MtAACEncoder_Handle void* + +///* Audio Codec */ +enum Law +{ + Law_ULaw = 0, /**< U law */ + Law_ALaw = 1, /**< A law */ + Law_PCM16 = 2, /**< 16 bit uniform PCM values. ԭʼ pcm */ + Law_G726 = 3 /**< G726 */ +}; + +///* Rate Bits */ +enum Rate +{ + Rate16kBits=2, /**< 16k bits per second (2 bits per ADPCM sample) */ + Rate24kBits=3, /**< 24k bits per second (3 bits per ADPCM sample) */ + Rate32kBits=4, /**< 32k bits per second (4 bits per ADPCM sample) */ + Rate40kBits=5 /**< 40k bits per second (5 bits per ADPCM sample) */ +}; + +typedef struct _g711param +{ + ; +}G711Param; + +typedef struct _g726param +{ + unsigned char ucRateBits;//Rate16kBits Rate24kBits Rate32kBits Rate40kBits +}G726Param; + +typedef struct _initParam +{ + unsigned char ucAudioCodec; // Law_uLaw Law_ALaw Law_PCM16 Law_G726 + unsigned char ucAudioChannel; //1 + unsigned int u32AudioSamplerate; //8000 + unsigned int u32PCMBitSize; //16 + union + { + G711Param g711param; + G726Param g726param; + }; + +}InitParam; + +#ifdef __cplusplus +extern "C" +{ +#endif + /* AAC Encoder Ϊֵ */ + Mt_API MtAACEncoder_Handle Mt_APICALL Mt_AACEncoder_Init(InitParam initPar); + + /* ݣر */ + Mt_API int Mt_APICALL Mt_AACEncoder_Encode(MtAACEncoder_Handle handle, unsigned char* inbuf, unsigned int inlen, unsigned char* outbuf, unsigned int* outlen); + + /* ͷAAC Encoder */ + Mt_API void Mt_APICALL Mt_AACEncoder_Release(MtAACEncoder_Handle handle); + +#ifdef __cplusplus +} +#endif + +#endif /* MtAACEncoder_API_H */ diff --git a/external/libfaac/MtDSSBuffers.cpp b/external/libfaac/MtDSSBuffers.cpp new file mode 100644 index 0000000..a219106 --- /dev/null +++ b/external/libfaac/MtDSSBuffers.cpp @@ -0,0 +1,106 @@ +#include "MtDSSBuffers.h" + +int init_buffers(buffers_t * bufs, int bufsize, int bufnum) +{ + int i; + bufs->rear = 0; + bufs->front = 0; + if(bufnum > MAX_BUF_NUM) + { + bufs->bufnum = MAX_BUF_NUM; + } + else + { + bufs->bufnum = bufnum; + } + for(i = 0; i < bufs->bufnum; i++) + { + bufs->buf[i].length = 0; + bufs->buf[i].start = (char *) calloc(1, bufsize); + bufs->buf[i].frame_type = -1; + bufs->buf[i].channel = -1; + bufs->buf[i].frame_index = 0; + if(bufs->buf[i].start == NULL) + return -1; + } +#ifdef WIN32 + InitializeCriticalSection(&(bufs->cs)); +#else + InitializeCriticalSection(&(bufs->cs), NULL); +#endif + return 0; +} + +int free_buffers(buffers_t* bufs) +{ + for(int i = 0; i < bufs->bufnum; i++) + { + if(bufs->buf[i].start != NULL) + free(bufs->buf[i].start); + } + bufs->pOnVideoData = NULL; + DeleteCriticalSection(&(bufs->cs)); + return 0; +} + +int buffers_get_data(void *data, unsigned int *length, buffers_t * bufs, int *type, + int *channel, int *frame_index) +{ + int res = -1; + EnterCriticalSection(&(bufs->cs)); + if(bufs->front != bufs->rear) + { + res = *length = bufs->buf[bufs->front].length; + memcpy(data, bufs->buf[bufs->front].start, *length); + *type = bufs->buf[bufs->front].frame_type; + *channel = bufs->buf[bufs->front].channel; + *frame_index = bufs->buf[bufs->front].frame_index; + bufs->front = (bufs->front + 1) % bufs->bufnum; + //res = 1; + } + LeaveCriticalSection(&(bufs->cs)); + return res; +} + +int buffers_put_data(void *data, unsigned int length, buffers_t * bufs, int type, + int channel, int frame_index) +{ + int res = 0; + EnterCriticalSection(&(bufs->cs)); + + if(((bufs->rear + 1) % bufs->bufnum) == bufs->front) + { + res = -1; + } + else + { + bufs->buf[bufs->rear].length = length; + bufs->buf[bufs->rear].frame_type = type; + bufs->buf[bufs->rear].channel = channel; + bufs->buf[bufs->rear].frame_index = frame_index; + if(length < iBufLen) + { + memcpy(bufs->buf[bufs->rear].start, data, length); + } + else + { + //WriteSystemLog("DataRecv.log", "Frame is too large"); + printf("Frame is too large, length=%d\r\n", length); + } + bufs->rear = (bufs->rear + 1) % bufs->bufnum; + res = 1; + } + + LeaveCriticalSection(&(bufs->cs)); + return res; +} + +void buffers_clear_data(buffers_t * bufs) +{ + EnterCriticalSection(&(bufs->cs)); + bufs->rear = 0; + bufs->front = 0; + LeaveCriticalSection(&(bufs->cs)); +} + + diff --git a/external/libfaac/MtDSSBuffers.h b/external/libfaac/MtDSSBuffers.h new file mode 100644 index 0000000..e1cbcdd --- /dev/null +++ b/external/libfaac/MtDSSBuffers.h @@ -0,0 +1,170 @@ + +#ifndef _EasyDSSBuffers_H_ +#define _EasyDSSBuffers_H_ + +#define _CRT_SECURE_NO_WARNINGS + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif +#include +#include +#include +#include + +#include +#if (defined(_WIN32)) //windows +//#define WIN32 +#ifndef DLL_EXPORT +#define JD_API extern "C"__declspec(dllimport) +#else +#define JD_API extern "C"__declspec(dllexport) +#endif +#elif defined(__linux__) //linux +#define __stdcall +#define CALLBACK +#define JD_API extern "C" +#define WINAPI +typedef int HANDLE; +typedef int HWND; +#endif + +#ifdef _WIN32 +#include +#endif +#include +#include +#include + +using namespace std; + +#define PFRAME 0x00 +#define IFRAME 0x01 + +const int iBufLen = 1024 * 128; +const int iRecvBufLen = iBufLen * 2; +const int iMaxParamterNum = 128; +const int iBufNum = 10; +const int MAX_BUF_NUM = 50; +const int iCharBufLen = 64; +const char BoundTag[] = "\r\n"; //帧间隔标识 +const int BoundTagLen = sizeof (BoundTag) - 1; //帧间隔标识长度 +//const char IFrameTag[] = "HISI264I";//录像I帧标志 +//const char PFrameTag[] = "HISI264P";//录像P帧标志 +//const int FrameTagLen = sizeof(IFrameTag) - 1;// 录像帧标志长度 + +const int MaxWaitTime = 3000; //超时时间 +const int MaxCameraNum = 24; //最大摄像机数量 + +const int AUDIO_BUFFER_SIZE = 960; //定义播放的每一片缓冲都是800个字节 +const int MAX_AUDIO_BUF = 4; //播放缓冲的通知索引 +const int BUFFERNOTIFYSIZE = AUDIO_BUFFER_SIZE;/*8192*//*192000*/ //缓冲通知位置的大小,请参见DirectSound应用程序开发快速入门 +const int SAMPLE_RATE = 8000;/*44100*/ //pcm 的采样率 8k +const int N_CHANNEL = 1;/*2*/ //PCM 声道数 单通道 +const int BITS_PER_SAMPLE = 16; //每个采样点的比特数 +const int CHANNEL = 1; +const int SAMPLES_PER_SECOND = 8000; +const int SIZE_AUDIO_FRAME = 960; + +#ifdef WIN32 +const GUID GUID_YUY2 = {0xc68e1552, 0x4a3f, 0x4706, + {0xb2, 0xd4, 0x83, 0x41, 0x4f, 0x15, 0xdc, 0xcc}}; +//typedef char int8_t; +//typedef unsigned char uint8_t; +//typedef short int16_t; +//typedef unsigned short uint16_t; +//typedef int int32_t; +//typedef unsigned int uint32_t; +//typedef __int64 int64_t; +//typedef unsigned __int64 uint64_t; +#else +typedef pthread_mutex_t CRITICAL_SECTION; +#define InitializeCriticalSection pthread_mutex_init +#define DeleteCriticalSection pthread_mutex_destroy +#define EnterCriticalSection pthread_mutex_lock +#define LeaveCriticalSection pthread_mutex_unlock +//typedef void* LPVOID; +//typedef unsigned long DWORD; +//#define CloseHandle close +#define Sleep(x) usleep(x*1000) +//#define closesocket close +//#define TRUE true +//#define FALSE false +//typedef bool BOOL; + +typedef struct +{ + pthread_mutex_t mtx; + pthread_cond_t cond; + bool manual_reset; + bool signaled; +} THANDLE, *PHANDLE; + +#define INFINITE 0xFFFFFFFF +#define WAIT_TIMEOUT 0x00000102L +#define WAIT_OBJECT_0 0 +#endif + +#ifndef TRACE +#define TRACE printf +#endif + +/*! + \brief H264码流回调函数指针 + \param iPlayHandle 播放句柄 + \param sData 数据缓冲区 + \param iLen 数据长度 + \param iDataType 数据类型 0 - 实时数据流, 1 - 录像数据流, 2 - 本地采集的音频流, 3 - 设备发过来的音频流 +*/ +typedef void(CALLBACK* fVideoDataCallBack)(int iPlayHandle, char* sData, int iLen, int iDataType, void* pUserData); + +/*! + \brief 告警信息回调函数指针 + \param pAlarmInfo 告警信息T_JD_AlarmInfo结构体指针 + \param pUserData 用户数据 +*/ +typedef void(CALLBACK* fMessageCallBack)(void* pAlarmInfo, void* pUserData); + +typedef struct +{ + char *start; + size_t length; + int frame_type; + int frame_index; + int channel; +} buffer_t; + +typedef struct _buffers_t +{ + int rear; + int front; + int bufnum; + int fps; + CRITICAL_SECTION cs; + buffer_t buf[MAX_BUF_NUM]; + fVideoDataCallBack pOnVideoData; // 数据回调函数指针 + void* pUserData; //回调函数用户数据 + + _buffers_t() + { + pOnVideoData = NULL; + pUserData = NULL; + } + +} buffers_t; + +int init_buffers(buffers_t * bufs, int bufsize, int bufnum); + +int free_buffers(buffers_t *bufs); + +int buffers_get_data(void *data, unsigned int *length, buffers_t * bufs, int *type, int *channel, int *frame_index); + +int buffers_put_data(void *data, unsigned int length, buffers_t * bufs, int type, int channel, int frame_index); + +void buffers_clear_data(buffers_t * bufs); + +#endif diff --git a/external/libfaac/PcmToAac.cpp b/external/libfaac/PcmToAac.cpp new file mode 100644 index 0000000..a77d822 --- /dev/null +++ b/external/libfaac/PcmToAac.cpp @@ -0,0 +1,78 @@ + +#include "PcmToAac.h" + +#include "outDebug.h" + +PcmToAac::PcmToAac(void) +{ + m_nInputSamples=0; + m_nMaxOutputBytes=0; + m_nPCMBitSize = 16; +} + + +PcmToAac::~PcmToAac(void) +{ + if (NULL != hEncoder) + { + /*Close FAAC engine*/ + faacEncClose(hEncoder); + } + +} + +bool PcmToAac::Init(InAudioInfo* info) +{ + unsigned int objectType = LOW; + unsigned int mpegVersion = MPEG2; + static unsigned int useTns = 0; //#define DEFAULT_TNS 0 + + //TODO: config this + unsigned int nChannels = /*1*/info->Channel(); + + m_nPCMBitSize = /*16*/ info->PCMBitSize(); + unsigned long nInputSamples = 0; + unsigned long nSampleRate = /*8000*/info->Samplerate(); + unsigned long nMaxOutputBytes = 0; + + + /*open FAAC engine*/ + hEncoder = faacEncOpen(nSampleRate, nChannels, &nInputSamples, &nMaxOutputBytes); + if (hEncoder == NULL) + { + if(AAC_DEBUG) printf("%s:[%d] failed to call faacEncOpen !\n", __FUNCTION__, __LINE__); + //return -1; + return false; + } + m_nInputSamples = nInputSamples; + m_nMaxOutputBytes = nMaxOutputBytes; + + + /*get current encoding configuration*/ + pConfiguration = faacEncGetCurrentConfiguration(hEncoder); + pConfiguration->inputFormat = FAAC_INPUT_16BIT; + + /*0 - raw; 1 - ADTS*/ + pConfiguration->outputFormat = 1; + pConfiguration->useTns = useTns; + pConfiguration->aacObjectType = objectType; + pConfiguration->mpegVersion = mpegVersion; + + /*set encoding configuretion*/ + faacEncSetConfiguration(hEncoder, pConfiguration); + + return true; +} + +int PcmToAac::Encode(int32_t * pbPCMBuffer, unsigned int nPCMBufferSize, unsigned char * pbAACBuffer, unsigned int nMaxOutputBytes) +{ + unsigned int nPCMBitSize = GetPCMBitSize(); + + + unsigned int nInputSamples = (nPCMBufferSize / (nPCMBitSize / 8)); + if(AAC_DEBUG) printf("%s:[%d] G711A -> PCM faacEncEncode....\n", __FUNCTION__, __LINE__); + int nRet = faacEncEncode(hEncoder, (int*) pbPCMBuffer, nInputSamples, pbAACBuffer, nMaxOutputBytes); + if(AAC_DEBUG) printf("%s:[%d] G711A -> PCM faacEncEncode\n", __FUNCTION__, __LINE__); + + return nRet; +} diff --git a/external/libfaac/PcmToAac.h b/external/libfaac/PcmToAac.h new file mode 100644 index 0000000..9295cc0 --- /dev/null +++ b/external/libfaac/PcmToAac.h @@ -0,0 +1,51 @@ + +#pragma once + +#include +#include +#include +#include "IDecodeToPcm.h" + +extern "C" +{ +#include +} + +class PcmToAac +{ +public: + PcmToAac(void); + ~PcmToAac(void); +public: + bool Init(InAudioInfo* info); + int Encode(int32_t * inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize); +public: + unsigned int GetPCMBitSize() + { + return m_nPCMBitSize; + } + unsigned int GetInputSamples() + { + return m_nInputSamples; + } + unsigned int GetMaxOutputBytes() + { + return m_nMaxOutputBytes; + } + unsigned int GetPCMBufferSize() + { + return (m_nInputSamples * (m_nPCMBitSize / 8)); + } + + +private: + faacEncHandle hEncoder; + faacEncConfigurationPtr pConfiguration; + + unsigned int m_nPCMBitSize /*= 16*/; + + unsigned long m_nInputSamples /*= 0*/; + + unsigned long m_nMaxOutputBytes /*= 0*/; +}; + diff --git a/external/libfaac/audio_buffer.cpp b/external/libfaac/audio_buffer.cpp new file mode 100644 index 0000000..66be603 --- /dev/null +++ b/external/libfaac/audio_buffer.cpp @@ -0,0 +1,56 @@ +#include "audio_buffer.h" +#include "MtDSSBuffers.h" +#include "outDebug.h" + + +audio_buffer::audio_buffer() +{ + data_ = new unsigned char[iBufLen]; + len_ = 0; +} + +audio_buffer::~audio_buffer() +{ + delete []data_; +} + +int audio_buffer::write_data(void* data, int len) +{ + if (iBufLen - len_ < len) + { + if(AAC_DEBUG) printf("audio_buffer full\n"); + return -1; + } + if (len > 0) + { + memcpy(data_ + len_, data, len); + len_ += len; + return len; + } + return 0; +} + +int audio_buffer::get_data(unsigned char* dest, int how_you_want) +{ + if (len_ == 0 || len_ < how_you_want) + { + return 0; + } + else + { + memcpy(dest, data_, how_you_want); + memmove(data_, data_ + how_you_want, len_ - how_you_want); + len_ -= how_you_want; + return how_you_want; + } +} + +unsigned char* audio_buffer::get_writable_ptr() +{ + return data_ + len_; +} + +void audio_buffer::update_data_len(int len) +{ + len_ += len; +} diff --git a/external/libfaac/audio_buffer.h b/external/libfaac/audio_buffer.h new file mode 100644 index 0000000..2a6246b --- /dev/null +++ b/external/libfaac/audio_buffer.h @@ -0,0 +1,21 @@ +#pragma once + +class audio_buffer +{ +public: + audio_buffer(void); + ~audio_buffer(void); + + int write_data(void *data, int len); + + int get_data(unsigned char *dest, int how_you_want); + + void update_data_len(int len); + + unsigned char *get_writable_ptr(); + +private: + unsigned char *data_; + int len_; +}; + diff --git a/external/libfaac/condef.h b/external/libfaac/condef.h new file mode 100644 index 0000000..d2fd5ca --- /dev/null +++ b/external/libfaac/condef.h @@ -0,0 +1,11 @@ +#ifndef _CONDEF_H +#define _CONDEF_H +#include +#include +#include +#include + +#define SAFE_DELETE_OBJ(OBJ) {if(NULL!=OBJ){delete OBJ;OBJ=NULL;}} +#define SAFE_FREE_BUF(OBJ) {if(NULL!=OBJ){free(OBJ);OBJ=NULL;}} + +#endif \ No newline at end of file diff --git a/external/libfaac/faac.h b/external/libfaac/faac.h new file mode 100644 index 0000000..57b2674 --- /dev/null +++ b/external/libfaac/faac.h @@ -0,0 +1,99 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: faac.h,v 1.36 2009/01/25 18:50:32 menno Exp $ + */ + +#ifndef _FAAC_H_ +#define _FAAC_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if defined(_WIN32) && !defined(__MINGW32__) +# ifndef FAACAPI +# define FAACAPI __stdcall +# endif +#else +# ifndef FAACAPI +# define FAACAPI +# endif +#endif + +#pragma pack(push, 1) + +typedef struct { + void *ptr; + char *name; +} +psymodellist_t; + +#include "faaccfg.h" + + +typedef void *faacEncHandle; + +#ifndef HAVE_INT32_T +typedef signed int int32_t; +#endif + +/* + Allows an application to get FAAC version info. This is intended + purely for informative purposes. + + Returns FAAC_CFG_VERSION. +*/ +int FAACAPI faacEncGetVersion(char **faac_id_string, + char **faac_copyright_string); + + +faacEncConfigurationPtr FAACAPI + faacEncGetCurrentConfiguration(faacEncHandle hEncoder); + + +int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder, + faacEncConfigurationPtr config); + + +faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate, + unsigned int numChannels, + unsigned long *inputSamples, + unsigned long *maxOutputBytes); + + +int FAACAPI faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder, unsigned char **ppBuffer, + unsigned long *pSizeOfDecoderSpecificInfo); + + +int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput, + unsigned char *outputBuffer, + unsigned int bufferSize); + + +int FAACAPI faacEncClose(faacEncHandle hEncoder); + + + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _FAAC_H_ */ diff --git a/external/libfaac/faaccfg.h b/external/libfaac/faaccfg.h new file mode 100644 index 0000000..278f60d --- /dev/null +++ b/external/libfaac/faaccfg.h @@ -0,0 +1,122 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: faaccfg.h,v 1.3 2004/07/04 12:12:05 corrados Exp $ + */ + +#ifndef _FAACCFG_H_ +#define _FAACCFG_H_ + +#define FAAC_CFG_VERSION 104 + +/* MPEG ID's */ +#define MPEG2 1 +#define MPEG4 0 + +/* AAC object types */ +#define MAIN 1 +#define LOW 2 +#define SSR 3 +#define LTP 4 + +/* Input Formats */ +#define FAAC_INPUT_NULL 0 +#define FAAC_INPUT_16BIT 1 +#define FAAC_INPUT_24BIT 2 +#define FAAC_INPUT_32BIT 3 +#define FAAC_INPUT_FLOAT 4 + +#define SHORTCTL_NORMAL 0 +#define SHORTCTL_NOSHORT 1 +#define SHORTCTL_NOLONG 2 + +#pragma pack(push, 1) +typedef struct faacEncConfiguration +{ + /* config version */ + int version; + + /* library version */ + char *name; + + /* copyright string */ + char *copyright; + + /* MPEG version, 2 or 4 */ + unsigned int mpegVersion; + + /* AAC object type */ + unsigned int aacObjectType; + + /* Allow mid/side coding */ + unsigned int allowMidside; + + /* Use one of the channels as LFE channel */ + unsigned int useLfe; + + /* Use Temporal Noise Shaping */ + unsigned int useTns; + + /* bitrate / channel of AAC file */ + unsigned long bitRate; + + /* AAC file frequency bandwidth */ + unsigned int bandWidth; + + /* Quantizer quality */ + unsigned long quantqual; + + /* Bitstream output format (0 = Raw; 1 = ADTS) */ + unsigned int outputFormat; + + /* psychoacoustic model list */ + psymodellist_t *psymodellist; + + /* selected index in psymodellist */ + unsigned int psymodelidx; + + /* + PCM Sample Input Format + 0 FAAC_INPUT_NULL invalid, signifies a misconfigured config + 1 FAAC_INPUT_16BIT native endian 16bit + 2 FAAC_INPUT_24BIT native endian 24bit in 24 bits (not implemented) + 3 FAAC_INPUT_32BIT native endian 24bit in 32 bits (DEFAULT) + 4 FAAC_INPUT_FLOAT 32bit floating point + */ + unsigned int inputFormat; + + /* block type enforcing (SHORTCTL_NORMAL/SHORTCTL_NOSHORT/SHORTCTL_NOLONG) */ + int shortctl; + + /* + Channel Remapping + + Default 0, 1, 2, 3 ... 63 (64 is MAX_CHANNELS in coder.h) + + WAVE 4.0 2, 0, 1, 3 + WAVE 5.0 2, 0, 1, 3, 4 + WAVE 5.1 2, 0, 1, 4, 5, 3 + AIFF 5.1 2, 0, 3, 1, 4, 5 + */ + int channel_map[64]; + +} faacEncConfiguration, *faacEncConfigurationPtr; + +#pragma pack(pop) + +#endif /* _FAACCFG_H_ */ diff --git a/external/libfaac/g711.cpp b/external/libfaac/g711.cpp new file mode 100644 index 0000000..102ef1c --- /dev/null +++ b/external/libfaac/g711.cpp @@ -0,0 +1,306 @@ +/* + * g711.c + * + * u-law, A-law and linear PCM conversions. + */ + +//#include "stdafx.h" +#include +#include +#include "g711.h" + +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; + +/* copy from CCITT G.711 specifications */ +unsigned char _u2a[128] = { /* u- to A-law conversions */ + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128}; + +unsigned char _a2u[128] = { /* A- to u-law conversions */ + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}; + +static int +search( + int val, + short *table, + int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +linear2alaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + aval = seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 4) & QUANT_MASK; + else + aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; + return (aval ^ mask); + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ +int +alaw2linear( + unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + + t = (a_val & QUANT_MASK) << 4; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return ((a_val & SIGN_BIT) ? t : -t); +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +linear2ulaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return (uval ^ mask); + } + +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +int +ulaw2linear( + unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +/* A-law to u-law conversion */ +unsigned char +alaw2ulaw( + unsigned char aval) +{ + aval &= 0xff; + return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : + (0x7F ^ _a2u[aval ^ 0x55])); +} + +/* u-law to A-law conversion */ +unsigned char +ulaw2alaw( + unsigned char uval) +{ + uval &= 0xff; + return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : + (0x55 ^ (_u2a[0x7F ^ uval] - 1))); +} + +int g711_decode(void *pout_buf, int *pout_len, const void *pin_buf, const int in_len , int type) +{ + int16_t *dst = (int16_t *) pout_buf; + uint8_t *src = (uint8_t *) pin_buf; + uint32_t i = 0; + int Ret = 0; + + if ((NULL == pout_buf) || \ + (NULL == pout_len) || \ + (NULL == pin_buf) || \ + (0 == in_len)) + { + return -1; + } + + if (*pout_len < 2 * in_len) + { + return -2; + } + //---{{{ + if (TP_ALAW == type) + { + for (i = 0; i < in_len; i++) + { + //*(dst++) = alawtos16[*(src++)]; + *(dst++) = (int16_t)alaw2linear(*(src++)); + } + }else + { + for (i = 0; i < in_len; i++) + { + //*(dst++) = alawtos16[*(src++)]; + *(dst++) = (int16_t)ulaw2linear(*(src++)); + } + } + + //---}}} + *pout_len = 2 * in_len; + + Ret = 2 * in_len; + return Ret; +} \ No newline at end of file diff --git a/external/libfaac/g711.h b/external/libfaac/g711.h new file mode 100644 index 0000000..5bbe962 --- /dev/null +++ b/external/libfaac/g711.h @@ -0,0 +1,24 @@ + +#ifndef __G_711_H_ +#define __G_711_H_ + +#include + +enum _e_g711_tp +{ + TP_ALAW, //G711A + TP_ULAW //G711U +}; + +unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */ +int alaw2linear(unsigned char a_val); + +unsigned char linear2ulaw(int pcm_val); /* 2's complement (16-bit range) */ +int ulaw2linear(unsigned char u_val); + +unsigned char alaw2ulaw(unsigned char aval); +unsigned char ulaw2alaw(unsigned char uval); + +int g711_decode(void *pout_buf, int *pout_len, const void *pin_buf, const int in_len , int type); + +#endif diff --git a/external/libfaac/libfaac/aacquant.c b/external/libfaac/libfaac/aacquant.c new file mode 100644 index 0000000..20a9ab7 --- /dev/null +++ b/external/libfaac/libfaac/aacquant.c @@ -0,0 +1,708 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * Copyright (C) 2002, 2003 Krzysztof Nikiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: aacquant.c,v 1.32 2008/03/23 23:00:25 menno Exp $ + */ + +#include +#include + +#include "frame.h" +#include "aacquant.h" +#include "coder.h" +#include "huffman.h" +#include "psych.h" +#include "util.h" + +#define TAKEHIRO_IEEE754_HACK 1 + +#define XRPOW_FTOI(src,dest) ((dest) = (int)(src)) +#define QUANTFAC(rx) adj43[rx] +#define ROUNDFAC 0.4054 + +static int FixNoise(CoderInfo *coderInfo, + const double *xr, + double *xr_pow, + int *xi, + double *xmin, + double *pow43, + double *adj43); + +static void CalcAllowedDist(CoderInfo *coderInfo, PsyInfo *psyInfo, + double *xr, double *xmin, int quality); + + +void AACQuantizeInit(CoderInfo *coderInfo, unsigned int numChannels, + AACQuantCfg *aacquantCfg) +{ + unsigned int channel, i; + + aacquantCfg->pow43 = (double*)AllocMemory(PRECALC_SIZE*sizeof(double)); + aacquantCfg->adj43 = (double*)AllocMemory(PRECALC_SIZE*sizeof(double)); + + aacquantCfg->pow43[0] = 0.0; + for(i=1;ipow43[i] = pow((double)i, 4.0/3.0); + +#if TAKEHIRO_IEEE754_HACK + aacquantCfg->adj43[0] = 0.0; + for (i = 1; i < PRECALC_SIZE; i++) + aacquantCfg->adj43[i] = i - 0.5 - pow(0.5 * (aacquantCfg->pow43[i - 1] + aacquantCfg->pow43[i]),0.75); +#else // !TAKEHIRO_IEEE754_HACK + for (i = 0; i < PRECALC_SIZE-1; i++) + aacquantCfg->adj43[i] = (i + 1) - pow(0.5 * (aacquantCfg->pow43[i] + aacquantCfg->pow43[i + 1]), 0.75); + aacquantCfg->adj43[i] = 0.5; +#endif + + for (channel = 0; channel < numChannels; channel++) { + coderInfo[channel].requantFreq = (double*)AllocMemory(BLOCK_LEN_LONG*sizeof(double)); + } +} + +void AACQuantizeEnd(CoderInfo *coderInfo, unsigned int numChannels, + AACQuantCfg *aacquantCfg) +{ + unsigned int channel; + + if (aacquantCfg->pow43) + { + FreeMemory(aacquantCfg->pow43); + aacquantCfg->pow43 = NULL; + } + if (aacquantCfg->adj43) + { + FreeMemory(aacquantCfg->adj43); + aacquantCfg->adj43 = NULL; + } + + for (channel = 0; channel < numChannels; channel++) { + if (coderInfo[channel].requantFreq) FreeMemory(coderInfo[channel].requantFreq); + } +} + +static void BalanceEnergy(CoderInfo *coderInfo, + const double *xr, const int *xi, + double *pow43) +{ + const double ifqstep = pow(2.0, 0.25); + const double logstep_1 = 1.0 / log(ifqstep); + int sb; + int nsfb = coderInfo->nr_of_sfb; + int start, end; + int l; + double en0, enq; + int shift; + + for (sb = 0; sb < nsfb; sb++) + { + double qfac_1; + + start = coderInfo->sfb_offset[sb]; + end = coderInfo->sfb_offset[sb+1]; + + qfac_1 = pow(2.0, -0.25*(coderInfo->scale_factor[sb] - coderInfo->global_gain)); + + en0 = 0.0; + enq = 0.0; + for (l = start; l < end; l++) + { + double xq; + + if (!sb && !xi[l]) + continue; + + xq = pow43[xi[l]]; + + en0 += xr[l] * xr[l]; + enq += xq * xq; + } + + if (enq == 0.0) + continue; + + enq *= qfac_1 * qfac_1; + + shift = (int)(log(sqrt(enq / en0)) * logstep_1 + 1000.5); + shift -= 1000; + + shift += coderInfo->scale_factor[sb]; + coderInfo->scale_factor[sb] = shift; + } +} + +static void UpdateRequant(CoderInfo *coderInfo, int *xi, + double *pow43) +{ + double *requant_xr = coderInfo->requantFreq; + int sb; + int i; + + for (sb = 0; sb < coderInfo->nr_of_sfb; sb++) + { + double invQuantFac = + pow(2.0, -0.25*(coderInfo->scale_factor[sb] - coderInfo->global_gain)); + int start = coderInfo->sfb_offset[sb]; + int end = coderInfo->sfb_offset[sb + 1]; + + for (i = start; i < end; i++) + requant_xr[i] = pow43[xi[i]] * invQuantFac; + } +} + +int AACQuantize(CoderInfo *coderInfo, + PsyInfo *psyInfo, + ChannelInfo *channelInfo, + int *cb_width, + int num_cb, + double *xr, + AACQuantCfg *aacquantCfg) +{ + int sb, i, do_q = 0; + int bits = 0, sign; + double xr_pow[FRAME_LEN]; + double xmin[MAX_SCFAC_BANDS]; + int xi[FRAME_LEN]; + + /* Use local copy's */ + int *scale_factor = coderInfo->scale_factor; + + /* Set all scalefactors to 0 */ + coderInfo->global_gain = 0; + for (sb = 0; sb < coderInfo->nr_of_sfb; sb++) + scale_factor[sb] = 0; + + /* Compute xr_pow */ + for (i = 0; i < FRAME_LEN; i++) { + double temp = fabs(xr[i]); + xr_pow[i] = sqrt(temp * sqrt(temp)); + do_q += (temp > 1E-20); + } + + if (do_q) { + CalcAllowedDist(coderInfo, psyInfo, xr, xmin, aacquantCfg->quality); + coderInfo->global_gain = 0; + FixNoise(coderInfo, xr, xr_pow, xi, xmin, + aacquantCfg->pow43, aacquantCfg->adj43); + BalanceEnergy(coderInfo, xr, xi, aacquantCfg->pow43); + UpdateRequant(coderInfo, xi, aacquantCfg->pow43); + + for ( i = 0; i < FRAME_LEN; i++ ) { + sign = (xr[i] < 0) ? -1 : 1; + xi[i] *= sign; + coderInfo->requantFreq[i] *= sign; + } + } else { + coderInfo->global_gain = 0; + SetMemory(xi, 0, FRAME_LEN*sizeof(int)); + } + + BitSearch(coderInfo, xi); + + /* offset the difference of common_scalefac and scalefactors by SF_OFFSET */ + for (i = 0; i < coderInfo->nr_of_sfb; i++) { + if ((coderInfo->book_vector[i]!=INTENSITY_HCB)&&(coderInfo->book_vector[i]!=INTENSITY_HCB2)) { + scale_factor[i] = coderInfo->global_gain - scale_factor[i] + SF_OFFSET; + } + } + coderInfo->global_gain = scale_factor[0]; +#if 0 + printf("global gain: %d\n", coderInfo->global_gain); + for (i = 0; i < coderInfo->nr_of_sfb; i++) + printf("sf %d: %d\n", i, coderInfo->scale_factor[i]); +#endif + // clamp to valid diff range + { + int previous_scale_factor = coderInfo->global_gain; + int previous_is_factor = 0; + for (i = 0; i < coderInfo->nr_of_sfb; i++) { + if ((coderInfo->book_vector[i]==INTENSITY_HCB) || + (coderInfo->book_vector[i]==INTENSITY_HCB2)) { + const int diff = scale_factor[i] - previous_is_factor; + if (diff < -60) scale_factor[i] = previous_is_factor - 60; + else if (diff > 59) scale_factor[i] = previous_is_factor + 59; + previous_is_factor = scale_factor[i]; +// printf("sf %d: %d diff=%d **\n", i, coderInfo->scale_factor[i], diff); + } else if (coderInfo->book_vector[i]) { + const int diff = scale_factor[i] - previous_scale_factor; + if (diff < -60) scale_factor[i] = previous_scale_factor - 60; + else if (diff > 59) scale_factor[i] = previous_scale_factor + 59; + previous_scale_factor = scale_factor[i]; +// printf("sf %d: %d diff=%d\n", i, coderInfo->scale_factor[i], diff); + } + } + } + + /* place the codewords and their respective lengths in arrays data[] and len[] respectively */ + /* there are 'counter' elements in each array, and these are variable length arrays depending on the input */ +#ifdef DRM + coderInfo->iLenReordSpData = 0; /* init length of reordered spectral data */ + coderInfo->iLenLongestCW = 0; /* init length of longest codeword */ + coderInfo->cur_cw = 0; /* init codeword counter */ +#endif + coderInfo->spectral_count = 0; + sb = 0; + for(i = 0; i < coderInfo->nr_of_sfb; i++) { + OutputBits( + coderInfo, +#ifdef DRM + &coderInfo->book_vector[i], /* needed for VCB11 */ +#else + coderInfo->book_vector[i], +#endif + xi, + coderInfo->sfb_offset[i], + coderInfo->sfb_offset[i+1]-coderInfo->sfb_offset[i]); + + if (coderInfo->book_vector[i]) + sb = i; + } + + // FIXME: Check those max_sfb/nr_of_sfb. Isn't it the same? + coderInfo->max_sfb = coderInfo->nr_of_sfb = sb + 1; + + return bits; +} + + +#if TAKEHIRO_IEEE754_HACK + +typedef union { + float f; + int i; +} fi_union; + +#define MAGIC_FLOAT (65536*(128)) +#define MAGIC_INT 0x4b000000 + +#if 0 +static void Quantize(const double *xp, int *pi, double istep) +{ + int j; + fi_union *fi; + + fi = (fi_union *)pi; + for (j = FRAME_LEN/4 - 1; j >= 0; --j) { + double x0 = istep * xp[0]; + double x1 = istep * xp[1]; + double x2 = istep * xp[2]; + double x3 = istep * xp[3]; + + x0 += MAGIC_FLOAT; fi[0].f = x0; + x1 += MAGIC_FLOAT; fi[1].f = x1; + x2 += MAGIC_FLOAT; fi[2].f = x2; + x3 += MAGIC_FLOAT; fi[3].f = x3; + + fi[0].f = x0 + (adj43asm - MAGIC_INT)[fi[0].i]; + fi[1].f = x1 + (adj43asm - MAGIC_INT)[fi[1].i]; + fi[2].f = x2 + (adj43asm - MAGIC_INT)[fi[2].i]; + fi[3].f = x3 + (adj43asm - MAGIC_INT)[fi[3].i]; + + fi[0].i -= MAGIC_INT; + fi[1].i -= MAGIC_INT; + fi[2].i -= MAGIC_INT; + fi[3].i -= MAGIC_INT; + fi += 4; + xp += 4; + } +} +#endif +static void QuantizeBand(const double *xp, int *pi, double istep, + int offset, int end, double *adj43) +{ + int j; + fi_union *fi; + + fi = (fi_union *)pi; + for (j = offset; j < end; j++) + { + double x0 = istep * xp[j]; + + x0 += MAGIC_FLOAT; fi[j].f = (float)x0; + fi[j].f = x0 + (adj43 - MAGIC_INT)[fi[j].i]; + fi[j].i -= MAGIC_INT; + } +} +#else +#if 0 +static void Quantize(const double *xr, int *ix, double istep) +{ + int j; + + for (j = FRAME_LEN/8; j > 0; --j) { + double x1, x2, x3, x4, x5, x6, x7, x8; + int rx1, rx2, rx3, rx4, rx5, rx6, rx7, rx8; + + x1 = *xr++ * istep; + x2 = *xr++ * istep; + XRPOW_FTOI(x1, rx1); + x3 = *xr++ * istep; + XRPOW_FTOI(x2, rx2); + x4 = *xr++ * istep; + XRPOW_FTOI(x3, rx3); + x5 = *xr++ * istep; + XRPOW_FTOI(x4, rx4); + x6 = *xr++ * istep; + XRPOW_FTOI(x5, rx5); + x7 = *xr++ * istep; + XRPOW_FTOI(x6, rx6); + x8 = *xr++ * istep; + XRPOW_FTOI(x7, rx7); + x1 += QUANTFAC(rx1); + XRPOW_FTOI(x8, rx8); + x2 += QUANTFAC(rx2); + XRPOW_FTOI(x1,*ix++); + x3 += QUANTFAC(rx3); + XRPOW_FTOI(x2,*ix++); + x4 += QUANTFAC(rx4); + XRPOW_FTOI(x3,*ix++); + x5 += QUANTFAC(rx5); + XRPOW_FTOI(x4,*ix++); + x6 += QUANTFAC(rx6); + XRPOW_FTOI(x5,*ix++); + x7 += QUANTFAC(rx7); + XRPOW_FTOI(x6,*ix++); + x8 += QUANTFAC(rx8); + XRPOW_FTOI(x7,*ix++); + XRPOW_FTOI(x8,*ix++); + } +} +#endif +static void QuantizeBand(const double *xp, int *ix, double istep, + int offset, int end, double *adj43) +{ + int j; + + for (j = offset; j < end; j++) + { + double x0 = istep * xp[j]; + x0 += adj43[(int)x0]; + ix[j] = (int)x0; + } +} +#endif + +static void CalcAllowedDist(CoderInfo *coderInfo, PsyInfo *psyInfo, + double *xr, double *xmin, int quality) +{ + int sfb, start, end, l; + const double globalthr = 132.0 / (double)quality; + int last = coderInfo->lastx; + int lastsb = 0; + int *cb_offset = coderInfo->sfb_offset; + int num_cb = coderInfo->nr_of_sfb; + double avgenrg = coderInfo->avgenrg; + + for (sfb = 0; sfb < num_cb; sfb++) + { + if (last > cb_offset[sfb]) + lastsb = sfb; + } + + for (sfb = 0; sfb < num_cb; sfb++) + { + double thr, tmp; + double enrg = 0.0; + + start = cb_offset[sfb]; + end = cb_offset[sfb + 1]; + + if (sfb > lastsb) + { + xmin[sfb] = 0; + continue; + } + + if (coderInfo->block_type != ONLY_SHORT_WINDOW) + { + double enmax = -1.0; + double lmax; + + lmax = start; + for (l = start; l < end; l++) + { + if (enmax < (xr[l] * xr[l])) + { + enmax = xr[l] * xr[l]; + lmax = l; + } + } + + start = lmax - 2; + end = lmax + 3; + if (start < 0) + start = 0; + if (end > last) + end = last; + } + + for (l = start; l < end; l++) + { + enrg += xr[l]*xr[l]; + } + + thr = enrg/((double)(end-start)*avgenrg); + thr = pow(thr, 0.1*(lastsb-sfb)/lastsb + 0.3); + + tmp = 1.0 - ((double)start / (double)last); + tmp = tmp * tmp * tmp + 0.075; + + thr = 1.0 / (1.4*thr + tmp); + + xmin[sfb] = ((coderInfo->block_type == ONLY_SHORT_WINDOW) ? 0.65 : 1.12) + * globalthr * thr; + } +} + +static int FixNoise(CoderInfo *coderInfo, + const double *xr, + double *xr_pow, + int *xi, + double *xmin, + double *pow43, + double *adj43) +{ + int i, sb; + int start, end; + double diffvol; + double tmp; + const double ifqstep = pow(2.0, 0.1875); + const double log_ifqstep = 1.0 / log(ifqstep); + const double maxstep = 0.05; + + for (sb = 0; sb < coderInfo->nr_of_sfb; sb++) + { + double sfacfix; + double fixstep = 0.25; + int sfac; + double fac; + int dist; + double sfacfix0 = 1.0, dist0 = 1e50; + double maxx; + + start = coderInfo->sfb_offset[sb]; + end = coderInfo->sfb_offset[sb+1]; + + if (!xmin[sb]) + goto nullsfb; + + maxx = 0.0; + for (i = start; i < end; i++) + { + if (xr_pow[i] > maxx) + maxx = xr_pow[i]; + } + + //printf("band %d: maxx: %f\n", sb, maxx); + if (maxx < 10.0) + { + nullsfb: + for (i = start; i < end; i++) + xi[i] = 0; + coderInfo->scale_factor[sb] = 10; + continue; + } + + sfacfix = 1.0 / maxx; + sfac = (int)(log(sfacfix) * log_ifqstep - 0.5); + for (i = start; i < end; i++) + xr_pow[i] *= sfacfix; + maxx *= sfacfix; + coderInfo->scale_factor[sb] = sfac; + QuantizeBand(xr_pow, xi, IPOW20(coderInfo->global_gain), start, end, + adj43); + //printf("\tsfac: %d\n", sfac); + + calcdist: + diffvol = 0.0; + for (i = start; i < end; i++) + { + tmp = xi[i]; + diffvol += tmp * tmp; // ~x^(3/2) + } + + if (diffvol < 1e-6) + diffvol = 1e-6; + tmp = pow(diffvol / (double)(end - start), -0.666); + + if (fabs(fixstep) > maxstep) + { + double dd = 0.5*(tmp / xmin[sb] - 1.0); + + if (fabs(dd) < fabs(fixstep)) + { + fixstep = dd; + + if (fabs(fixstep) < maxstep) + fixstep = maxstep * ((fixstep > 0) ? 1 : -1); + } + } + + if (fixstep > 0) + { + if (tmp < dist0) + { + dist0 = tmp; + sfacfix0 = sfacfix; + } + else + { + if (fixstep > .1) + fixstep = .1; + } + } + else + { + dist0 = tmp; + sfacfix0 = sfacfix; + } + + dist = (tmp > xmin[sb]); + fac = 0.0; + if (fabs(fixstep) >= maxstep) + { + if ((dist && (fixstep < 0)) + || (!dist && (fixstep > 0))) + { + fixstep = -0.5 * fixstep; + } + + fac = 1.0 + fixstep; + } + else if (dist) + { + fac = 1.0 + fabs(fixstep); + } + + if (fac != 0.0) + { + if (maxx * fac >= IXMAX_VAL) + { + // restore best noise + fac = sfacfix0 / sfacfix; + for (i = start; i < end; i++) + xr_pow[i] *= fac; + maxx *= fac; + sfacfix *= fac; + coderInfo->scale_factor[sb] = log(sfacfix) * log_ifqstep - 0.5; + QuantizeBand(xr_pow, xi, IPOW20(coderInfo->global_gain), start, end, + adj43); + continue; + } + + if (coderInfo->scale_factor[sb] < -10) + { + for (i = start; i < end; i++) + xr_pow[i] *= fac; + maxx *= fac; + sfacfix *= fac; + coderInfo->scale_factor[sb] = log(sfacfix) * log_ifqstep - 0.5; + QuantizeBand(xr_pow, xi, IPOW20(coderInfo->global_gain), start, end, + adj43); + goto calcdist; + } + } + } + return 0; +} + +int SortForGrouping(CoderInfo* coderInfo, + PsyInfo *psyInfo, + ChannelInfo *channelInfo, + int *sfb_width_table, + double *xr) +{ + int i,j,ii; + int index = 0; + double xr_tmp[FRAME_LEN]; + int group_offset=0; + int k=0; + int windowOffset = 0; + + + /* set up local variables for used quantInfo elements */ + int* sfb_offset = coderInfo->sfb_offset; + int* nr_of_sfb = &(coderInfo->nr_of_sfb); + int* window_group_length; + int num_window_groups; + *nr_of_sfb = coderInfo->max_sfb; /* Init to max_sfb */ + window_group_length = coderInfo->window_group_length; + num_window_groups = coderInfo->num_window_groups; + + /* calc org sfb_offset just for shortblock */ + sfb_offset[k]=0; + for (k=1 ; k <*nr_of_sfb+1; k++) { + sfb_offset[k] = sfb_offset[k-1] + sfb_width_table[k-1]; + } + + /* sort the input spectral coefficients */ + index = 0; + group_offset=0; + for (i=0; i< num_window_groups; i++) { + for (k=0; k<*nr_of_sfb; k++) { + for (j=0; j < window_group_length[i]; j++) { + for (ii=0;ii< sfb_width_table[k];ii++) + xr_tmp[index++] = xr[ii+ sfb_offset[k] + BLOCK_LEN_SHORT*j +group_offset]; + } + } + group_offset += BLOCK_LEN_SHORT*window_group_length[i]; + } + + for (k=0; ksfb_offset[coderInfo->nr_of_sfb]; + for (l = 0; l < end; l++) + { + if (xr[l]) + { + last = l; + totenrg += xr[l] * xr[l]; + } + } + last++; + + coderInfo->lastx = last; + coderInfo->avgenrg = totenrg / last; +} diff --git a/external/libfaac/libfaac/aacquant.h b/external/libfaac/libfaac/aacquant.h new file mode 100644 index 0000000..400901f --- /dev/null +++ b/external/libfaac/libfaac/aacquant.h @@ -0,0 +1,74 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: aacquant.h,v 1.9 2003/10/12 16:43:39 knik Exp $ + */ + +#ifndef AACQUANT_H +#define AACQUANT_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "coder.h" +#include "psych.h" + +#define IXMAX_VAL 8191 +#define PRECALC_SIZE (IXMAX_VAL+2) +#define LARGE_BITS 100000 +#define SF_OFFSET 100 + +#define POW20(x) pow(2.0,((double)x)*.25) +#define IPOW20(x) pow(2.0,-((double)x)*.1875) + +#pragma pack(push, 1) +typedef struct + { + double *pow43; + double *adj43; + double quality; + } AACQuantCfg; +#pragma pack(pop) + +void AACQuantizeInit(CoderInfo *coderInfo, unsigned int numChannels, + AACQuantCfg *aacquantCfg); +void AACQuantizeEnd(CoderInfo *coderInfo, unsigned int numChannels, + AACQuantCfg *aacquantCfg); + +int AACQuantize(CoderInfo *coderInfo, + PsyInfo *psyInfo, + ChannelInfo *channelInfo, + int *cb_width, + int num_cb, + double *xr, + AACQuantCfg *aacquantcfg); + +int SortForGrouping(CoderInfo* coderInfo, + PsyInfo *psyInfo, + ChannelInfo *channelInfo, + int *sfb_width_table, + double *xr); +void CalcAvgEnrg(CoderInfo *coderInfo, + const double *xr); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AACQUANT_H */ diff --git a/external/libfaac/libfaac/backpred.c b/external/libfaac/libfaac/backpred.c new file mode 100644 index 0000000..e57a02f --- /dev/null +++ b/external/libfaac/libfaac/backpred.c @@ -0,0 +1,381 @@ +/********************************************************************** + +This software module was originally developed by +and edited by Nokia in the course of +development of the MPEG-2 NBC/MPEG-4 Audio standard +ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an +implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools +as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives +users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this +software module or modifications thereof for use in hardware or +software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio +standards. Those intending to use this software module in hardware or +software products are advised that this use may infringe existing +patents. The original developer of this software module and his/her +company, the subsequent editors and their companies, and ISO/IEC have +no liability for use of this software module or modifications thereof +in an implementation. Copyright is not released for non MPEG-2 +NBC/MPEG-4 Audio conforming products. The original developer retains +full right to use the code for his/her own purpose, assign or donate +the code to a third party and to inhibit third party from using the +code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This +copyright notice must be included in all copies or derivative works. + +Copyright (c) 1997. +**********************************************************************/ +/* + * $Id: backpred.c,v 1.5 2001/09/04 18:39:35 menno Exp $ + */ + +#include +#include "frame.h" +#include "coder.h" +#include "channels.h" +#include "backpred.h" + + +void PredInit(faacEncHandle hEncoder) +{ + unsigned int channel; + + for (channel = 0; channel < hEncoder->numChannels; channel++) { + BwpInfo *bwpInfo = &(hEncoder->coderInfo[channel].bwpInfo); + + bwpInfo->psy_init_mc = 0; + bwpInfo->reset_count_mc = 0; + } +} + +void PredCalcPrediction(double *act_spec, double *last_spec, int btype, + int nsfb, + int *isfb_width, + CoderInfo *coderInfo, + ChannelInfo *channelInfo, + int chanNum) +{ + int i, k, j, cb_long; + int leftChanNum; + int isRightWithCommonWindow; + double num_bit, snr[SBMAX_L]; + double energy[BLOCK_LEN_LONG], snr_p[BLOCK_LEN_LONG], temp1, temp2; + ChannelInfo *thisChannel; + + /* Set pointers for specified channel number */ + /* int psy_init; */ + int *psy_init; + double (*dr)[BLOCK_LEN_LONG],(*e)[BLOCK_LEN_LONG]; + double (*K)[BLOCK_LEN_LONG], (*R)[BLOCK_LEN_LONG]; + double (*VAR)[BLOCK_LEN_LONG], (*KOR)[BLOCK_LEN_LONG]; + double *sb_samples_pred; + int *thisLineNeedsResetting; + /* int reset_count; */ + int *reset_count; + int *pred_global_flag; + int *pred_sfb_flag; + int *reset_group; + + /* Set pointers for this chanNum */ + pred_global_flag = &(coderInfo[chanNum].pred_global_flag); + pred_sfb_flag = coderInfo[chanNum].pred_sfb_flag; + reset_group = &(coderInfo[chanNum].reset_group_number); + psy_init = &coderInfo[chanNum].bwpInfo.psy_init_mc; + dr = &coderInfo[chanNum].bwpInfo.dr_mc[0]; + e = &coderInfo[chanNum].bwpInfo.e_mc[0]; + K = &coderInfo[chanNum].bwpInfo.K_mc[0]; + R = &coderInfo[chanNum].bwpInfo.R_mc[0]; + VAR = &coderInfo[chanNum].bwpInfo.VAR_mc[0]; + KOR = &coderInfo[chanNum].bwpInfo.KOR_mc[0]; + sb_samples_pred = &coderInfo[chanNum].bwpInfo.sb_samples_pred_mc[0]; + thisLineNeedsResetting = &coderInfo[chanNum].bwpInfo.thisLineNeedsResetting_mc[0]; + reset_count = &coderInfo[chanNum].bwpInfo.reset_count_mc; + + thisChannel = &(channelInfo[chanNum]); + *psy_init = (*psy_init && (btype!=2)); + + if((*psy_init) == 0) { + for (j=0; jch_is_left) { + (*reset_count)++; + if (*reset_count >= 31 * RESET_FRAME) + *reset_count = RESET_FRAME; + } + return; + } + + + /**************************************************/ + /* Compute state using last_spec */ + /**************************************************/ + for (i=0;i=1;j--) + R[j][i] = A*(R[j-1][i]-dr[j][i]); + R[0][i] = A*e[0][i]; + } + + + /**************************************************/ + /* Reset state here if resets were sent */ + /**************************************************/ + for (i=0;iMINVAR) + K[j][i] = KOR[j][i]/VAR[j][i]*B; + else + K[j][i] = 0; + } + } + + + for (k=0; kcpe)&&( !(thisChannel->ch_is_left))) { + leftChanNum = thisChannel->paired_ch; + if (channelInfo[leftChanNum].common_window) { + isRightWithCommonWindow = 1; + } + } + + if (isRightWithCommonWindow) { + + /**************************************************/ + /* Use predictor data from the left channel. */ + /**************************************************/ + CopyPredInfo(&(coderInfo[chanNum]),&(coderInfo[leftChanNum])); + + /* Make sure to turn off bands with intensity stereo */ +#if 0 + if (thisChannel->is_info.is_present) { + for (i=0; iis_info.is_used[i]) { + pred_sfb_flag[i] = 0; + } + } + } +#endif + + cb_long=0; + for (i=0; i0.0) + num_bit+=snr[i]/6.*isfb_width[i]; + + /* Determine global enable, if not enabled predicted samples are zeroed */ + pred_global_flag[0]=1; + if(num_bit<50) { + pred_global_flag[0]=0; num_bit=0.0; + for (j=0; jcpe)&&( !(thisChannel->ch_is_left))) { + /* if (!thisChannel->ch_is_left) {*/ + /**********************************************************/ + /* Using predictor reset data from the left channel. */ + /**********************************************************/ + reset_count = &coderInfo[leftChanNum].bwpInfo.reset_count_mc; + /* Reset the frame counter */ + for (i=0;i= 31 * RESET_FRAME) + *reset_count = RESET_FRAME; + if (*reset_count % RESET_FRAME == 0) + { /* Send a reset in this frame */ + *reset_group = *reset_count / 8; + for (i = *reset_group - 1; i < BLOCK_LEN_LONG; i += 30) + { + thisLineNeedsResetting[i]=1; + } + } + else + *reset_group = -1; + } + + + /* Ensure that prediction data is sent when there is a prediction + * reset. + */ + if (*reset_group != -1 && pred_global_flag[0] == 0) + { + pred_global_flag[0] = 1; + for (i = 0; i < nsfb; i++) + pred_sfb_flag[i] = 0; + } +} + + +void CopyPredInfo(CoderInfo *right, CoderInfo *left) +{ + int band; + + right->pred_global_flag = left->pred_global_flag; + right->reset_group_number = left->reset_group_number; + + for (band = 0; bandpred_sfb_flag[band] = left->pred_sfb_flag[band]; + } +} + + + + diff --git a/external/libfaac/libfaac/backpred.h b/external/libfaac/libfaac/backpred.h new file mode 100644 index 0000000..db5680d --- /dev/null +++ b/external/libfaac/libfaac/backpred.h @@ -0,0 +1,51 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: backpred.h,v 1.5 2001/06/08 18:01:09 menno Exp $ + */ + +#ifndef _AAC_BACK_H_INCLUDED +#define _AAC_BACK_H_INCLUDED + +#define PRED_ALPHA 0.90625 +#define PRED_A 0.953125 +#define PRED_B 0.953125 + +#define ALPHA PRED_ALPHA +#define A PRED_A +#define B PRED_B +#define MINVAR 1.e-10 + +/* Reset every RESET_FRAME frames. */ +#define RESET_FRAME 8 + +void PredCalcPrediction(double *act_spec, + double *last_spec, + int btype, + int nsfb, + int *isfb_width, + CoderInfo *coderInfo, + ChannelInfo *channelInfo, + int chanNum); + +void PredInit(faacEncHandle hEncoder); + +void CopyPredInfo(CoderInfo *right, CoderInfo *left); + + +#endif diff --git a/external/libfaac/libfaac/bitstream.c b/external/libfaac/libfaac/bitstream.c new file mode 100644 index 0000000..fb3f005 --- /dev/null +++ b/external/libfaac/libfaac/bitstream.c @@ -0,0 +1,1496 @@ +/********************************************************************** + +This software module was originally developed by +and edited by Texas Instruments in the course of +development of the MPEG-2 NBC/MPEG-4 Audio standard +ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an +implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools +as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives +users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this +software module or modifications thereof for use in hardware or +software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio +standards. Those intending to use this software module in hardware or +software products are advised that this use may infringe existing +patents. The original developer of this software module and his/her +company, the subsequent editors and their companies, and ISO/IEC have +no liability for use of this software module or modifications thereof +in an implementation. Copyright is not released for non MPEG-2 +NBC/MPEG-4 Audio conforming products. The original developer retains +full right to use the code for his/her own purpose, assign or donate +the code to a third party and to inhibit third party from using the +code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This +copyright notice must be included in all copies or derivative works. + +Copyright (c) 1997. +**********************************************************************/ +/* + * $Id: bitstream.c,v 1.34 2007/06/05 18:59:47 menno Exp $ + */ + +#include +#include + +#include "coder.h" +#include "channels.h" +#include "huffman.h" +#include "bitstream.h" +#include "ltp.h" +#include "util.h" + +static int CountBitstream(faacEncHandle hEncoder, + CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int numChannels); +static int WriteADTSHeader(faacEncHandle hEncoder, + BitStream *bitStream, + int writeFlag); +static int WriteCPE(CoderInfo *coderInfoL, + CoderInfo *coderInfoR, + ChannelInfo *channelInfo, + BitStream* bitStream, + int objectType, + int writeFlag); +static int WriteSCE(CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int objectType, + int writeFlag); +static int WriteLFE(CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int objectType, + int writeFlag); +static int WriteICSInfo(CoderInfo *coderInfo, + BitStream *bitStream, + int objectType, + int common_window, + int writeFlag); +static int WriteICS(CoderInfo *coderInfo, + BitStream *bitStream, + int commonWindow, + int objectType, + int writeFlag); +static int WriteLTPPredictorData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); +static int WritePredictorData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); +static int WritePulseData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); +static int WriteTNSData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); +static int WriteGainControlData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); +static int WriteSpectralData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); +static int WriteAACFillBits(BitStream* bitStream, + int numBits, + int writeFlag); +static int FindGroupingBits(CoderInfo *coderInfo); +static long BufferNumBit(BitStream *bitStream); +static int WriteByte(BitStream *bitStream, + unsigned long data, + int numBit); +static int ByteAlign(BitStream* bitStream, + int writeFlag, int bitsSoFar); +#ifdef DRM +static int PutBitHcr(BitStream *bitStream, + unsigned long curpos, + unsigned long data, + int numBit); +static int rewind_word(int W, int len); +static int WriteReorderedSpectralData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); +static void calc_CRC(BitStream *bitStream, int len); +#endif + + +static int WriteFAACStr(BitStream *bitStream, char *version, int write) +{ + int i; + char str[200]; + int len, padbits, count; + int bitcnt; + + sprintf(str, "libfaac %s", version); + + len = strlen(str) + 1; + padbits = (8 - ((bitStream->numBit + 7) % 8)) % 8; + count = len + 3; + + bitcnt = LEN_SE_ID + 4 + ((count < 15) ? 0 : 8) + count * 8; + if (!write) + return bitcnt; + + PutBit(bitStream, ID_FIL, LEN_SE_ID); + if (count < 15) + { + PutBit(bitStream, count, 4); + } + else + { + PutBit(bitStream, 15, 4); + PutBit(bitStream, count - 14, 8); + } + + PutBit(bitStream, 0, padbits); + PutBit(bitStream, 0, 8); + PutBit(bitStream, 0, 8); // just in case + for (i = 0; i < len; i++) + PutBit(bitStream, str[i], 8); + + PutBit(bitStream, 0, 8 - padbits); + + return bitcnt; +} + + +int WriteBitstream(faacEncHandle hEncoder, + CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int numChannel) +{ + int channel; + int bits = 0; + int bitsLeftAfterFill, numFillBits; + + CountBitstream(hEncoder, coderInfo, channelInfo, bitStream, numChannel); + + if(hEncoder->config.outputFormat == 1){ + bits += WriteADTSHeader(hEncoder, bitStream, 1); + }else{ + bits = 0; // compilier will remove it, byt anyone will see that current size of bitstream is 0 + } + +/* sur: faad2 complains about scalefactor error if we are writing FAAC String */ +#ifndef DRM + if (hEncoder->frameNum == 4) + WriteFAACStr(bitStream, hEncoder->config.name, 1); +#endif + + for (channel = 0; channel < numChannel; channel++) { + + if (channelInfo[channel].present) { + + /* Write out a single_channel_element */ + if (!channelInfo[channel].cpe) { + + if (channelInfo[channel].lfe) { + /* Write out lfe */ + bits += WriteLFE(&coderInfo[channel], + &channelInfo[channel], + bitStream, + hEncoder->config.aacObjectType, + 1); + } else { + /* Write out sce */ + bits += WriteSCE(&coderInfo[channel], + &channelInfo[channel], + bitStream, + hEncoder->config.aacObjectType, + 1); + } + + } else { + + if (channelInfo[channel].ch_is_left) { + /* Write out cpe */ + bits += WriteCPE(&coderInfo[channel], + &coderInfo[channelInfo[channel].paired_ch], + &channelInfo[channel], + bitStream, + hEncoder->config.aacObjectType, + 1); + } + } + } + } + + /* Compute how many fill bits are needed to avoid overflowing bit reservoir */ + /* Save room for ID_END terminator */ + if (bits < (8 - LEN_SE_ID) ) { + numFillBits = 8 - LEN_SE_ID - bits; + } else { + numFillBits = 0; + } + + /* Write AAC fill_elements, smallest fill element is 7 bits. */ + /* Function may leave up to 6 bits left after fill, so tell it to fill a few extra */ + numFillBits += 6; + bitsLeftAfterFill = WriteAACFillBits(bitStream, numFillBits, 1); + bits += (numFillBits - bitsLeftAfterFill); + + /* Write ID_END terminator */ + bits += LEN_SE_ID; + PutBit(bitStream, ID_END, LEN_SE_ID); + + /* Now byte align the bitstream */ + /* + * This byte_alignment() is correct for both MPEG2 and MPEG4, although + * in MPEG4 the byte_alignment() is officially done before the new frame + * instead of at the end. But this is basically the same. + */ + bits += ByteAlign(bitStream, 1, bits); + + return bits; +} + +static int CountBitstream(faacEncHandle hEncoder, + CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int numChannel) +{ + int channel; + int bits = 0; + int bitsLeftAfterFill, numFillBits; + + + if(hEncoder->config.outputFormat == 1){ + bits += WriteADTSHeader(hEncoder, bitStream, 0); + }else{ + bits = 0; // compilier will remove it, byt anyone will see that current size of bitstream is 0 + } + +/* sur: faad2 complains about scalefactor error if we are writing FAAC String */ +#ifndef DRM + if (hEncoder->frameNum == 4) + bits += WriteFAACStr(bitStream, hEncoder->config.name, 0); +#endif + + for (channel = 0; channel < numChannel; channel++) { + + if (channelInfo[channel].present) { + + /* Write out a single_channel_element */ + if (!channelInfo[channel].cpe) { + + if (channelInfo[channel].lfe) { + /* Write out lfe */ + bits += WriteLFE(&coderInfo[channel], + &channelInfo[channel], + bitStream, + hEncoder->config.aacObjectType, + 0); + } else { + /* Write out sce */ + bits += WriteSCE(&coderInfo[channel], + &channelInfo[channel], + bitStream, + hEncoder->config.aacObjectType, + 0); + } + + } else { + + if (channelInfo[channel].ch_is_left) { + /* Write out cpe */ + bits += WriteCPE(&coderInfo[channel], + &coderInfo[channelInfo[channel].paired_ch], + &channelInfo[channel], + bitStream, + hEncoder->config.aacObjectType, + 0); + } + } + } + } + + /* Compute how many fill bits are needed to avoid overflowing bit reservoir */ + /* Save room for ID_END terminator */ + if (bits < (8 - LEN_SE_ID) ) { + numFillBits = 8 - LEN_SE_ID - bits; + } else { + numFillBits = 0; + } + + /* Write AAC fill_elements, smallest fill element is 7 bits. */ + /* Function may leave up to 6 bits left after fill, so tell it to fill a few extra */ + numFillBits += 6; + bitsLeftAfterFill = WriteAACFillBits(bitStream, numFillBits, 0); + bits += (numFillBits - bitsLeftAfterFill); + + /* Write ID_END terminator */ + bits += LEN_SE_ID; + + /* Now byte align the bitstream */ + bits += ByteAlign(bitStream, 0, bits); + + hEncoder->usedBytes = bit2byte(bits); + + return bits; +} + +static int WriteADTSHeader(faacEncHandle hEncoder, + BitStream *bitStream, + int writeFlag) +{ + int bits = 56; + + if (writeFlag) { + /* Fixed ADTS header */ + PutBit(bitStream, 0xFFFF, 12); /* 12 bit Syncword */ + PutBit(bitStream, hEncoder->config.mpegVersion, 1); /* ID == 0 for MPEG4 AAC, 1 for MPEG2 AAC */ + PutBit(bitStream, 0, 2); /* layer == 0 */ + PutBit(bitStream, 1, 1); /* protection absent */ + PutBit(bitStream, hEncoder->config.aacObjectType - 1, 2); /* profile */ + PutBit(bitStream, hEncoder->sampleRateIdx, 4); /* sampling rate */ + PutBit(bitStream, 0, 1); /* private bit */ + PutBit(bitStream, hEncoder->numChannels, 3); /* ch. config (must be > 0) */ + /* simply using numChannels only works for + 6 channels or less, else a channel + configuration should be written */ + PutBit(bitStream, 0, 1); /* original/copy */ + PutBit(bitStream, 0, 1); /* home */ + +#if 0 // Removed in corrigendum 14496-3:2002 + if (hEncoder->config.mpegVersion == 0) + PutBit(bitStream, 0, 2); /* emphasis */ +#endif + + /* Variable ADTS header */ + PutBit(bitStream, 0, 1); /* copyr. id. bit */ + PutBit(bitStream, 0, 1); /* copyr. id. start */ + PutBit(bitStream, hEncoder->usedBytes, 13); + PutBit(bitStream, 0x7FF, 11); /* buffer fullness (0x7FF for VBR) */ + PutBit(bitStream, 0, 2); /* raw data blocks (0+1=1) */ + + } + + /* + * MPEG2 says byte_aligment() here, but ADTS always is multiple of 8 bits + * MPEG4 has no byte_alignment() here + */ + /* + if (hEncoder->config.mpegVersion == 1) + bits += ByteAlign(bitStream, writeFlag); + */ + +#if 0 // Removed in corrigendum 14496-3:2002 + if (hEncoder->config.mpegVersion == 0) + bits += 2; /* emphasis */ +#endif + + return bits; +} + +static int WriteCPE(CoderInfo *coderInfoL, + CoderInfo *coderInfoR, + ChannelInfo *channelInfo, + BitStream* bitStream, + int objectType, + int writeFlag) +{ + int bits = 0; + +#ifndef DRM + if (writeFlag) { + /* write ID_CPE, single_element_channel() identifier */ + PutBit(bitStream, ID_CPE, LEN_SE_ID); + + /* write the element_identifier_tag */ + PutBit(bitStream, channelInfo->tag, LEN_TAG); + + /* common_window? */ + PutBit(bitStream, channelInfo->common_window, LEN_COM_WIN); + } + + bits += LEN_SE_ID; + bits += LEN_TAG; + bits += LEN_COM_WIN; +#endif + + /* if common_window, write ics_info */ + if (channelInfo->common_window) { + int numWindows, maxSfb; + + bits += WriteICSInfo(coderInfoL, bitStream, objectType, channelInfo->common_window, writeFlag); + numWindows = coderInfoL->num_window_groups; + maxSfb = coderInfoL->max_sfb; + + if (writeFlag) { + PutBit(bitStream, channelInfo->msInfo.is_present, LEN_MASK_PRES); + if (channelInfo->msInfo.is_present == 1) { + int g; + int b; + for (g=0;gmsInfo.ms_used[g*maxSfb+b], LEN_MASK); + } + } + } + } + bits += LEN_MASK_PRES; + if (channelInfo->msInfo.is_present == 1) + bits += (numWindows*maxSfb*LEN_MASK); + } + + /* Write individual_channel_stream elements */ + bits += WriteICS(coderInfoL, bitStream, channelInfo->common_window, objectType, writeFlag); + bits += WriteICS(coderInfoR, bitStream, channelInfo->common_window, objectType, writeFlag); + + return bits; +} + +static int WriteSCE(CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int objectType, + int writeFlag) +{ + int bits = 0; + +#ifndef DRM + if (writeFlag) { + /* write Single Element Channel (SCE) identifier */ + PutBit(bitStream, ID_SCE, LEN_SE_ID); + + /* write the element identifier tag */ + PutBit(bitStream, channelInfo->tag, LEN_TAG); + } + + bits += LEN_SE_ID; + bits += LEN_TAG; +#endif + + /* Write an Individual Channel Stream element */ + bits += WriteICS(coderInfo, bitStream, 0, objectType, writeFlag); + + return bits; +} + +static int WriteLFE(CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int objectType, + int writeFlag) +{ + int bits = 0; + + if (writeFlag) { + /* write ID_LFE, lfe_element_channel() identifier */ + PutBit(bitStream, ID_LFE, LEN_SE_ID); + + /* write the element_identifier_tag */ + PutBit(bitStream, channelInfo->tag, LEN_TAG); + } + + bits += LEN_SE_ID; + bits += LEN_TAG; + + /* Write an individual_channel_stream element */ + bits += WriteICS(coderInfo, bitStream, 0, objectType, writeFlag); + + return bits; +} + +static int WriteICSInfo(CoderInfo *coderInfo, + BitStream *bitStream, + int objectType, + int common_window, + int writeFlag) +{ + int grouping_bits; + int bits = 0; + + if (writeFlag) { + /* write out ics_info() information */ + PutBit(bitStream, 0, LEN_ICS_RESERV); /* reserved Bit*/ + + /* Write out window sequence */ + PutBit(bitStream, coderInfo->block_type, LEN_WIN_SEQ); /* block type */ + + /* Write out window shape */ + PutBit(bitStream, coderInfo->window_shape, LEN_WIN_SH); /* window shape */ + } + + bits += LEN_ICS_RESERV; + bits += LEN_WIN_SEQ; + bits += LEN_WIN_SH; + + /* For short windows, write out max_sfb and scale_factor_grouping */ + if (coderInfo->block_type == ONLY_SHORT_WINDOW){ + if (writeFlag) { + PutBit(bitStream, coderInfo->max_sfb, LEN_MAX_SFBS); + grouping_bits = FindGroupingBits(coderInfo); + PutBit(bitStream, grouping_bits, MAX_SHORT_WINDOWS - 1); /* the grouping bits */ + } + bits += LEN_MAX_SFBS; + bits += MAX_SHORT_WINDOWS - 1; + } else { /* Otherwise, write out max_sfb and predictor data */ + if (writeFlag) { + PutBit(bitStream, coderInfo->max_sfb, LEN_MAX_SFBL); + } + bits += LEN_MAX_SFBL; +#ifdef DRM + } + if (writeFlag) { + PutBit(bitStream,coderInfo->tnsInfo.tnsDataPresent,LEN_TNS_PRES); + } + bits += LEN_TNS_PRES; +#endif + if (objectType == LTP) + { + bits++; + if(writeFlag) + PutBit(bitStream, coderInfo->ltpInfo.global_pred_flag, 1); /* Prediction Global used */ + + bits += WriteLTPPredictorData(coderInfo, bitStream, writeFlag); + if (common_window) + bits += WriteLTPPredictorData(coderInfo, bitStream, writeFlag); + } else { + bits++; + if (writeFlag) + PutBit(bitStream, coderInfo->pred_global_flag, LEN_PRED_PRES); /* predictor_data_present */ + + bits += WritePredictorData(coderInfo, bitStream, writeFlag); + } +#ifndef DRM + } +#endif + + return bits; +} + +static int WriteICS(CoderInfo *coderInfo, + BitStream *bitStream, + int commonWindow, + int objectType, + int writeFlag) +{ + /* this function writes out an individual_channel_stream to the bitstream and */ + /* returns the number of bits written to the bitstream */ + int bits = 0; + +#ifndef DRM + /* Write the 8-bit global_gain */ + if (writeFlag) + PutBit(bitStream, coderInfo->global_gain, LEN_GLOB_GAIN); + bits += LEN_GLOB_GAIN; +#endif + + /* Write ics information */ + if (!commonWindow) { + bits += WriteICSInfo(coderInfo, bitStream, objectType, commonWindow, writeFlag); + } + +#ifdef DRM + /* Write the 8-bit global_gain */ + if (writeFlag) + PutBit(bitStream, coderInfo->global_gain, LEN_GLOB_GAIN); + bits += LEN_GLOB_GAIN; +#endif + + bits += SortBookNumbers(coderInfo, bitStream, writeFlag); + bits += WriteScalefactors(coderInfo, bitStream, writeFlag); +#ifdef DRM + if (writeFlag) { + /* length_of_reordered_spectral_data */ + PutBit(bitStream, coderInfo->iLenReordSpData, LEN_HCR_REORDSD); + + /* length_of_longest_codeword */ + PutBit(bitStream, coderInfo->iLenLongestCW, LEN_HCR_LONGCW); + } + bits += LEN_HCR_REORDSD + LEN_HCR_LONGCW; +#else + bits += WritePulseData(coderInfo, bitStream, writeFlag); +#endif + bits += WriteTNSData(coderInfo, bitStream, writeFlag); +#ifndef DRM + bits += WriteGainControlData(coderInfo, bitStream, writeFlag); +#endif + +#ifdef DRM + /* DRM CRC calculation */ + if (writeFlag) + calc_CRC(bitStream, bits); + + bits += WriteReorderedSpectralData(coderInfo, bitStream, writeFlag); +#else + bits += WriteSpectralData(coderInfo, bitStream, writeFlag); +#endif + + /* Return number of bits */ + return bits; +} + +static int WriteLTPPredictorData(CoderInfo *coderInfo, BitStream *bitStream, int writeFlag) +{ + int i, last_band; + int bits; + LtpInfo *ltpInfo = &coderInfo->ltpInfo; + + bits = 0; + + if (ltpInfo->global_pred_flag) + { + + if(writeFlag) + PutBit(bitStream, 1, 1); /* LTP used */ + bits++; + + switch(coderInfo->block_type) + { + case ONLY_LONG_WINDOW: + case LONG_SHORT_WINDOW: + case SHORT_LONG_WINDOW: + bits += LEN_LTP_LAG; + bits += LEN_LTP_COEF; + if(writeFlag) + { + PutBit(bitStream, ltpInfo->delay[0], LEN_LTP_LAG); + PutBit(bitStream, ltpInfo->weight_idx, LEN_LTP_COEF); + } + + last_band = ((coderInfo->nr_of_sfb < MAX_LT_PRED_LONG_SFB) ? + coderInfo->nr_of_sfb : MAX_LT_PRED_LONG_SFB); +// last_band = coderInfo->nr_of_sfb; + + bits += last_band; + if(writeFlag) + for (i = 0; i < last_band; i++) + PutBit(bitStream, ltpInfo->sfb_prediction_used[i], LEN_LTP_LONG_USED); + break; + + default: + break; + } + } + + return (bits); +} + +static int WritePredictorData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag) +{ + int bits = 0; + + /* Write global predictor data present */ + short predictorDataPresent = coderInfo->pred_global_flag; + int numBands = min(coderInfo->max_pred_sfb, coderInfo->nr_of_sfb); + + if (writeFlag) { + if (predictorDataPresent) { + int b; + if (coderInfo->reset_group_number == -1) { + PutBit(bitStream, 0, LEN_PRED_RST); /* No prediction reset */ + } else { + PutBit(bitStream, 1, LEN_PRED_RST); + PutBit(bitStream, (unsigned long)coderInfo->reset_group_number, + LEN_PRED_RSTGRP); + } + + for (b=0;bpred_sfb_flag[b], LEN_PRED_ENAB); + } + } + } + bits += (predictorDataPresent) ? + (LEN_PRED_RST + + ((coderInfo->reset_group_number)!=-1)*LEN_PRED_RSTGRP + + numBands*LEN_PRED_ENAB) : 0; + + return bits; +} + +static int WritePulseData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag) +{ + int bits = 0; + + if (writeFlag) { + PutBit(bitStream, 0, LEN_PULSE_PRES); /* no pulse_data_present */ + } + + bits += LEN_PULSE_PRES; + + return bits; +} + +static int WriteTNSData(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag) +{ + int bits = 0; + int numWindows; + int len_tns_nfilt; + int len_tns_length; + int len_tns_order; + int filtNumber; + int resInBits; + int bitsToTransmit; + unsigned long unsignedIndex; + int w; + + TnsInfo* tnsInfoPtr = &coderInfo->tnsInfo; + +#ifndef DRM + if (writeFlag) { + PutBit(bitStream,tnsInfoPtr->tnsDataPresent,LEN_TNS_PRES); + } + bits += LEN_TNS_PRES; +#endif + + /* If TNS is not present, bail */ + if (!tnsInfoPtr->tnsDataPresent) { + return bits; + } + + /* Set window-dependent TNS parameters */ + if (coderInfo->block_type == ONLY_SHORT_WINDOW) { + numWindows = MAX_SHORT_WINDOWS; + len_tns_nfilt = LEN_TNS_NFILTS; + len_tns_length = LEN_TNS_LENGTHS; + len_tns_order = LEN_TNS_ORDERS; + } + else { + numWindows = 1; + len_tns_nfilt = LEN_TNS_NFILTL; + len_tns_length = LEN_TNS_LENGTHL; + len_tns_order = LEN_TNS_ORDERL; + } + + /* Write TNS data */ + bits += (numWindows * len_tns_nfilt); + for (w=0;wwindowData[w]; + int numFilters = windowDataPtr->numFilters; + if (writeFlag) { + PutBit(bitStream,numFilters,len_tns_nfilt); /* n_filt[] = 0 */ + } + if (numFilters) { + bits += LEN_TNS_COEFF_RES; + resInBits = windowDataPtr->coefResolution; + if (writeFlag) { + PutBit(bitStream,resInBits-DEF_TNS_RES_OFFSET,LEN_TNS_COEFF_RES); + } + bits += numFilters * (len_tns_length+len_tns_order); + for (filtNumber=0;filtNumbertnsFilter[filtNumber]; + int order = tnsFilterPtr->order; + if (writeFlag) { + PutBit(bitStream,tnsFilterPtr->length,len_tns_length); + PutBit(bitStream,order,len_tns_order); + } + if (order) { + bits += (LEN_TNS_DIRECTION + LEN_TNS_COMPRESS); + if (writeFlag) { + PutBit(bitStream,tnsFilterPtr->direction,LEN_TNS_DIRECTION); + PutBit(bitStream,tnsFilterPtr->coefCompress,LEN_TNS_COMPRESS); + } + bitsToTransmit = resInBits - tnsFilterPtr->coefCompress; + bits += order * bitsToTransmit; + if (writeFlag) { + int i; + for (i=1;i<=order;i++) { + unsignedIndex = (unsigned long) (tnsFilterPtr->index[i])&(~(~0<data; + int* len = coderInfo->len; + + if (writeFlag) { + for(i = 0; i < coderInfo->spectral_count; i++) { + if (len[i] > 0) { /* only send out non-zero codebook data */ + PutBit(bitStream, data[i], len[i]); /* write data */ + bits += len[i]; + } + } + } else { + for(i = 0; i < coderInfo->spectral_count; i++) { + bits += len[i]; + } + } + + return bits; +} + +static int WriteAACFillBits(BitStream* bitStream, + int numBits, + int writeFlag) +{ + int numberOfBitsLeft = numBits; + + /* Need at least (LEN_SE_ID + LEN_F_CNT) bits for a fill_element */ + int minNumberOfBits = LEN_SE_ID + LEN_F_CNT; + + while (numberOfBitsLeft >= minNumberOfBits) + { + int numberOfBytes; + int maxCount; + + if (writeFlag) { + PutBit(bitStream, ID_FIL, LEN_SE_ID); /* Write fill_element ID */ + } + numberOfBitsLeft -= minNumberOfBits; /* Subtract for ID,count */ + + numberOfBytes = (int)(numberOfBitsLeft/LEN_BYTE); + maxCount = (1< maxNumberOfBytes ) ? (maxNumberOfBytes) : (numberOfBytes); + escCount = numberOfBytes - maxCount; + if (writeFlag) { + PutBit(bitStream, escCount, LEN_BYTE); + for (i = 0; i < numberOfBytes-1; i++) { + PutBit(bitStream, 0, LEN_BYTE); + } + } + } + numberOfBitsLeft -= LEN_BYTE*numberOfBytes; + } + + return numberOfBitsLeft; +} + +static int FindGroupingBits(CoderInfo *coderInfo) +{ + /* This function inputs the grouping information and outputs the seven bit + 'grouping_bits' field that the AAC decoder expects. */ + + int grouping_bits = 0; + int tmp[8]; + int i, j; + int index = 0; + + for(i = 0; i < coderInfo->num_window_groups; i++){ + for (j = 0; j < coderInfo->window_group_length[i]; j++){ + tmp[index++] = i; + } + } + + for(i = 1; i < 8; i++){ + grouping_bits = grouping_bits << 1; + if(tmp[i] == tmp[i-1]) { + grouping_bits++; + } + } + + return grouping_bits; +} + +/* size in bytes! */ +BitStream *OpenBitStream(int size, unsigned char *buffer) +{ + BitStream *bitStream; + + bitStream = AllocMemory(sizeof(BitStream)); + bitStream->size = size; +#ifdef DRM + /* skip first byte for CRC */ + bitStream->numBit = 8; + bitStream->currentBit = 8; +#else + bitStream->numBit = 0; + bitStream->currentBit = 0; +#endif + bitStream->data = buffer; + SetMemory(bitStream->data, 0, size); + + return bitStream; +} + +int CloseBitStream(BitStream *bitStream) +{ + int bytes = bit2byte(bitStream->numBit); + + FreeMemory(bitStream); + + return bytes; +} + +static long BufferNumBit(BitStream *bitStream) +{ + return bitStream->numBit; +} + +static int WriteByte(BitStream *bitStream, + unsigned long data, + int numBit) +{ + long numUsed,idx; + + idx = (bitStream->currentBit / BYTE_NUMBIT) % bitStream->size; + numUsed = bitStream->currentBit % BYTE_NUMBIT; +#ifndef DRM + if (numUsed == 0) + bitStream->data[idx] = 0; +#endif + bitStream->data[idx] |= (data & ((1<currentBit += numBit; + bitStream->numBit = bitStream->currentBit; + + return 0; +} + +int PutBit(BitStream *bitStream, + unsigned long data, + int numBit) +{ + int num,maxNum,curNum; + unsigned long bits; + + if (numBit == 0) + return 0; + + /* write bits in packets according to buffer byte boundaries */ + num = 0; + maxNum = BYTE_NUMBIT - bitStream->currentBit % BYTE_NUMBIT; + while (num < numBit) { + curNum = min(numBit-num,maxNum); + bits = data>>(numBit-num-curNum); + if (WriteByte(bitStream, bits, curNum)) { + return 1; + } + num += curNum; + maxNum = BYTE_NUMBIT; + } + + return 0; +} + +static int ByteAlign(BitStream *bitStream, int writeFlag, int bitsSoFar) +{ + int len, i,j; + + if (writeFlag) + { + len = BufferNumBit(bitStream); + } else { + len = bitsSoFar; + } + + j = (8 - (len%8))%8; + + if ((len % 8) == 0) j = 0; + if (writeFlag) { + for( i=0; icurrentBit = curpos; + return PutBit(bitStream, data, numBit); +} + +static int rewind_word(int W, int len) +{ /* rewind len (max. 32) bits so that the MSB becomes LSB */ + short i; + int tmp_W = 0; + + for (i = 0; i < len; i++) { + tmp_W <<= 1; + if (W & (1<window_group_length; + int* sfb_offset = coderInfo->sfb_offset; + + cw_info_t cw_info[FRAME_LEN]; + cw_info_t cw_info_preso[FRAME_LEN]; + + int num_cw = coderInfo->cur_cw; + int window_cw_cnt[MAX_SHORT_WINDOWS] = {0,0,0,0,0,0,0,0}; + + int presort, set, num_sets; + + unsigned short cur_cb, cw_cnt; + short is_backwards; + int diff, tmp_data, cw_part_cnt, cur_cw_part; + + int cur_cw_len, cur_data; + int sfb_cnt, win_cnt, acc_win_cnt, win_grp_cnt; + int coeff_cnt, last_sfb, cur_sfb_len; + + /* set up local pointers to data and len */ + /* data array contains data to be written */ + /* len array contains lengths of data words */ + int* data = coderInfo->data; + int* len = coderInfo->len; + int* num_data = coderInfo->num_data_cw; + + if (writeFlag) { + /* build offset table */ + cur_data = 0; + cw_info[0].cw_offset = 0; + for (i = 0; i < num_cw; i++) { + cur_cw_len = 0; + for (j = 0; j < num_data[i]; j++) { + cur_cw_len += len[cur_data++]; + } + + cw_info[i].num_data = num_data[i]; + cw_info[i].cw_len = cur_cw_len; + if (i > 0) /* calculate offset (codeword info parameter) */ + cw_info[i].cw_offset = cw_info[i - 1].cw_offset + num_data[i - 1]; + } + + /* presort codewords ------------------------------------------------ */ + /* classify codewords first */ + sfb_cnt = win_cnt = win_grp_cnt = coeff_cnt = last_sfb = acc_win_cnt = 0; + cur_sfb_len = sfb_offset[1] / window_group_length[0]; + cur_cb = coderInfo->book_vector[0]; + for (i = 0; i < num_cw; i++) { + /* Set codeword info parameters */ + cw_info[i].cb = cur_cb; + cw_info[i].num_sl_cw = (cur_cb < FIRST_PAIR_HCB) ? QUAD_LEN : PAIR_LEN; + + cw_info[i].window = acc_win_cnt + win_cnt; + cw_info[i].cw_nr = window_cw_cnt[cw_info[i].window]; + window_cw_cnt[cw_info[i].window]++; + + coeff_cnt += cw_info[i].num_sl_cw; + if (coeff_cnt - last_sfb >= cur_sfb_len) { + last_sfb += cur_sfb_len; + + win_cnt++; /* next window */ + if (win_cnt == window_group_length[win_grp_cnt]) { + win_cnt = 0; + + sfb_cnt++; /* next sfb */ + if (sfb_cnt == coderInfo->all_sfb) { + sfb_cnt = 0; + + acc_win_cnt += window_group_length[win_grp_cnt]; + win_grp_cnt++; /* next window group */ + } + + /* new codebook and sfb length */ + cur_cb = coderInfo->book_vector[sfb_cnt]; + if (last_sfb < FRAME_LEN) { + cur_sfb_len = (sfb_offset[sfb_cnt + 1] - sfb_offset[sfb_cnt]) + / window_group_length[win_grp_cnt]; + } + } + } + } + + /* presorting (first presorting step) */ + /* only needed for short windows */ + +/* Somehow the second presorting step does not give expected results. Disabling the + following code surprisingly gives good results. TODO: find the bug */ + if (0) {//coderInfo->block_type == ONLY_SHORT_WINDOW) { + for (i = 0; i < MAX_SHORT_WINDOWS; i++) + window_cw_cnt[i] = 0; /* reset all counters */ + + win_cnt = 0; + cw_cnt = 0; + for (i = 0; i < num_cw; i++) { + for (j = 0; j < num_cw; j++) { + if (cw_info[j].window == win_cnt) { + if (cw_info[j].cw_nr == window_cw_cnt[win_cnt]) { + cw_info_preso[cw_cnt++] = cw_info[j]; + window_cw_cnt[win_cnt]++; + + /* check if two one-dimensional codewords */ + if (cw_info[j].num_sl_cw == PAIR_LEN) { + cw_info_preso[cw_cnt++] = cw_info[j + 1]; + window_cw_cnt[win_cnt]++; + } + + win_cnt++; /* next window */ + if (win_cnt == MAX_SHORT_WINDOWS) + win_cnt = 0; + } + } + } + } + } else { + for (i = 0; i < num_cw; i++) { + cw_info_preso[i] = cw_info[i]; /* just copy */ + } + } + + /* presorting (second presorting step) */ + cw_cnt = 0; + for (presort = 0; presort < LEN_PRESORT_CODEBOOK; presort++) { + /* next codebook that has to be processed according to presorting */ + unsigned short nextCB = PresortedCodebook_VCB11[presort]; + + for (i = 0; i < num_cw; i++) { + /* process only codewords that are due now */ + if ((cw_info_preso[i].cb == nextCB) || + ((nextCB < ESC_HCB) && (cw_info_preso[i].cb == nextCB + 1))) + { + cw_info[cw_cnt++] = cw_info_preso[i]; + } + } + } + + /* init segments */ + accsegmsize = 0; + for (i = 0; i < num_cw; i++) { + /* 8.5.3.3.3.2 Derivation of segment width */ + cursegmsize = min(maxCwLen[cw_info[i].cb], coderInfo->iLenLongestCW); + + if (accsegmsize + cursegmsize > coderInfo->iLenReordSpData) { + /* the last segment is extended until iLenReordSpData */ + segment[segmcnt - 1].right = coderInfo->iLenReordSpData - 1; + segment[segmcnt - 1].len = coderInfo->iLenReordSpData - segment[segmcnt - 1].left; + break; + } + + segment[segmcnt].left = accsegmsize; + segment[segmcnt].right = accsegmsize + cursegmsize - 1; + segment[segmcnt++].len = cursegmsize; + accsegmsize += cursegmsize; + } + + /* store current bit position */ + startbitpos = bitStream->currentBit; + + /* write write priority codewords (PCWs) and nonPCWs ---------------- */ + num_sets = num_cw / segmcnt; /* number of sets */ + + for (set = 0; set <= num_sets; set++) { + int trial; + + /* ever second set the bit order is reversed */ + is_backwards = set % 2; + + for (trial = 0; trial < segmcnt; trial++) { + int codewordBase; + int set_encoded = segmcnt; + + if (set == num_sets) + set_encoded = num_cw - set * segmcnt; /* last set is shorter than the rest */ + + for (codewordBase = 0; codewordBase < segmcnt; codewordBase++) { + int segment_index = (trial + codewordBase) % segmcnt; + int codeword_index = codewordBase + set * segmcnt; + + if (codeword_index >= num_cw) + break; + + if ((cw_info[codeword_index].cw_len > 0) && (segment[segment_index].len > 0)) { + /* codeword is not yet written (completely) */ + /* space left in this segment */ + short tmplen; + + /* how many bits can be written? */ + if (segment[segment_index].len >= cw_info[codeword_index].cw_len) { + tmplen = cw_info[codeword_index].cw_len; + set_encoded--; /* CW fits into segment */ + } else { + tmplen = segment[segment_index].len; + } + + /* Adjust lengths */ + cw_info[codeword_index].cw_len -= tmplen; + segment[segment_index].len -= tmplen; + + /* write codewords to bitstream */ + for (cw_part_cnt = 0; cw_part_cnt < cw_info[codeword_index].num_data; cw_part_cnt++) { + cur_cw_part = cw_info[codeword_index].cw_offset + cw_part_cnt; + + if (len[cur_cw_part] <= tmplen) { + /* write complete data, no partitioning */ + if (is_backwards) { + /* write data in reversed bit-order */ + PutBitHcr(bitStream, startbitpos + segment[segment_index].right - len[cur_cw_part] + 1, + rewind_word(data[cur_cw_part], len[cur_cw_part]), len[cur_cw_part]); + + segment[segment_index].right -= len[cur_cw_part]; + } else { + PutBitHcr(bitStream, startbitpos + segment[segment_index].left, + data[cur_cw_part], len[cur_cw_part]); + + segment[segment_index].left += len[cur_cw_part]; + } + + tmplen -= len[cur_cw_part]; + len[cur_cw_part] = 0; + } else { + /* codeword part must be partitioned */ + /* data must be taken from the left side */ + tmp_data = data[cur_cw_part]; + + diff = len[cur_cw_part] - tmplen; + tmp_data >>= diff; + + /* remove bits which are already used */ + data[cur_cw_part] &= (1 << diff) - 1 /* diff number of ones */; + len[cur_cw_part] = diff; + + if (is_backwards) { + /* write data in reversed bit-order */ + PutBitHcr(bitStream, startbitpos + segment[segment_index].right - tmplen + 1, + rewind_word(tmp_data, tmplen), tmplen); + + segment[segment_index].right -= tmplen; + } else { + PutBitHcr(bitStream, startbitpos + segment[segment_index].left, + tmp_data, tmplen); + + segment[segment_index].left += tmplen; + } + + tmplen = 0; + } + + if (tmplen == 0) + break; /* all data written for this segment trial */ + } + } + } /* of codewordBase */ + + if (set_encoded == 0) + break; /* no unencoded codewords left in this set */ + } /* of trial */ + } + + /* set parameter for bit stream to current correct position */ + bitStream->currentBit = startbitpos + coderInfo->iLenReordSpData; + bitStream->numBit = bitStream->currentBit; + } + + return coderInfo->iLenReordSpData; +} + +/* + CRC8 x^8 + x^4 + x^3 + x^2 + 1 +*/ +static const unsigned char _crctable[256] = +{ + 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, + 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, + 0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E, + 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, + 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4, + 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, + 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, + 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, + 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, + 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, + 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, + 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65, + 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, + 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, + 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, + 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, + 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75, + 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, + 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, + 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, + 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, + 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, + 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, + 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7, + 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, + 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, + 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, + 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, + 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1, + 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, + 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, + 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4 +}; + +static void calc_CRC(BitStream *bitStream, int len) +{ + //int i; + //unsigned char r = ~0; /* Initialize to all ones */ + unsigned char crc = ~0; /* Initialize to all ones */ + + /* CRC polynome used x^8 + x^4 + x^3 + x^2 +1 */ + + unsigned int cb = len / 8; + unsigned int taillen = len & 0x7; + unsigned char* pb = &bitStream->data[1]; + //compatible, but slower unsigned char b = ( bitStream->data[cb + 1] ) >> ( 8 - taillen ); + unsigned char b = bitStream->data[cb + 1]; + +//#define GPOLY 0435 +// +// for (i = 8; i < len + 8; i++) { +// r = ( (r << 1) ^ (( ( +// ( bitStream->data[i / 8] >> (7 - (i % 8)) ) +// & 1) ^ ((r >> 7) & 1)) * GPOLY )) & 0xFF; +// } + +#define GP 0x1d + +//fprintf( stderr, "\nfaac:" ); + + while ( cb-- ) + { +//fprintf( stderr, " %02X", *pb ); + crc = _crctable[ crc ^ *pb++ ]; + } + + //compatible, but slower switch ( taillen ) + //{ + //case 7: + // crc = ( ( crc << 1 ) ^ ( ( ( ( b >> 6 ) & 1 ) ^ ( ( crc >> 7 ) & 1 ) ) * GP ) ) & 0xFF; + // // goto next case + //case 6: + // crc = ( ( crc << 1 ) ^ ( ( ( ( b >> 5 ) & 1 ) ^ ( ( crc >> 7 ) & 1 ) ) * GP ) ) & 0xFF; + // // goto next case + //case 5: + // crc = ( ( crc << 1 ) ^ ( ( ( ( b >> 4 ) & 1 ) ^ ( ( crc >> 7 ) & 1 ) ) * GP ) ) & 0xFF; + // // goto next case + //case 4: + // crc = ( ( crc << 1 ) ^ ( ( ( ( b >> 3 ) & 1 ) ^ ( ( crc >> 7 ) & 1 ) ) * GP ) ) & 0xFF; + // // goto next case + //case 3: + // crc = ( ( crc << 1 ) ^ ( ( ( ( b >> 2 ) & 1 ) ^ ( ( crc >> 7 ) & 1 ) ) * GP ) ) & 0xFF; + // // goto next case + //case 2: + // crc = ( ( crc << 1 ) ^ ( ( ( ( b >> 1 ) & 1 ) ^ ( ( crc >> 7 ) & 1 ) ) * GP ) ) & 0xFF; + // // goto next case + //case 1: + // crc = ( ( crc << 1 ) ^ ( ( ( b & 1 ) ^ ( ( crc >> 7 ) & 1 ) ) * GP ) ) & 0xFF; + // break; + //} +//fprintf( stderr, " %02X", ( b >> ( 8 - taillen ) ) << 7 ); + switch ( taillen ) + { + case 7: + crc = ( ( crc << 1 ) ^ ( ( (signed char)( b ^ crc ) >> 7 ) & GP ) ) & 0xFF; + b <<= 1; + // goto next case + case 6: + crc = ( ( crc << 1 ) ^ ( ( (signed char)( b ^ crc ) >> 7 ) & GP ) ) & 0xFF; + b <<= 1; + // goto next case + case 5: + crc = ( ( crc << 1 ) ^ ( ( (signed char)( b ^ crc ) >> 7 ) & GP ) ) & 0xFF; + b <<= 1; + // goto next case + case 4: + crc = ( ( crc << 1 ) ^ ( ( (signed char)( b ^ crc ) >> 7 ) & GP ) ) & 0xFF; + b <<= 1; + // goto next case + case 3: + crc = ( ( crc << 1 ) ^ ( ( (signed char)( b ^ crc ) >> 7 ) & GP ) ) & 0xFF; + b <<= 1; + // goto next case + case 2: + crc = ( ( crc << 1 ) ^ ( ( (signed char)( b ^ crc ) >> 7 ) & GP ) ) & 0xFF; + b <<= 1; + // goto next case + case 1: + crc = ( ( crc << 1 ) ^ ( ( (signed char)( b ^ crc ) >> 7 ) & GP ) ) & 0xFF; + break; + } + + //if ( crc != r ) + //{ + // fprintf( stderr, "%08X != %08X\n", crc, r ); + //} +//fprintf( stderr, " (%5d bits), CRC is %02X\n", len, ~crc & 0xFF ); + + /* CRC is stored inverted, per definition at first byte in stream */ + bitStream->data[0] = ~crc; +} +#endif diff --git a/external/libfaac/libfaac/bitstream.h b/external/libfaac/libfaac/bitstream.h new file mode 100644 index 0000000..b56e1a5 --- /dev/null +++ b/external/libfaac/libfaac/bitstream.h @@ -0,0 +1,172 @@ +/********************************************************************** +MPEG-4 Audio VM +Bit stream module + + + +This software module was originally developed by + +Heiko Purnhagen (University of Hannover) + +and edited by + +in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard +ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an +implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools +as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives +users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this +software module or modifications thereof for use in hardware or +software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio +standards. Those intending to use this software module in hardware or +software products are advised that this use may infringe existing +patents. The original developer of this software module and his/her +company, the subsequent editors and their companies, and ISO/IEC have +no liability for use of this software module or modifications thereof +in an implementation. Copyright is not released for non MPEG-2 +NBC/MPEG-4 Audio conforming products. The original developer retains +full right to use the code for his/her own purpose, assign or donate +the code to a third party and to inhibit third party from using the +code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This +copyright notice must be included in all copies or derivative works. + +Copyright (c) 1996. +**********************************************************************/ +/* + * $Id: bitstream.h,v 1.14 2004/07/04 12:10:52 corrados Exp $ + */ + +#ifndef BITSTREAM_H +#define BITSTREAM_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "frame.h" +#include "coder.h" +#include "channels.h" + +/* + * Raw bitstream constants + */ +#define LEN_SE_ID 3 +#define LEN_TAG 4 +#define LEN_GLOB_GAIN 8 +#define LEN_COM_WIN 1 +#define LEN_ICS_RESERV 1 +#define LEN_WIN_SEQ 2 +#define LEN_WIN_SH 1 +#define LEN_MAX_SFBL 6 +#define LEN_MAX_SFBS 4 +#define LEN_CB 4 +#define LEN_SCL_PCM 8 +#define LEN_PRED_PRES 1 +#define LEN_PRED_RST 1 +#define LEN_PRED_RSTGRP 5 +#define LEN_PRED_ENAB 1 +#define LEN_MASK_PRES 2 +#define LEN_MASK 1 +#define LEN_PULSE_PRES 1 + +#define LEN_TNS_PRES 1 +#define LEN_TNS_NFILTL 2 +#define LEN_TNS_NFILTS 1 +#define LEN_TNS_COEFF_RES 1 +#define LEN_TNS_LENGTHL 6 +#define LEN_TNS_LENGTHS 4 +#define LEN_TNS_ORDERL 5 +#define LEN_TNS_ORDERS 3 +#define LEN_TNS_DIRECTION 1 +#define LEN_TNS_COMPRESS 1 +#define LEN_GAIN_PRES 1 + +#define LEN_NEC_NPULSE 2 +#define LEN_NEC_ST_SFB 6 +#define LEN_NEC_POFF 5 +#define LEN_NEC_PAMP 4 +#define NUM_NEC_LINES 4 +#define NEC_OFFSET_AMP 4 + +#define LEN_NCC 3 +#define LEN_IS_CPE 1 +#define LEN_CC_LR 1 +#define LEN_CC_DOM 1 +#define LEN_CC_SGN 1 +#define LEN_CCH_GES 2 +#define LEN_CCH_CGP 1 +#define LEN_D_CNT 4 +#define LEN_D_ESC 12 +#define LEN_F_CNT 4 +#define LEN_F_ESC 8 +#define LEN_BYTE 8 +#define LEN_PAD_DATA 8 + +#define LEN_PC_COMM 8 + +#ifdef DRM +# define LEN_HCR_REORDSD 14 +# define LEN_HCR_LONGCW 6 +# define FIRST_PAIR_HCB 5 +# define QUAD_LEN 4 +# define PAIR_LEN 2 +# define ESC_HCB 11 +#endif + +#define ID_SCE 0 +#define ID_CPE 1 +#define ID_CCE 2 +#define ID_LFE 3 +#define ID_DSE 4 +#define ID_PCE 5 +#define ID_FIL 6 +#define ID_END 7 + + +/* MPEG ID's */ +#define MPEG2 1 +#define MPEG4 0 + +/* AAC object types */ +#define MAIN 1 +#define LOW 2 +#define SSR 3 +#define LTP 4 + + +#define BYTE_NUMBIT 8 /* bits in byte (char) */ +#define LONG_NUMBIT 32 /* bits in unsigned long */ +#define bit2byte(a) (((a)+BYTE_NUMBIT-1)/BYTE_NUMBIT) + + +typedef struct +{ + unsigned char *data; /* data bits */ + long numBit; /* number of bits in buffer */ + long size; /* buffer size in bytes */ + long currentBit; /* current bit position in bit stream */ + long numByte; /* number of bytes read/written (only file) */ +} BitStream; + + + +int WriteBitstream(faacEncHandle hEncoder, + CoderInfo *coderInfo, + ChannelInfo *channelInfo, + BitStream *bitStream, + int numChannels); + + +BitStream *OpenBitStream(int size, unsigned char *buffer); + +int CloseBitStream(BitStream *bitStream); + +int PutBit(BitStream *bitStream, + unsigned long data, + int numBit); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* BITSTREAM_H */ + diff --git a/external/libfaac/libfaac/channels.c b/external/libfaac/libfaac/channels.c new file mode 100644 index 0000000..c205b02 --- /dev/null +++ b/external/libfaac/libfaac/channels.c @@ -0,0 +1,110 @@ +/************************* MPEG-2 NBC Audio Decoder ************************** + * * +"This software module was originally developed in the course of +development of the MPEG-2 NBC/MPEG-4 Audio standard ISO/IEC 13818-7, +14496-1,2 and 3. This software module is an implementation of a part of one or more +MPEG-2 NBC/MPEG-4 Audio tools as specified by the MPEG-2 NBC/MPEG-4 +Audio standard. ISO/IEC gives users of the MPEG-2 NBC/MPEG-4 Audio +standards free license to this software module or modifications thereof for use in +hardware or software products claiming conformance to the MPEG-2 NBC/MPEG-4 +Audio standards. Those intending to use this software module in hardware or +software products are advised that this use may infringe existing patents. +The original developer of this software module and his/her company, the subsequent +editors and their companies, and ISO/IEC have no liability for use of this software +module or modifications thereof in an implementation. Copyright is not released for +non MPEG-2 NBC/MPEG-4 Audio conforming products.The original developer +retains full right to use the code for his/her own purpose, assign or donate the +code to a third party and to inhibit third party from using the code for non +MPEG-2 NBC/MPEG-4 Audio conforming products. This copyright notice must +be included in all copies or derivative works." +Copyright(c)1996. + * * + ****************************************************************************/ +/* + * $Id: channels.c,v 1.5 2001/09/04 18:39:35 menno Exp $ + */ + +#include "channels.h" +#include "coder.h" +#include "util.h" + +/* If LFE present */ +/* Num channels # of SCE's # of CPE's #of LFE's */ +/* ============ ========== ========== ========= */ +/* 1 1 0 0 */ +/* 2 0 1 0 */ +/* 3 1 1 0 */ +/* 4 1 1 1 */ +/* 5 1 2 0 */ +/* For more than 5 channels, use the following elements: */ +/* 2*N 1 2*(N-1) 1 */ +/* 2*N+1 1 2*N 0 */ +/* */ +/* Else: */ +/* */ +/* Num channels # of SCE's # of CPE's #of LFE's */ +/* ============ ========== ========== ========= */ +/* 1 1 0 0 */ +/* 2 0 1 0 */ +/* 3 1 1 0 */ +/* 4 2 1 0 */ +/* 5 1 2 0 */ +/* For more than 5 channels, use the following elements: */ +/* 2*N 2 2*(N-1) 0 */ +/* 2*N+1 1 2*N 0 */ + +void GetChannelInfo(ChannelInfo *channelInfo, int numChannels, int useLfe) +{ + int sceTag = 0; + int lfeTag = 0; + int cpeTag = 0; + int numChannelsLeft = numChannels; + + + /* First element is sce, except for 2 channel case */ + if (numChannelsLeft != 2) { + channelInfo[numChannels-numChannelsLeft].present = 1; + channelInfo[numChannels-numChannelsLeft].tag = sceTag++; + channelInfo[numChannels-numChannelsLeft].cpe = 0; + channelInfo[numChannels-numChannelsLeft].lfe = 0; + numChannelsLeft--; + } + + /* Next elements are cpe's */ + while (numChannelsLeft > 1) { + /* Left channel info */ + channelInfo[numChannels-numChannelsLeft].present = 1; + channelInfo[numChannels-numChannelsLeft].tag = cpeTag++; + channelInfo[numChannels-numChannelsLeft].cpe = 1; + channelInfo[numChannels-numChannelsLeft].common_window = 0; + channelInfo[numChannels-numChannelsLeft].ch_is_left = 1; + channelInfo[numChannels-numChannelsLeft].paired_ch = numChannels-numChannelsLeft+1; + channelInfo[numChannels-numChannelsLeft].lfe = 0; + numChannelsLeft--; + + /* Right channel info */ + channelInfo[numChannels-numChannelsLeft].present = 1; + channelInfo[numChannels-numChannelsLeft].cpe = 1; + channelInfo[numChannels-numChannelsLeft].common_window = 0; + channelInfo[numChannels-numChannelsLeft].ch_is_left = 0; + channelInfo[numChannels-numChannelsLeft].paired_ch = numChannels-numChannelsLeft-1; + channelInfo[numChannels-numChannelsLeft].lfe = 0; + numChannelsLeft--; + } + + /* Is there another channel left ? */ + if (numChannelsLeft) { + if (useLfe) { + channelInfo[numChannels-numChannelsLeft].present = 1; + channelInfo[numChannels-numChannelsLeft].tag = lfeTag++; + channelInfo[numChannels-numChannelsLeft].cpe = 0; + channelInfo[numChannels-numChannelsLeft].lfe = 1; + } else { + channelInfo[numChannels-numChannelsLeft].present = 1; + channelInfo[numChannels-numChannelsLeft].tag = sceTag++; + channelInfo[numChannels-numChannelsLeft].cpe = 0; + channelInfo[numChannels-numChannelsLeft].lfe = 0; + } + numChannelsLeft--; + } +} diff --git a/external/libfaac/libfaac/channels.h b/external/libfaac/libfaac/channels.h new file mode 100644 index 0000000..acb8a9d --- /dev/null +++ b/external/libfaac/libfaac/channels.h @@ -0,0 +1,54 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: channels.h,v 1.7 2003/06/26 19:19:41 knik Exp $ + */ + +#ifndef CHANNEL_H +#define CHANNEL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "coder.h" + +typedef struct { + int is_present; + int ms_used[MAX_SCFAC_BANDS]; +} MSInfo; + +typedef struct { + int tag; + int present; + int ch_is_left; + int paired_ch; + int common_window; + int cpe; + int sce; + int lfe; + MSInfo msInfo; +} ChannelInfo; + +void GetChannelInfo(ChannelInfo *channelInfo, int numChannels, int useLfe); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CHANNEL_H */ diff --git a/external/libfaac/libfaac/coder.h b/external/libfaac/libfaac/coder.h new file mode 100644 index 0000000..b80ec32 --- /dev/null +++ b/external/libfaac/libfaac/coder.h @@ -0,0 +1,210 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: coder.h,v 1.13 2005/02/02 07:49:10 sur Exp $ + */ + +#ifndef CODER_H +#define CODER_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Allow encoding of Digital Radio Mondiale (DRM) */ +//#define DRM + +/* Allow encoding of Digital Radio Mondiale (DRM) with transform length 1024 */ +//#define DRM_1024 + +#define MAX_CHANNELS 64 + +#ifdef DRM +#ifdef DRM_1024 +# define FRAME_LEN 1024 +# define BLOCK_LEN_LONG 1024 +# define BLOCK_LEN_SHORT 128 +#else +# define FRAME_LEN 960 +# define BLOCK_LEN_LONG 960 +# define BLOCK_LEN_SHORT 120 +#endif /* DRM_1024 */ +#else +# define FRAME_LEN 1024 +# define BLOCK_LEN_LONG 1024 +# define BLOCK_LEN_SHORT 128 +#endif + +#define NSFB_LONG 51 +#define NSFB_SHORT 15 +#define MAX_SHORT_WINDOWS 8 +#define MAX_SCFAC_BANDS ((NSFB_SHORT+1)*MAX_SHORT_WINDOWS) + +enum WINDOW_TYPE { + ONLY_LONG_WINDOW, + LONG_SHORT_WINDOW, + ONLY_SHORT_WINDOW, + SHORT_LONG_WINDOW +}; + +#define TNS_MAX_ORDER 20 +#define DEF_TNS_GAIN_THRESH 1.4 +#define DEF_TNS_COEFF_THRESH 0.1 +#define DEF_TNS_COEFF_RES 4 +#define DEF_TNS_RES_OFFSET 3 +#define LEN_TNS_NFILTL 2 +#define LEN_TNS_NFILTS 1 + +#define DELAY 2048 +#define LEN_LTP_DATA_PRESENT 1 +#define LEN_LTP_LAG 11 +#define LEN_LTP_COEF 3 +#define LEN_LTP_SHORT_USED 1 +#define LEN_LTP_SHORT_LAG_PRESENT 1 +#define LEN_LTP_SHORT_LAG 5 +#define LTP_LAG_OFFSET 16 +#define LEN_LTP_LONG_USED 1 +#define MAX_LT_PRED_LONG_SFB 40 +#define MAX_LT_PRED_SHORT_SFB 13 +#define SHORT_SQ_OFFSET (BLOCK_LEN_LONG-(BLOCK_LEN_SHORT*4+BLOCK_LEN_SHORT/2)) +#define CODESIZE 8 +#define NOK_LT_BLEN (3 * BLOCK_LEN_LONG) + +#define SBMAX_L 49 +#define LPC 2 + +typedef struct { + int order; /* Filter order */ + int direction; /* Filtering direction */ + int coefCompress; /* Are coeffs compressed? */ + int length; /* Length, in bands */ + double aCoeffs[TNS_MAX_ORDER+1]; /* AR Coefficients */ + double kCoeffs[TNS_MAX_ORDER+1]; /* Reflection Coefficients */ + int index[TNS_MAX_ORDER+1]; /* Coefficient indices */ +} TnsFilterData; + +typedef struct { + int numFilters; /* Number of filters */ + int coefResolution; /* Coefficient resolution */ + TnsFilterData tnsFilter[1< +#include +#include + +#include "fft.h" +#include "util.h" + +#define MAXLOGM 9 +#define MAXLOGR 8 + +#if defined DRM && !defined DRM_1024 + +#include "kiss_fft/kiss_fft.h" +#include "kiss_fft/kiss_fftr.h" + +static const int logm_to_nfft[] = +{ +/* 0 1 2 3 */ + 0, 0, 0, 0, +/* 4 5 6 7 */ + 0, 0, 60, 0, +/* 8 9 */ + 240, 480 +}; + +void fft_initialize( FFT_Tables *fft_tables ) +{ + memset( fft_tables->cfg, 0, sizeof( fft_tables->cfg ) ); +} +void fft_terminate( FFT_Tables *fft_tables ) +{ + unsigned int i; + for ( i = 0; i < sizeof( fft_tables->cfg ) / sizeof( fft_tables->cfg[0] ); i++ ) + { + if ( fft_tables->cfg[i][0] ) + { + free( fft_tables->cfg[i][0] ); + fft_tables->cfg[i][0] = NULL; + } + if ( fft_tables->cfg[i][1] ) + { + free( fft_tables->cfg[i][1] ); + fft_tables->cfg[i][1] = NULL; + } + } +} + +void rfft( FFT_Tables *fft_tables, double *x, int logm ) +{ +#if 0 +/* sur: do not use real-only optimized FFT */ + double xi[1 << MAXLOGR]; + + int nfft; + + if ( logm > MAXLOGR ) + { + fprintf(stderr, "rfft size too big\n"); + exit(1); + } + + nfft = logm_to_nfft[logm]; + + if ( nfft ) + { + //unsigned int i; + //for ( i = 0; i < nfft; i++ ) + //{ + // xi[i] = 0.0; + //} + memset( xi, 0, nfft * sizeof( xi[0] ) ); + + fft( fft_tables, x, xi, logm ); + + memcpy( x + nfft / 2, xi, ( nfft / 2 ) * sizeof(x[0]) ); + } + else + { + fprintf( stderr, "bad config for logm = %d\n", logm); + exit( 1 ); + } + +#else +/* sur: use real-only optimized FFT */ + + int nfft = 0; + + kiss_fft_scalar fin[1 << MAXLOGR]; + kiss_fft_cpx fout[1 << MAXLOGR]; + + if ( logm > MAXLOGR ) + { + fprintf(stderr, "fft size too big\n"); + exit(1); + } + + nfft = logm_to_nfft[logm]; + + if ( fft_tables->cfg[logm][0] == NULL ) + { + if ( nfft ) + { + fft_tables->cfg[logm][0] = kiss_fftr_alloc( nfft, 0, NULL, NULL ); + } + else + { + fprintf(stderr, "bad logm = %d\n", logm); + exit( 1 ); + } + } + + if ( fft_tables->cfg[logm][0] ) + { + unsigned int i; + + for ( i = 0; i < nfft; i++ ) + { + fin[i] = x[i]; + } + + kiss_fftr( (kiss_fftr_cfg)fft_tables->cfg[logm][0], fin, fout ); + + for ( i = 0; i < nfft / 2; i++ ) + { + x[i] = fout[i].r; + x[i + nfft / 2] = fout[i].i; + } + } + else + { + fprintf( stderr, "bad config for logm = %d\n", logm); + exit( 1 ); + } +#endif +} + +void fft( FFT_Tables *fft_tables, double *xr, double *xi, int logm ) +{ + int nfft = 0; + + kiss_fft_cpx fin[1 << MAXLOGM]; + kiss_fft_cpx fout[1 << MAXLOGM]; + + if ( logm > MAXLOGM ) + { + fprintf(stderr, "fft size too big\n"); + exit(1); + } + + nfft = logm_to_nfft[logm]; + + if ( fft_tables->cfg[logm][0] == NULL ) + { + if ( nfft ) + { + fft_tables->cfg[logm][0] = kiss_fft_alloc( nfft, 0, NULL, NULL ); + } + else + { + fprintf(stderr, "bad logm = %d\n", logm); + exit( 1 ); + } + } + + if ( fft_tables->cfg[logm][0] ) + { + unsigned int i; + + for ( i = 0; i < nfft; i++ ) + { + fin[i].r = xr[i]; + fin[i].i = xi[i]; + } + + kiss_fft( (kiss_fft_cfg)fft_tables->cfg[logm][0], fin, fout ); + + for ( i = 0; i < nfft; i++ ) + { + xr[i] = fout[i].r; + xi[i] = fout[i].i; + } + } + else + { + fprintf( stderr, "bad config for logm = %d\n", logm); + exit( 1 ); + } +} + +void ffti( FFT_Tables *fft_tables, double *xr, double *xi, int logm ) +{ + int nfft = 0; + + kiss_fft_cpx fin[1 << MAXLOGM]; + kiss_fft_cpx fout[1 << MAXLOGM]; + + if ( logm > MAXLOGM ) + { + fprintf(stderr, "fft size too big\n"); + exit(1); + } + + nfft = logm_to_nfft[logm]; + + if ( fft_tables->cfg[logm][1] == NULL ) + { + if ( nfft ) + { + fft_tables->cfg[logm][1] = kiss_fft_alloc( nfft, 1, NULL, NULL ); + } + else + { + fprintf(stderr, "bad logm = %d\n", logm); + exit( 1 ); + } + } + + if ( fft_tables->cfg[logm][1] ) + { + unsigned int i; + double fac = 1.0 / (double)nfft; + + for ( i = 0; i < nfft; i++ ) + { + fin[i].r = xr[i]; + fin[i].i = xi[i]; + } + + kiss_fft( (kiss_fft_cfg)fft_tables->cfg[logm][1], fin, fout ); + + for ( i = 0; i < nfft; i++ ) + { + xr[i] = fout[i].r * fac; + xi[i] = fout[i].i * fac; + } + } + else + { + fprintf( stderr, "bad config for logm = %d\n", logm); + exit( 1 ); + } +} + +/* sur: Trying to use cfft from libfaad2 -- it does not work 'from scratch' */ +// +//#include "cfft/common.h" +// +//void fft_initialize( FFT_Tables *fft_tables ) +//{ +// memset( fft_tables->cfft, 0, sizeof( fft_tables->cfft ) ); +//} +//void fft_terminate( FFT_Tables *fft_tables ) +//{ +// unsigned int i; +// for ( i = 0; i < sizeof( fft_tables->cfft ) / sizeof( fft_tables->cfft[0] ); i++ ) +// { +// if ( fft_tables->cfft[i] ) +// { +// cfftu( fft_tables->cfft[i] ); +// fft_tables->cfft[i] = NULL; +// } +// } +//} +// +//void rfft( FFT_Tables *fft_tables, double *x, int logm ) +//{ +// double xi[1 << MAXLOGR]; +// +// int nfft; +// +// if ( logm > MAXLOGR ) +// { +// fprintf(stderr, "rfft size too big\n"); +// exit(1); +// } +// +// nfft = logm_to_nfft[logm]; +// +// if ( nfft ) +// { +// unsigned int i; +// +// for ( i = 0; i < nfft; i++ ) +// { +// xi[i] = 0.0; +// } +// //memset( xi, 0, nfft * sizeof( xi[0] ) ); +// +// fft( fft_tables, x, xi, logm ); +// +// memcpy( x + nfft / 2, xi, ( nfft / 2 ) * sizeof(x[0]) ); +// +//#ifdef SUR_DEBUG_FFT +// { +// FILE* f = fopen( "fft.log", "at" ); +// +// fprintf( f, "RFFT(%d)\n", nfft ); +// +// for ( i = 0; i < nfft; i++ ) +// { +// fprintf( f, ";%d;%g;%g\n", i, x[i], xi[i] ); +// } +// +// fclose( f ); +// } +//#endif +// } +// else +// { +// fprintf( stderr, "bad config for logm = %d\n", logm); +// exit( 1 ); +// } +//} +// +//void fft( FFT_Tables *fft_tables, double *xr, double *xi, int logm ) +//{ +// int nfft; +// +// complex_t c[1 << MAXLOGM]; +// +// if ( logm > MAXLOGM ) +// { +// fprintf(stderr, "fft size too big\n"); +// exit(1); +// } +// +// nfft = logm_to_nfft[logm]; +// +// if ( fft_tables->cfft[logm] == NULL ) +// { +// if ( nfft ) +// { +// fft_tables->cfft[logm] = cffti( nfft ); +// } +// else +// { +// fprintf(stderr, "bad logm = %d\n", logm); +// exit( 1 ); +// } +// } +// +// if ( fft_tables->cfft[logm] ) +// { +// unsigned int i; +// +// for ( i = 0; i < nfft; i++ ) +// { +// RE( c[i] ) = xr[i]; +// IM( c[i] ) = xi[i]; +// } +// +// cfftf( fft_tables->cfft[logm], c ); +// +// for ( i = 0; i < nfft; i++ ) +// { +// xr[i] = RE( c[i] ); +// xi[i] = IM( c[i] ); +// } +// +//#ifdef SUR_DEBUG_FFT +// { +// FILE* f = fopen( "fft.log", "at" ); +// +// fprintf( f, "FFT(%d)\n", nfft ); +// +// for ( i = 0; i < nfft; i++ ) +// { +// fprintf( f, ";%d;%g;%g\n", i, xr[i], xi[i] ); +// } +// +// fclose( f ); +// } +//#endif +// } +// else +// { +// fprintf( stderr, "bad config for logm = %d\n", logm); +// exit( 1 ); +// } +//} +// +//void ffti( FFT_Tables *fft_tables, double *xr, double *xi, int logm ) +//{ +// int nfft; +// +// complex_t c[1 << MAXLOGM]; +// +// if ( logm > MAXLOGM ) +// { +// fprintf(stderr, "fft size too big\n"); +// exit(1); +// } +// +// nfft = logm_to_nfft[logm]; +// +// if ( fft_tables->cfft[logm] == NULL ) +// { +// if ( nfft ) +// { +// fft_tables->cfft[logm] = cffti( nfft ); +// } +// else +// { +// fprintf(stderr, "bad logm = %d\n", logm); +// exit( 1 ); +// } +// } +// +// if ( fft_tables->cfft[logm] ) +// { +// unsigned int i; +// +// for ( i = 0; i < nfft; i++ ) +// { +// RE( c[i] ) = xr[i]; +// IM( c[i] ) = xi[i]; +// } +// +// cfftb( fft_tables->cfft[logm], c ); +// +// for ( i = 0; i < nfft; i++ ) +// { +// xr[i] = RE( c[i] ); +// xi[i] = IM( c[i] ); +// } +// +//#ifdef SUR_DEBUG_FFT +// { +// FILE* f = fopen( "fft.log", "at" ); +// +// fprintf( f, "FFTI(%d)\n", nfft ); +// +// for ( i = 0; i < nfft; i++ ) +// { +// fprintf( f, ";%d;%g;%g\n", i, xr[i], xi[i] ); +// } +// +// fclose( f ); +// } +//#endif +// } +// else +// { +// fprintf( stderr, "bad config for logm = %d\n", logm); +// exit( 1 ); +// } +//} + +#else /* !defined DRM || defined DRM_1024 */ + +void fft_initialize( FFT_Tables *fft_tables ) +{ + int i; + fft_tables->costbl = AllocMemory( (MAXLOGM+1) * sizeof( fft_tables->costbl[0] ) ); + fft_tables->negsintbl = AllocMemory( (MAXLOGM+1) * sizeof( fft_tables->negsintbl[0] ) ); + fft_tables->reordertbl = AllocMemory( (MAXLOGM+1) * sizeof( fft_tables->reordertbl[0] ) ); + + for( i = 0; i< MAXLOGM+1; i++ ) + { + fft_tables->costbl[i] = NULL; + fft_tables->negsintbl[i] = NULL; + fft_tables->reordertbl[i] = NULL; + } +} + +void fft_terminate( FFT_Tables *fft_tables ) +{ + int i; + + for( i = 0; i< MAXLOGM+1; i++ ) + { + if( fft_tables->costbl[i] != NULL ) + FreeMemory( fft_tables->costbl[i] ); + + if( fft_tables->negsintbl[i] != NULL ) + FreeMemory( fft_tables->negsintbl[i] ); + + if( fft_tables->reordertbl[i] != NULL ) + FreeMemory( fft_tables->reordertbl[i] ); + } + + FreeMemory( fft_tables->costbl ); + FreeMemory( fft_tables->negsintbl ); + FreeMemory( fft_tables->reordertbl ); + + fft_tables->costbl = NULL; + fft_tables->negsintbl = NULL; + fft_tables->reordertbl = NULL; +} + +static void reorder( FFT_Tables *fft_tables, double *x, int logm) +{ + int i; + int size = 1 << logm; + unsigned short *r; //size + + + if ( fft_tables->reordertbl[logm] == NULL ) // create bit reversing table + { + fft_tables->reordertbl[logm] = AllocMemory(size * sizeof(*(fft_tables->reordertbl[0]))); + + for (i = 0; i < size; i++) + { + int reversed = 0; + int b0; + int tmp = i; + + for (b0 = 0; b0 < logm; b0++) + { + reversed = (reversed << 1) | (tmp & 1); + tmp >>= 1; + } + fft_tables->reordertbl[logm][i] = reversed; + } + } + + r = fft_tables->reordertbl[logm]; + + for (i = 0; i < size; i++) + { + int j = r[i]; + double tmp; + + if (j <= i) + continue; + + tmp = x[i]; + x[i] = x[j]; + x[j] = tmp; + } +} + +static void fft_proc( + double *xr, + double *xi, + fftfloat *refac, + fftfloat *imfac, + int size) +{ + int step, shift, pos; + int exp, estep; + + estep = size; + for (step = 1; step < size; step *= 2) + { + int x1; + int x2 = 0; + estep >>= 1; + for (pos = 0; pos < size; pos += (2 * step)) + { + x1 = x2; + x2 += step; + exp = 0; + for (shift = 0; shift < step; shift++) + { + double v2r, v2i; + + v2r = xr[x2] * refac[exp] - xi[x2] * imfac[exp]; + v2i = xr[x2] * imfac[exp] + xi[x2] * refac[exp]; + + xr[x2] = xr[x1] - v2r; + xr[x1] += v2r; + + xi[x2] = xi[x1] - v2i; + + xi[x1] += v2i; + + exp += estep; + + x1++; + x2++; + } + } + } +} + +static void check_tables( FFT_Tables *fft_tables, int logm) +{ + if( fft_tables->costbl[logm] == NULL ) + { + int i; + int size = 1 << logm; + + if( fft_tables->negsintbl[logm] != NULL ) + FreeMemory( fft_tables->negsintbl[logm] ); + + fft_tables->costbl[logm] = AllocMemory((size / 2) * sizeof(*(fft_tables->costbl[0]))); + fft_tables->negsintbl[logm] = AllocMemory((size / 2) * sizeof(*(fft_tables->negsintbl[0]))); + + for (i = 0; i < (size >> 1); i++) + { + double theta = 2.0 * M_PI * ((double) i) / (double) size; + fft_tables->costbl[logm][i] = cos(theta); + fft_tables->negsintbl[logm][i] = -sin(theta); + } + } +} + +void fft( FFT_Tables *fft_tables, double *xr, double *xi, int logm) +{ + if (logm > MAXLOGM) + { + fprintf(stderr, "fft size too big\n"); + exit(1); + } + + if (logm < 1) + { + //printf("logm < 1\n"); + return; + } + + check_tables( fft_tables, logm); + + reorder( fft_tables, xr, logm); + reorder( fft_tables, xi, logm); + + fft_proc( xr, xi, fft_tables->costbl[logm], fft_tables->negsintbl[logm], 1 << logm ); +} + +void rfft( FFT_Tables *fft_tables, double *x, int logm) +{ + double xi[1 << MAXLOGR]; + + if (logm > MAXLOGR) + { + fprintf(stderr, "rfft size too big\n"); + exit(1); + } + + memset(xi, 0, (1 << logm) * sizeof(xi[0])); + + fft( fft_tables, x, xi, logm); + + memcpy(x + (1 << (logm - 1)), xi, (1 << (logm - 1)) * sizeof(*x)); +} + +void ffti( FFT_Tables *fft_tables, double *xr, double *xi, int logm) +{ + int i, size; + double fac; + double *xrp, *xip; + + fft( fft_tables, xi, xr, logm); + + size = 1 << logm; + fac = 1.0 / size; + xrp = xr; + xip = xi; + + for (i = 0; i < size; i++) + { + *xrp++ *= fac; + *xip++ *= fac; + } +} + +#endif /* defined DRM && !defined DRM_1024 */ + +/* +$Log: fft.c,v $ +Revision 1.12 2005/02/02 07:49:55 sur +Added interface to kiss_fft library to implement FFT for 960 transform length. + +Revision 1.11 2004/04/02 14:56:17 danchr +fix name clash w/ libavcodec: fft_init -> fft_initialize +bump version number to 1.24 beta + +Revision 1.10 2003/11/16 05:02:51 stux +moved global tables from fft.c into hEncoder FFT_Tables. Add fft_init and fft_terminate, flowed through all necessary changes. This should remove at least one instance of a memory leak, and fix some thread-safety problems. Version update to 1.23.3 + +Revision 1.9 2003/09/07 16:48:01 knik +reduced arrays size + +Revision 1.8 2002/11/23 17:32:54 knik +rfft: made xi a local variable + +Revision 1.7 2002/08/21 16:52:25 knik +new simplier and faster fft routine and correct real fft +new real fft is just a complex fft wrapper so it is slower than optimal but +by surprise it seems to be at least as fast as the old buggy function + +*/ diff --git a/external/libfaac/libfaac/fft.h b/external/libfaac/libfaac/fft.h new file mode 100644 index 0000000..322db00 --- /dev/null +++ b/external/libfaac/libfaac/fft.h @@ -0,0 +1,55 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * $Id: fft.h,v 1.6 2005/02/02 07:50:35 sur Exp $ + * Copyright (C) 2002 Krzysztof Nikiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _FFT_H_ +#define _FFT_H_ + +typedef float fftfloat; + +#if defined DRM && !defined DRM_1024 + +#define MAX_FFT 10 + +typedef struct +{ + /* cfg[Max FFT][FFT and inverse FFT] */ + void* cfg[MAX_FFT][2]; +} FFT_Tables; + +#else /* use own FFT */ + +typedef struct +{ + fftfloat **costbl; + fftfloat **negsintbl; + unsigned short **reordertbl; +} FFT_Tables; + +#endif /* defined DRM && !defined DRM_1024 */ + +void fft_initialize ( FFT_Tables *fft_tables ); +void fft_terminate ( FFT_Tables *fft_tables ); + +void rfft ( FFT_Tables *fft_tables, double *x, int logm ); +void fft ( FFT_Tables *fft_tables, double *xr, double *xi, int logm ); +void ffti ( FFT_Tables *fft_tables, double *xr, double *xi, int logm ); + +#endif diff --git a/external/libfaac/libfaac/filtbank.c b/external/libfaac/libfaac/filtbank.c new file mode 100644 index 0000000..0a24cce --- /dev/null +++ b/external/libfaac/libfaac/filtbank.c @@ -0,0 +1,578 @@ +/************************* MPEG-2 NBC Audio Decoder ************************** + * * +"This software module was originally developed by +AT&T, Dolby Laboratories, Fraunhofer Gesellschaft IIS in the course of +development of the MPEG-2 NBC/MPEG-4 Audio standard ISO/IEC 13818-7, +14496-1,2 and 3. This software module is an implementation of a part of one or more +MPEG-2 NBC/MPEG-4 Audio tools as specified by the MPEG-2 NBC/MPEG-4 +Audio standard. ISO/IEC gives users of the MPEG-2 NBC/MPEG-4 Audio +standards free license to this software module or modifications thereof for use in +hardware or software products claiming conformance to the MPEG-2 NBC/MPEG-4 +Audio standards. Those intending to use this software module in hardware or +software products are advised that this use may infringe existing patents. +The original developer of this software module and his/her company, the subsequent +editors and their companies, and ISO/IEC have no liability for use of this software +module or modifications thereof in an implementation. Copyright is not released for +non MPEG-2 NBC/MPEG-4 Audio conforming products.The original developer +retains full right to use the code for his/her own purpose, assign or donate the +code to a third party and to inhibit third party from using the code for non +MPEG-2 NBC/MPEG-4 Audio conforming products. This copyright notice must +be included in all copies or derivative works." +Copyright(c)1996. + * * + ****************************************************************************/ +/* + * $Id: filtbank.c,v 1.13 2005/02/02 07:51:12 sur Exp $ + */ + +/* + * CHANGES: + * 2001/01/17: menno: Added frequency cut off filter. + * + */ + +#include +#include +#include + +#include "coder.h" +#include "filtbank.h" +#include "frame.h" +#include "fft.h" +#include "util.h" + +#define TWOPI 2*M_PI + + +static void CalculateKBDWindow ( double* win, double alpha, int length ); +static double Izero ( double x); +static void MDCT ( FFT_Tables *fft_tables, double *data, int N ); +static void IMDCT ( FFT_Tables *fft_tables, double *data, int N ); + + + +void FilterBankInit(faacEncHandle hEncoder) +{ + unsigned int i, channel; + + for (channel = 0; channel < hEncoder->numChannels; channel++) { + hEncoder->freqBuff[channel] = (double*)AllocMemory(2*FRAME_LEN*sizeof(double)); + hEncoder->overlapBuff[channel] = (double*)AllocMemory(FRAME_LEN*sizeof(double)); + SetMemory(hEncoder->overlapBuff[channel], 0, FRAME_LEN*sizeof(double)); + } + + hEncoder->sin_window_long = (double*)AllocMemory(BLOCK_LEN_LONG*sizeof(double)); + hEncoder->sin_window_short = (double*)AllocMemory(BLOCK_LEN_SHORT*sizeof(double)); + hEncoder->kbd_window_long = (double*)AllocMemory(BLOCK_LEN_LONG*sizeof(double)); + hEncoder->kbd_window_short = (double*)AllocMemory(BLOCK_LEN_SHORT*sizeof(double)); + + for( i=0; isin_window_long[i] = sin((M_PI/(2*BLOCK_LEN_LONG)) * (i + 0.5)); + for( i=0; isin_window_short[i] = sin((M_PI/(2*BLOCK_LEN_SHORT)) * (i + 0.5)); + + CalculateKBDWindow(hEncoder->kbd_window_long, 4, BLOCK_LEN_LONG*2); + CalculateKBDWindow(hEncoder->kbd_window_short, 6, BLOCK_LEN_SHORT*2); +} + +void FilterBankEnd(faacEncHandle hEncoder) +{ + unsigned int channel; + + for (channel = 0; channel < hEncoder->numChannels; channel++) { + if (hEncoder->freqBuff[channel]) FreeMemory(hEncoder->freqBuff[channel]); + if (hEncoder->overlapBuff[channel]) FreeMemory(hEncoder->overlapBuff[channel]); + } + + if (hEncoder->sin_window_long) FreeMemory(hEncoder->sin_window_long); + if (hEncoder->sin_window_short) FreeMemory(hEncoder->sin_window_short); + if (hEncoder->kbd_window_long) FreeMemory(hEncoder->kbd_window_long); + if (hEncoder->kbd_window_short) FreeMemory(hEncoder->kbd_window_short); +} + +void FilterBank(faacEncHandle hEncoder, + CoderInfo *coderInfo, + double *p_in_data, + double *p_out_mdct, + double *p_overlap, + int overlap_select) +{ + double *p_o_buf, *first_window, *second_window; + double *transf_buf; + int k, i; + int block_type = coderInfo->block_type; + + transf_buf = (double*)AllocMemory(2*BLOCK_LEN_LONG*sizeof(double)); + + /* create / shift old values */ + /* We use p_overlap here as buffer holding the last frame time signal*/ + if(overlap_select != MNON_OVERLAPPED) { + memcpy(transf_buf, p_overlap, FRAME_LEN*sizeof(double)); + memcpy(transf_buf+BLOCK_LEN_LONG, p_in_data, FRAME_LEN*sizeof(double)); + memcpy(p_overlap, p_in_data, FRAME_LEN*sizeof(double)); + } else { + memcpy(transf_buf, p_in_data, 2*FRAME_LEN*sizeof(double)); + } + + /* Window shape processing */ + if(overlap_select != MNON_OVERLAPPED) { + switch (coderInfo->prev_window_shape) { + case SINE_WINDOW: + if ( (block_type == ONLY_LONG_WINDOW) || (block_type == LONG_SHORT_WINDOW)) + first_window = hEncoder->sin_window_long; + else + first_window = hEncoder->sin_window_short; + break; + case KBD_WINDOW: + if ( (block_type == ONLY_LONG_WINDOW) || (block_type == LONG_SHORT_WINDOW)) + first_window = hEncoder->kbd_window_long; + else + first_window = hEncoder->kbd_window_short; + break; + } + + switch (coderInfo->window_shape){ + case SINE_WINDOW: + if ( (block_type == ONLY_LONG_WINDOW) || (block_type == SHORT_LONG_WINDOW)) + second_window = hEncoder->sin_window_long; + else + second_window = hEncoder->sin_window_short; + break; + case KBD_WINDOW: + if ( (block_type == ONLY_LONG_WINDOW) || (block_type == SHORT_LONG_WINDOW)) + second_window = hEncoder->kbd_window_long; + else + second_window = hEncoder->kbd_window_short; + break; + } + } else { + /* Always long block and sine window for LTP */ + first_window = hEncoder->sin_window_long; + second_window = hEncoder->sin_window_long; + } + + /* Set ptr to transf-Buffer */ + p_o_buf = transf_buf; + + /* Separate action for each Block Type */ + switch (block_type) { + case ONLY_LONG_WINDOW : + for ( i = 0 ; i < BLOCK_LEN_LONG ; i++){ + p_out_mdct[i] = p_o_buf[i] * first_window[i]; + p_out_mdct[i+BLOCK_LEN_LONG] = p_o_buf[i+BLOCK_LEN_LONG] * second_window[BLOCK_LEN_LONG-i-1]; + } + MDCT( &hEncoder->fft_tables, p_out_mdct, 2*BLOCK_LEN_LONG ); + break; + + case LONG_SHORT_WINDOW : + for ( i = 0 ; i < BLOCK_LEN_LONG ; i++) + p_out_mdct[i] = p_o_buf[i] * first_window[i]; + memcpy(p_out_mdct+BLOCK_LEN_LONG,p_o_buf+BLOCK_LEN_LONG,NFLAT_LS*sizeof(double)); + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++) + p_out_mdct[i+BLOCK_LEN_LONG+NFLAT_LS] = p_o_buf[i+BLOCK_LEN_LONG+NFLAT_LS] * second_window[BLOCK_LEN_SHORT-i-1]; + SetMemory(p_out_mdct+BLOCK_LEN_LONG+NFLAT_LS+BLOCK_LEN_SHORT,0,NFLAT_LS*sizeof(double)); + MDCT( &hEncoder->fft_tables, p_out_mdct, 2*BLOCK_LEN_LONG ); + break; + + case SHORT_LONG_WINDOW : + SetMemory(p_out_mdct,0,NFLAT_LS*sizeof(double)); + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++) + p_out_mdct[i+NFLAT_LS] = p_o_buf[i+NFLAT_LS] * first_window[i]; + memcpy(p_out_mdct+NFLAT_LS+BLOCK_LEN_SHORT,p_o_buf+NFLAT_LS+BLOCK_LEN_SHORT,NFLAT_LS*sizeof(double)); + for ( i = 0 ; i < BLOCK_LEN_LONG ; i++) + p_out_mdct[i+BLOCK_LEN_LONG] = p_o_buf[i+BLOCK_LEN_LONG] * second_window[BLOCK_LEN_LONG-i-1]; + MDCT( &hEncoder->fft_tables, p_out_mdct, 2*BLOCK_LEN_LONG ); + break; + + case ONLY_SHORT_WINDOW : + p_o_buf += NFLAT_LS; + for ( k=0; k < MAX_SHORT_WINDOWS; k++ ) { + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++ ){ + p_out_mdct[i] = p_o_buf[i] * first_window[i]; + p_out_mdct[i+BLOCK_LEN_SHORT] = p_o_buf[i+BLOCK_LEN_SHORT] * second_window[BLOCK_LEN_SHORT-i-1]; + } + MDCT( &hEncoder->fft_tables, p_out_mdct, 2*BLOCK_LEN_SHORT ); + p_out_mdct += BLOCK_LEN_SHORT; + p_o_buf += BLOCK_LEN_SHORT; + first_window = second_window; + } + break; + } + + if (transf_buf) FreeMemory(transf_buf); +} + +void IFilterBank(faacEncHandle hEncoder, + CoderInfo *coderInfo, + double *p_in_data, + double *p_out_data, + double *p_overlap, + int overlap_select) +{ + double *o_buf, *transf_buf, *overlap_buf; + double *first_window, *second_window; + + double *fp; + int k, i; + int block_type = coderInfo->block_type; + + transf_buf = (double*)AllocMemory(2*BLOCK_LEN_LONG*sizeof(double)); + overlap_buf = (double*)AllocMemory(2*BLOCK_LEN_LONG*sizeof(double)); + + /* Window shape processing */ + if (overlap_select != MNON_OVERLAPPED) { +// switch (coderInfo->prev_window_shape){ +// case SINE_WINDOW: + if ( (block_type == ONLY_LONG_WINDOW) || (block_type == LONG_SHORT_WINDOW)) + first_window = hEncoder->sin_window_long; + else + first_window = hEncoder->sin_window_short; +// break; +// case KBD_WINDOW: +// if ( (block_type == ONLY_LONG_WINDOW) || (block_type == LONG_SHORT_WINDOW)) +// first_window = hEncoder->kbd_window_long; +// else +// first_window = hEncoder->kbd_window_short; +// break; +// } + +// switch (coderInfo->window_shape){ +// case SINE_WINDOW: + if ( (block_type == ONLY_LONG_WINDOW) || (block_type == SHORT_LONG_WINDOW)) + second_window = hEncoder->sin_window_long; + else + second_window = hEncoder->sin_window_short; +// break; +// case KBD_WINDOW: +// if ( (block_type == ONLY_LONG_WINDOW) || (block_type == SHORT_LONG_WINDOW)) +// second_window = hEncoder->kbd_window_long; +// else +// second_window = hEncoder->kbd_window_short; +// break; +// } + } else { + /* Always long block and sine window for LTP */ + first_window = hEncoder->sin_window_long; + second_window = hEncoder->sin_window_long; + } + + /* Assemble overlap buffer */ + memcpy(overlap_buf,p_overlap,BLOCK_LEN_LONG*sizeof(double)); + o_buf = overlap_buf; + + /* Separate action for each Block Type */ + switch( block_type ) { + case ONLY_LONG_WINDOW : + memcpy(transf_buf, p_in_data,BLOCK_LEN_LONG*sizeof(double)); + IMDCT( &hEncoder->fft_tables, transf_buf, 2*BLOCK_LEN_LONG ); + for ( i = 0 ; i < BLOCK_LEN_LONG ; i++) + transf_buf[i] *= first_window[i]; + if (overlap_select != MNON_OVERLAPPED) { + for ( i = 0 ; i < BLOCK_LEN_LONG; i++ ){ + o_buf[i] += transf_buf[i]; + o_buf[i+BLOCK_LEN_LONG] = transf_buf[i+BLOCK_LEN_LONG] * second_window[BLOCK_LEN_LONG-i-1]; + } + } else { /* overlap_select == NON_OVERLAPPED */ + for ( i = 0 ; i < BLOCK_LEN_LONG; i++ ) + transf_buf[i+BLOCK_LEN_LONG] *= second_window[BLOCK_LEN_LONG-i-1]; + } + break; + + case LONG_SHORT_WINDOW : + memcpy(transf_buf, p_in_data,BLOCK_LEN_LONG*sizeof(double)); + IMDCT( &hEncoder->fft_tables, transf_buf, 2*BLOCK_LEN_LONG ); + for ( i = 0 ; i < BLOCK_LEN_LONG ; i++) + transf_buf[i] *= first_window[i]; + if (overlap_select != MNON_OVERLAPPED) { + for ( i = 0 ; i < BLOCK_LEN_LONG; i++ ) + o_buf[i] += transf_buf[i]; + memcpy(o_buf+BLOCK_LEN_LONG,transf_buf+BLOCK_LEN_LONG,NFLAT_LS*sizeof(double)); + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++) + o_buf[i+BLOCK_LEN_LONG+NFLAT_LS] = transf_buf[i+BLOCK_LEN_LONG+NFLAT_LS] * second_window[BLOCK_LEN_SHORT-i-1]; + SetMemory(o_buf+BLOCK_LEN_LONG+NFLAT_LS+BLOCK_LEN_SHORT,0,NFLAT_LS*sizeof(double)); + } else { /* overlap_select == NON_OVERLAPPED */ + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++) + transf_buf[i+BLOCK_LEN_LONG+NFLAT_LS] *= second_window[BLOCK_LEN_SHORT-i-1]; + SetMemory(transf_buf+BLOCK_LEN_LONG+NFLAT_LS+BLOCK_LEN_SHORT,0,NFLAT_LS*sizeof(double)); + } + break; + + case SHORT_LONG_WINDOW : + memcpy(transf_buf, p_in_data,BLOCK_LEN_LONG*sizeof(double)); + IMDCT( &hEncoder->fft_tables, transf_buf, 2*BLOCK_LEN_LONG ); + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++) + transf_buf[i+NFLAT_LS] *= first_window[i]; + if (overlap_select != MNON_OVERLAPPED) { + for ( i = 0 ; i < BLOCK_LEN_SHORT; i++ ) + o_buf[i+NFLAT_LS] += transf_buf[i+NFLAT_LS]; + memcpy(o_buf+BLOCK_LEN_SHORT+NFLAT_LS,transf_buf+BLOCK_LEN_SHORT+NFLAT_LS,NFLAT_LS*sizeof(double)); + for ( i = 0 ; i < BLOCK_LEN_LONG ; i++) + o_buf[i+BLOCK_LEN_LONG] = transf_buf[i+BLOCK_LEN_LONG] * second_window[BLOCK_LEN_LONG-i-1]; + } else { /* overlap_select == NON_OVERLAPPED */ + SetMemory(transf_buf,0,NFLAT_LS*sizeof(double)); + for ( i = 0 ; i < BLOCK_LEN_LONG ; i++) + transf_buf[i+BLOCK_LEN_LONG] *= second_window[BLOCK_LEN_LONG-i-1]; + } + break; + + case ONLY_SHORT_WINDOW : + if (overlap_select != MNON_OVERLAPPED) { + fp = o_buf + NFLAT_LS; + } else { /* overlap_select == NON_OVERLAPPED */ + fp = transf_buf; + } + for ( k=0; k < MAX_SHORT_WINDOWS; k++ ) { + memcpy(transf_buf,p_in_data,BLOCK_LEN_SHORT*sizeof(double)); + IMDCT( &hEncoder->fft_tables, transf_buf, 2*BLOCK_LEN_SHORT ); + p_in_data += BLOCK_LEN_SHORT; + if (overlap_select != MNON_OVERLAPPED) { + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++){ + transf_buf[i] *= first_window[i]; + fp[i] += transf_buf[i]; + fp[i+BLOCK_LEN_SHORT] = transf_buf[i+BLOCK_LEN_SHORT] * second_window[BLOCK_LEN_SHORT-i-1]; + } + fp += BLOCK_LEN_SHORT; + } else { /* overlap_select == NON_OVERLAPPED */ + for ( i = 0 ; i < BLOCK_LEN_SHORT ; i++){ + fp[i] *= first_window[i]; + fp[i+BLOCK_LEN_SHORT] *= second_window[BLOCK_LEN_SHORT-i-1]; + } + fp += 2*BLOCK_LEN_SHORT; + } + first_window = second_window; + } + SetMemory(o_buf+BLOCK_LEN_LONG+NFLAT_LS+BLOCK_LEN_SHORT,0,NFLAT_LS*sizeof(double)); + break; + } + + if (overlap_select != MNON_OVERLAPPED) + memcpy(p_out_data,o_buf,BLOCK_LEN_LONG*sizeof(double)); + else /* overlap_select == NON_OVERLAPPED */ + memcpy(p_out_data,transf_buf,2*BLOCK_LEN_LONG*sizeof(double)); + + /* save unused output data */ + memcpy(p_overlap,o_buf+BLOCK_LEN_LONG,BLOCK_LEN_LONG*sizeof(double)); + + if (overlap_buf) FreeMemory(overlap_buf); + if (transf_buf) FreeMemory(transf_buf); +} + +void specFilter(double *freqBuff, + int sampleRate, + int lowpassFreq, + int specLen + ) +{ + int lowpass,xlowpass; + + /* calculate the last line which is not zero */ + lowpass = (lowpassFreq * specLen) / (sampleRate>>1) + 1; + xlowpass = (lowpass < specLen) ? lowpass : specLen ; + + SetMemory(freqBuff+xlowpass,0,(specLen-xlowpass)*sizeof(double)); +} + +static double Izero(double x) +{ + const double IzeroEPSILON = 1E-41; /* Max error acceptable in Izero */ + double sum, u, halfx, temp; + int n; + + sum = u = n = 1; + halfx = x/2.0; + do { + temp = halfx/(double)n; + n += 1; + temp *= temp; + u *= temp; + sum += u; + } while (u >= IzeroEPSILON*sum); + + return(sum); +} + +static void CalculateKBDWindow(double* win, double alpha, int length) +{ + int i; + double IBeta; + double tmp; + double sum = 0.0; + + alpha *= M_PI; + IBeta = 1.0/Izero(alpha); + + /* calculate lower half of Kaiser Bessel window */ + for(i=0; i<(length>>1); i++) { + tmp = 4.0*(double)i/(double)length - 1.0; + win[i] = Izero(alpha*sqrt(1.0-tmp*tmp))*IBeta; + sum += win[i]; + } + + sum = 1.0/sum; + tmp = 0.0; + + /* calculate lower half of window */ + for(i=0; i<(length>>1); i++) { + tmp += win[i]; + win[i] = sqrt(tmp*sum); + } +} + +static void MDCT( FFT_Tables *fft_tables, double *data, int N ) +{ + double *xi, *xr; + double tempr, tempi, c, s, cold, cfreq, sfreq; /* temps for pre and post twiddle */ + double freq = TWOPI / N; + double cosfreq8, sinfreq8; + int i, n; + + xi = (double*)AllocMemory((N >> 2)*sizeof(double)); + xr = (double*)AllocMemory((N >> 2)*sizeof(double)); + + /* prepare for recurrence relation in pre-twiddle */ + cfreq = cos (freq); + sfreq = sin (freq); + cosfreq8 = cos (freq * 0.125); + sinfreq8 = sin (freq * 0.125); + c = cosfreq8; + s = sinfreq8; + + for (i = 0; i < (N >> 2); i++) { + /* calculate real and imaginary parts of g(n) or G(p) */ + n = (N >> 1) - 1 - 2 * i; + + if (i < (N >> 3)) + tempr = data [(N >> 2) + n] + data [N + (N >> 2) - 1 - n]; /* use second form of e(n) for n = N / 2 - 1 - 2i */ + else + tempr = data [(N >> 2) + n] - data [(N >> 2) - 1 - n]; /* use first form of e(n) for n = N / 2 - 1 - 2i */ + + n = 2 * i; + if (i < (N >> 3)) + tempi = data [(N >> 2) + n] - data [(N >> 2) - 1 - n]; /* use first form of e(n) for n=2i */ + else + tempi = data [(N >> 2) + n] + data [N + (N >> 2) - 1 - n]; /* use second form of e(n) for n=2i*/ + + /* calculate pre-twiddled FFT input */ + xr[i] = tempr * c + tempi * s; + xi[i] = tempi * c - tempr * s; + + /* use recurrence to prepare cosine and sine for next value of i */ + cold = c; + c = c * cfreq - s * sfreq; + s = s * cfreq + cold * sfreq; + } + + /* Perform in-place complex FFT of length N/4 */ + switch (N) { + case BLOCK_LEN_SHORT * 2: + fft( fft_tables, xr, xi, 6); + break; + case BLOCK_LEN_LONG * 2: + fft( fft_tables, xr, xi, 9); + } + + /* prepare for recurrence relations in post-twiddle */ + c = cosfreq8; + s = sinfreq8; + + /* post-twiddle FFT output and then get output data */ + for (i = 0; i < (N >> 2); i++) { + /* get post-twiddled FFT output */ + tempr = 2. * (xr[i] * c + xi[i] * s); + tempi = 2. * (xi[i] * c - xr[i] * s); + + /* fill in output values */ + data [2 * i] = -tempr; /* first half even */ + data [(N >> 1) - 1 - 2 * i] = tempi; /* first half odd */ + data [(N >> 1) + 2 * i] = -tempi; /* second half even */ + data [N - 1 - 2 * i] = tempr; /* second half odd */ + + /* use recurrence to prepare cosine and sine for next value of i */ + cold = c; + c = c * cfreq - s * sfreq; + s = s * cfreq + cold * sfreq; + } + + if (xr) FreeMemory(xr); + if (xi) FreeMemory(xi); +} + +static void IMDCT( FFT_Tables *fft_tables, double *data, int N) +{ + double *xi, *xr; + double tempr, tempi, c, s, cold, cfreq, sfreq; /* temps for pre and post twiddle */ + double freq = 2.0 * M_PI / N; + double fac, cosfreq8, sinfreq8; + int i; + + xi = (double*)AllocMemory((N >> 2)*sizeof(double)); + xr = (double*)AllocMemory((N >> 2)*sizeof(double)); + + /* Choosing to allocate 2/N factor to Inverse Xform! */ + fac = 2. / N; /* remaining 2/N from 4/N IFFT factor */ + + /* prepare for recurrence relation in pre-twiddle */ + cfreq = cos (freq); + sfreq = sin (freq); + cosfreq8 = cos (freq * 0.125); + sinfreq8 = sin (freq * 0.125); + c = cosfreq8; + s = sinfreq8; + + for (i = 0; i < (N >> 2); i++) { + /* calculate real and imaginary parts of g(n) or G(p) */ + tempr = -data[2 * i]; + tempi = data[(N >> 1) - 1 - 2 * i]; + + /* calculate pre-twiddled FFT input */ + xr[i] = tempr * c - tempi * s; + xi[i] = tempi * c + tempr * s; + + /* use recurrence to prepare cosine and sine for next value of i */ + cold = c; + c = c * cfreq - s * sfreq; + s = s * cfreq + cold * sfreq; + } + + /* Perform in-place complex IFFT of length N/4 */ + switch (N) { + case BLOCK_LEN_SHORT * 2: + ffti( fft_tables, xr, xi, 6); + break; + case BLOCK_LEN_LONG * 2: + ffti( fft_tables, xr, xi, 9); + } + + /* prepare for recurrence relations in post-twiddle */ + c = cosfreq8; + s = sinfreq8; + + /* post-twiddle FFT output and then get output data */ + for (i = 0; i < (N >> 2); i++) { + + /* get post-twiddled FFT output */ + tempr = fac * (xr[i] * c - xi[i] * s); + tempi = fac * (xi[i] * c + xr[i] * s); + + /* fill in output values */ + data [(N >> 1) + (N >> 2) - 1 - 2 * i] = tempr; + if (i < (N >> 3)) + data [(N >> 1) + (N >> 2) + 2 * i] = tempr; + else + data [2 * i - (N >> 2)] = -tempr; + + data [(N >> 2) + 2 * i] = tempi; + if (i < (N >> 3)) + data [(N >> 2) - 1 - 2 * i] = -tempi; + else + data [(N >> 2) + N - 1 - 2*i] = tempi; + + /* use recurrence to prepare cosine and sine for next value of i */ + cold = c; + c = c * cfreq - s * sfreq; + s = s * cfreq + cold * sfreq; + } + + if (xr) FreeMemory(xr); + if (xi) FreeMemory(xi); +} diff --git a/external/libfaac/libfaac/filtbank.h b/external/libfaac/libfaac/filtbank.h new file mode 100644 index 0000000..4a869e9 --- /dev/null +++ b/external/libfaac/libfaac/filtbank.h @@ -0,0 +1,71 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: filtbank.h,v 1.11 2005/02/02 07:51:49 sur Exp $ + */ + +#ifndef FILTBANK_H +#define FILTBANK_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "frame.h" + +#ifdef DRM +#define NFLAT_LS (( BLOCK_LEN_LONG - BLOCK_LEN_SHORT ) / 2) +#else +#define NFLAT_LS 448 +#endif + +#define MOVERLAPPED 0 +#define MNON_OVERLAPPED 1 + + +#define SINE_WINDOW 0 +#define KBD_WINDOW 1 + +void FilterBankInit ( faacEncHandle hEncoder ); + +void FilterBankEnd ( faacEncHandle hEncoder ); + +void FilterBank( faacEncHandle hEncoder, + CoderInfo *coderInfo, + double *p_in_data, + double *p_out_mdct, + double *p_overlap, + int overlap_select ); + +void IFilterBank( faacEncHandle hEncoder, + CoderInfo *coderInfo, + double *p_in_data, + double *p_out_mdct, + double *p_overlap, + int overlap_select ); + +void specFilter( double *freqBuff, + int sampleRate, + int lowpassFreq, + int specLen ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* FILTBANK_H */ diff --git a/external/libfaac/libfaac/frame.c b/external/libfaac/libfaac/frame.c new file mode 100644 index 0000000..c66325e --- /dev/null +++ b/external/libfaac/libfaac/frame.c @@ -0,0 +1,1252 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: frame.c,v 1.67 2004/11/17 14:26:06 menno Exp $ + */ + +/* + * CHANGES: + * 2001/01/17: menno: Added frequency cut off filter. + * 2001/02/28: menno: Added Temporal Noise Shaping. + * 2001/03/05: menno: Added Long Term Prediction. + * 2001/05/01: menno: Added backward prediction. + * + */ + +#include +#include +#include +#include + +#include "frame.h" +#include "coder.h" +#include "midside.h" +#include "channels.h" +#include "bitstream.h" +#include "filtbank.h" +#include "aacquant.h" +#include "util.h" +#include "huffman.h" +#include "psych.h" +#include "tns.h" +#include "ltp.h" +#include "backpred.h" +#include "version.h" + +#if FAAC_RELEASE +static char *libfaacName = FAAC_VERSION; +#else +static char *libfaacName = FAAC_VERSION ".1 (" __DATE__ ") UNSTABLE"; +#endif +static char *libCopyright = + "FAAC - Freeware Advanced Audio Coder (http://www.audiocoding.com/)\n" + " Copyright (C) 1999,2000,2001 Menno Bakker\n" + " Copyright (C) 2002,2003 Krzysztof Nikiel\n" + "This software is based on the ISO MPEG-4 reference source code.\n"; + +static const psymodellist_t psymodellist[] = { + {&psymodel2, "knipsycho psychoacoustic"}, + {NULL} +}; + +static SR_INFO srInfo[12+1]; + +// base bandwidth for q=100 +static const int bwbase = 16000; +// bandwidth multiplier (for quantiser) +static const int bwmult = 120; +// max bandwidth/samplerate ratio +static const double bwfac = 0.45; + + +int FAACAPI faacEncGetVersion( char **faac_id_string, + char **faac_copyright_string) +{ + if (faac_id_string) + *faac_id_string = libfaacName; + + if (faac_copyright_string) + *faac_copyright_string = libCopyright; + + return FAAC_CFG_VERSION; +} + + +int FAACAPI faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder,unsigned char** ppBuffer,unsigned long* pSizeOfDecoderSpecificInfo) +{ + BitStream* pBitStream = NULL; + + if((hEncoder == NULL) || (ppBuffer == NULL) || (pSizeOfDecoderSpecificInfo == NULL)) { + return -1; + } + + if(hEncoder->config.mpegVersion == MPEG2){ + return -2; /* not supported */ + } + + *pSizeOfDecoderSpecificInfo = 2; + *ppBuffer = malloc(2); + + if(*ppBuffer != NULL){ + + memset(*ppBuffer,0,*pSizeOfDecoderSpecificInfo); + pBitStream = OpenBitStream(*pSizeOfDecoderSpecificInfo, *ppBuffer); + PutBit(pBitStream, hEncoder->config.aacObjectType, 5); + PutBit(pBitStream, hEncoder->sampleRateIdx, 4); + PutBit(pBitStream, hEncoder->numChannels, 4); + CloseBitStream(pBitStream); + + return 0; + } else { + return -3; + } +} + + +faacEncConfigurationPtr FAACAPI faacEncGetCurrentConfiguration(faacEncHandle hEncoder) +{ + faacEncConfigurationPtr config = &(hEncoder->config); + + return config; +} + +int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder, + faacEncConfigurationPtr config) +{ + int i; + + hEncoder->config.allowMidside = config->allowMidside; + hEncoder->config.useLfe = config->useLfe; + hEncoder->config.useTns = config->useTns; + hEncoder->config.aacObjectType = config->aacObjectType; + hEncoder->config.mpegVersion = config->mpegVersion; + hEncoder->config.outputFormat = config->outputFormat; + hEncoder->config.inputFormat = config->inputFormat; + hEncoder->config.shortctl = config->shortctl; + + assert((hEncoder->config.outputFormat == 0) || (hEncoder->config.outputFormat == 1)); + + switch( hEncoder->config.inputFormat ) + { + case FAAC_INPUT_16BIT: + //case FAAC_INPUT_24BIT: + case FAAC_INPUT_32BIT: + case FAAC_INPUT_FLOAT: + break; + + default: + return 0; + break; + } + + /* No SSR supported for now */ + if (hEncoder->config.aacObjectType == SSR) + return 0; + + /* LTP only with MPEG4 */ + if ((hEncoder->config.aacObjectType == LTP) && (hEncoder->config.mpegVersion != MPEG4)) + return 0; + + /* Re-init TNS for new profile */ + TnsInit(hEncoder); + + /* Check for correct bitrate */ + if (config->bitRate > MaxBitrate(hEncoder->sampleRate)) + return 0; +#if 0 + if (config->bitRate < MinBitrate()) + return 0; +#endif + + if (config->bitRate && !config->bandWidth) + { + static struct { + int rate; // per channel at 44100 sampling frequency + int cutoff; + } rates[] = { +#ifdef DRM + /* DRM uses low bit-rates. We've chosen higher bandwidth values and + decrease the quantizer quality at the same time to preserve the + low bit-rate */ + {4500, 1200}, + {9180, 2500}, + {11640, 3000}, + {14500, 4000}, + {17460, 5500}, + {20960, 6250}, + {40000, 12000}, +#else + {29500, 5000}, + {37500, 7000}, + {47000, 10000}, + {64000, 16000}, + {76000, 20000}, +#endif + {0, 0} + }; + + int f0, f1; + int r0, r1; + +#ifdef DRM + double tmpbitRate = (double)config->bitRate; +#else + double tmpbitRate = (double)config->bitRate * 44100 / hEncoder->sampleRate; +#endif + + config->quantqual = 100; + + f0 = f1 = rates[0].cutoff; + r0 = r1 = rates[0].rate; + + for (i = 0; rates[i].rate; i++) + { + f0 = f1; + f1 = rates[i].cutoff; + r0 = r1; + r1 = rates[i].rate; + if (rates[i].rate >= tmpbitRate) + break; + } + + if (tmpbitRate > r1) + tmpbitRate = r1; + if (tmpbitRate < r0) + tmpbitRate = r0; + + if (f1 > f0) + config->bandWidth = + pow((double)tmpbitRate / r1, + log((double)f1 / f0) / log ((double)r1 / r0)) * (double)f1; + else + config->bandWidth = f1; + +#ifndef DRM + config->bandWidth = + (double)config->bandWidth * hEncoder->sampleRate / 44100; + config->bitRate = tmpbitRate * hEncoder->sampleRate / 44100; +#endif + + if (config->bandWidth > bwbase) + config->bandWidth = bwbase; + } + + hEncoder->config.bitRate = config->bitRate; + + if (!config->bandWidth) + { + config->bandWidth = (config->quantqual - 100) * bwmult + bwbase; + } + + hEncoder->config.bandWidth = config->bandWidth; + + // check bandwidth + if (hEncoder->config.bandWidth < 100) + hEncoder->config.bandWidth = 100; + if (hEncoder->config.bandWidth > (hEncoder->sampleRate / 2)) + hEncoder->config.bandWidth = hEncoder->sampleRate / 2; + + if (config->quantqual > 500) + config->quantqual = 500; + if (config->quantqual < 10) + config->quantqual = 10; + + hEncoder->config.quantqual = config->quantqual; + + /* set quantization quality */ + hEncoder->aacquantCfg.quality = config->quantqual; + + // reset psymodel + hEncoder->psymodel->PsyEnd(&hEncoder->gpsyInfo, hEncoder->psyInfo, hEncoder->numChannels); + if (config->psymodelidx >= (sizeof(psymodellist) / sizeof(psymodellist[0]) - 1)) + config->psymodelidx = (sizeof(psymodellist) / sizeof(psymodellist[0])) - 2; + + hEncoder->config.psymodelidx = config->psymodelidx; + hEncoder->psymodel = psymodellist[hEncoder->config.psymodelidx].model; + hEncoder->psymodel->PsyInit(&hEncoder->gpsyInfo, hEncoder->psyInfo, hEncoder->numChannels, + hEncoder->sampleRate, hEncoder->srInfo->cb_width_long, + hEncoder->srInfo->num_cb_long, hEncoder->srInfo->cb_width_short, + hEncoder->srInfo->num_cb_short); + + /* load channel_map */ + for( i = 0; i < 64; i++ ) + hEncoder->config.channel_map[i] = config->channel_map[i]; + + /* OK */ + return 1; +} + +faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate, + unsigned int numChannels, + unsigned long *inputSamples, + unsigned long *maxOutputBytes) +{ + unsigned int channel; + faacEncHandle hEncoder; + + *inputSamples = FRAME_LEN*numChannels; + *maxOutputBytes = (6144/8)*numChannels; + +#ifdef DRM + *maxOutputBytes += 1; /* for CRC */ +#endif + + hEncoder = (faacEncStruct*)AllocMemory(sizeof(faacEncStruct)); + SetMemory(hEncoder, 0, sizeof(faacEncStruct)); + + hEncoder->numChannels = numChannels; + hEncoder->sampleRate = sampleRate; + hEncoder->sampleRateIdx = GetSRIndex(sampleRate); + + /* Initialize variables to default values */ + hEncoder->frameNum = 0; + hEncoder->flushFrame = 0; + + /* Default configuration */ + hEncoder->config.version = FAAC_CFG_VERSION; + hEncoder->config.name = libfaacName; + hEncoder->config.copyright = libCopyright; + hEncoder->config.mpegVersion = MPEG4; + hEncoder->config.aacObjectType = LTP; + hEncoder->config.allowMidside = 1; + hEncoder->config.useLfe = 1; + hEncoder->config.useTns = 0; + hEncoder->config.bitRate = 0; /* default bitrate / channel */ + hEncoder->config.bandWidth = bwfac * hEncoder->sampleRate; + if (hEncoder->config.bandWidth > bwbase) + hEncoder->config.bandWidth = bwbase; + hEncoder->config.quantqual = 100; + hEncoder->config.psymodellist = (psymodellist_t *)psymodellist; + hEncoder->config.psymodelidx = 0; + hEncoder->psymodel = + hEncoder->config.psymodellist[hEncoder->config.psymodelidx].model; + hEncoder->config.shortctl = SHORTCTL_NORMAL; + + /* default channel map is straight-through */ + for( channel = 0; channel < 64; channel++ ) + hEncoder->config.channel_map[channel] = channel; + + /* + by default we have to be compatible with all previous software + which assumes that we will generate ADTS + /AV + */ + hEncoder->config.outputFormat = 1; + + /* + be compatible with software which assumes 24bit in 32bit PCM + */ + hEncoder->config.inputFormat = FAAC_INPUT_32BIT; + + /* find correct sampling rate depending parameters */ + hEncoder->srInfo = &srInfo[hEncoder->sampleRateIdx]; + + for (channel = 0; channel < numChannels; channel++) + { + hEncoder->coderInfo[channel].prev_window_shape = SINE_WINDOW; + hEncoder->coderInfo[channel].window_shape = SINE_WINDOW; + hEncoder->coderInfo[channel].block_type = ONLY_LONG_WINDOW; + hEncoder->coderInfo[channel].num_window_groups = 1; + hEncoder->coderInfo[channel].window_group_length[0] = 1; + + /* FIXME: Use sr_idx here */ + hEncoder->coderInfo[channel].max_pred_sfb = GetMaxPredSfb(hEncoder->sampleRateIdx); + + hEncoder->sampleBuff[channel] = NULL; + hEncoder->nextSampleBuff[channel] = NULL; + hEncoder->next2SampleBuff[channel] = NULL; + hEncoder->ltpTimeBuff[channel] = (double*)AllocMemory(2*BLOCK_LEN_LONG*sizeof(double)); + SetMemory(hEncoder->ltpTimeBuff[channel], 0, 2*BLOCK_LEN_LONG*sizeof(double)); + } + + /* Initialize coder functions */ + fft_initialize( &hEncoder->fft_tables ); + + hEncoder->psymodel->PsyInit(&hEncoder->gpsyInfo, hEncoder->psyInfo, hEncoder->numChannels, + hEncoder->sampleRate, hEncoder->srInfo->cb_width_long, + hEncoder->srInfo->num_cb_long, hEncoder->srInfo->cb_width_short, + hEncoder->srInfo->num_cb_short); + + FilterBankInit(hEncoder); + + TnsInit(hEncoder); + + LtpInit(hEncoder); + + PredInit(hEncoder); + + AACQuantizeInit(hEncoder->coderInfo, hEncoder->numChannels, + &(hEncoder->aacquantCfg)); + + + + HuffmanInit(hEncoder->coderInfo, hEncoder->numChannels); + + /* Return handle */ + return hEncoder; +} + +int FAACAPI faacEncClose(faacEncHandle hEncoder) +{ + unsigned int channel; + + /* Deinitialize coder functions */ + hEncoder->psymodel->PsyEnd(&hEncoder->gpsyInfo, hEncoder->psyInfo, hEncoder->numChannels); + + FilterBankEnd(hEncoder); + + LtpEnd(hEncoder); + + AACQuantizeEnd(hEncoder->coderInfo, hEncoder->numChannels, + &(hEncoder->aacquantCfg)); + + HuffmanEnd(hEncoder->coderInfo, hEncoder->numChannels); + + fft_terminate( &hEncoder->fft_tables ); + + /* Free remaining buffer memory */ + for (channel = 0; channel < hEncoder->numChannels; channel++) + { + if (hEncoder->ltpTimeBuff[channel]) + FreeMemory(hEncoder->ltpTimeBuff[channel]); + if (hEncoder->sampleBuff[channel]) + FreeMemory(hEncoder->sampleBuff[channel]); + if (hEncoder->nextSampleBuff[channel]) + FreeMemory(hEncoder->nextSampleBuff[channel]); + if (hEncoder->next2SampleBuff[channel]) + FreeMemory (hEncoder->next2SampleBuff[channel]); + if (hEncoder->next3SampleBuff[channel]) + FreeMemory (hEncoder->next3SampleBuff[channel]); + } + + /* Free handle */ + if (hEncoder) + FreeMemory(hEncoder); + + return 0; +} + +int FAACAPI faacEncEncode(faacEncHandle hEncoder, + int32_t *inputBuffer, + unsigned int samplesInput, + unsigned char *outputBuffer, + unsigned int bufferSize + ) +{ + unsigned int channel, i; + int sb, frameBytes; + unsigned int offset; + BitStream *bitStream; /* bitstream used for writing the frame to */ + TnsInfo *tnsInfo_for_LTP; + TnsInfo *tnsDecInfo; +#ifdef DRM + int desbits, diff; + double fix; +#endif + + /* local copy's of parameters */ + ChannelInfo *channelInfo = hEncoder->channelInfo; + CoderInfo *coderInfo = hEncoder->coderInfo; + unsigned int numChannels = hEncoder->numChannels; + unsigned int sampleRate = hEncoder->sampleRate; + unsigned int aacObjectType = hEncoder->config.aacObjectType; + unsigned int mpegVersion = hEncoder->config.mpegVersion; + unsigned int useLfe = hEncoder->config.useLfe; + unsigned int useTns = hEncoder->config.useTns; + unsigned int allowMidside = hEncoder->config.allowMidside; + unsigned int bandWidth = hEncoder->config.bandWidth; + unsigned int shortctl = hEncoder->config.shortctl; + + /* Increase frame number */ + hEncoder->frameNum++; + + if (samplesInput == 0) + hEncoder->flushFrame++; + + /* After 4 flush frames all samples have been encoded, + return 0 bytes written */ + if (hEncoder->flushFrame > 4) + return 0; + + /* Determine the channel configuration */ + GetChannelInfo(channelInfo, numChannels, useLfe); + + /* Update current sample buffers */ + for (channel = 0; channel < numChannels; channel++) + { + double *tmp; + + if (hEncoder->sampleBuff[channel]) { + for(i = 0; i < FRAME_LEN; i++) { + hEncoder->ltpTimeBuff[channel][i] = hEncoder->sampleBuff[channel][i]; + } + } + if (hEncoder->nextSampleBuff[channel]) { + for(i = 0; i < FRAME_LEN; i++) { + hEncoder->ltpTimeBuff[channel][FRAME_LEN + i] = + hEncoder->nextSampleBuff[channel][i]; + } + } + + if (!hEncoder->sampleBuff[channel]) + hEncoder->sampleBuff[channel] = (double*)AllocMemory(FRAME_LEN*sizeof(double)); + + tmp = hEncoder->sampleBuff[channel]; + + hEncoder->sampleBuff[channel] = hEncoder->nextSampleBuff[channel]; + hEncoder->nextSampleBuff[channel] = hEncoder->next2SampleBuff[channel]; + hEncoder->next2SampleBuff[channel] = hEncoder->next3SampleBuff[channel]; + hEncoder->next3SampleBuff[channel] = tmp; + + if (samplesInput == 0) + { + /* start flushing*/ + for (i = 0; i < FRAME_LEN; i++) + hEncoder->next3SampleBuff[channel][i] = 0.0; + } + else + { + int samples_per_channel = samplesInput/numChannels; + + /* handle the various input formats and channel remapping */ + switch( hEncoder->config.inputFormat ) + { + case FAAC_INPUT_16BIT: + { + short *input_channel = (short*)inputBuffer + hEncoder->config.channel_map[channel]; + + for (i = 0; i < samples_per_channel; i++) + { + hEncoder->next3SampleBuff[channel][i] = (double)*input_channel; + input_channel += numChannels; + } + } + break; + + case FAAC_INPUT_32BIT: + { + int32_t *input_channel = (int32_t*)inputBuffer + hEncoder->config.channel_map[channel]; + + for (i = 0; i < samples_per_channel; i++) + { + hEncoder->next3SampleBuff[channel][i] = (1.0/256) * (double)*input_channel; + input_channel += numChannels; + } + } + break; + + case FAAC_INPUT_FLOAT: + { + float *input_channel = (float*)inputBuffer + hEncoder->config.channel_map[channel]; + + for (i = 0; i < samples_per_channel; i++) + { + hEncoder->next3SampleBuff[channel][i] = (double)*input_channel; + input_channel += numChannels; + } + } + break; + + default: + return -1; /* invalid input format */ + break; + } + + for (i = (int)(samplesInput/numChannels); i < FRAME_LEN; i++) + hEncoder->next3SampleBuff[channel][i] = 0.0; + } + + /* Psychoacoustics */ + /* Update buffers and run FFT on new samples */ + /* LFE psychoacoustic can run without it */ + if (!channelInfo[channel].lfe || channelInfo[channel].cpe) + { + hEncoder->psymodel->PsyBufferUpdate( + &hEncoder->fft_tables, + &hEncoder->gpsyInfo, + &hEncoder->psyInfo[channel], + hEncoder->next3SampleBuff[channel], + bandWidth, + hEncoder->srInfo->cb_width_short, + hEncoder->srInfo->num_cb_short); + } + } + + if (hEncoder->frameNum <= 3) /* Still filling up the buffers */ + return 0; + + /* Psychoacoustics */ + hEncoder->psymodel->PsyCalculate(channelInfo, &hEncoder->gpsyInfo, hEncoder->psyInfo, + hEncoder->srInfo->cb_width_long, hEncoder->srInfo->num_cb_long, + hEncoder->srInfo->cb_width_short, + hEncoder->srInfo->num_cb_short, numChannels); + + hEncoder->psymodel->BlockSwitch(coderInfo, hEncoder->psyInfo, numChannels); + + /* force block type */ + if (shortctl == SHORTCTL_NOSHORT) + { + for (channel = 0; channel < numChannels; channel++) + { + coderInfo[channel].block_type = ONLY_LONG_WINDOW; + } + } + if (shortctl == SHORTCTL_NOLONG) + { + for (channel = 0; channel < numChannels; channel++) + { + coderInfo[channel].block_type = ONLY_SHORT_WINDOW; + } + } + + /* AAC Filterbank, MDCT with overlap and add */ + for (channel = 0; channel < numChannels; channel++) { + int k; + + FilterBank(hEncoder, + &coderInfo[channel], + hEncoder->sampleBuff[channel], + hEncoder->freqBuff[channel], + hEncoder->overlapBuff[channel], + MOVERLAPPED); + + if (coderInfo[channel].block_type == ONLY_SHORT_WINDOW) { + for (k = 0; k < 8; k++) { + specFilter(hEncoder->freqBuff[channel]+k*BLOCK_LEN_SHORT, + sampleRate, bandWidth, BLOCK_LEN_SHORT); + } + } else { + specFilter(hEncoder->freqBuff[channel], sampleRate, + bandWidth, BLOCK_LEN_LONG); + } + } + + /* TMP: Build sfb offset table and other stuff */ + for (channel = 0; channel < numChannels; channel++) { + channelInfo[channel].msInfo.is_present = 0; + + if (coderInfo[channel].block_type == ONLY_SHORT_WINDOW) { + coderInfo[channel].max_sfb = hEncoder->srInfo->num_cb_short; + coderInfo[channel].nr_of_sfb = hEncoder->srInfo->num_cb_short; + + coderInfo[channel].num_window_groups = 1; + coderInfo[channel].window_group_length[0] = 8; + coderInfo[channel].window_group_length[1] = 0; + coderInfo[channel].window_group_length[2] = 0; + coderInfo[channel].window_group_length[3] = 0; + coderInfo[channel].window_group_length[4] = 0; + coderInfo[channel].window_group_length[5] = 0; + coderInfo[channel].window_group_length[6] = 0; + coderInfo[channel].window_group_length[7] = 0; + + offset = 0; + for (sb = 0; sb < coderInfo[channel].nr_of_sfb; sb++) { + coderInfo[channel].sfb_offset[sb] = offset; + offset += hEncoder->srInfo->cb_width_short[sb]; + } + coderInfo[channel].sfb_offset[coderInfo[channel].nr_of_sfb] = offset; + } else { + coderInfo[channel].max_sfb = hEncoder->srInfo->num_cb_long; + coderInfo[channel].nr_of_sfb = hEncoder->srInfo->num_cb_long; + + coderInfo[channel].num_window_groups = 1; + coderInfo[channel].window_group_length[0] = 1; + + offset = 0; + for (sb = 0; sb < coderInfo[channel].nr_of_sfb; sb++) { + coderInfo[channel].sfb_offset[sb] = offset; + offset += hEncoder->srInfo->cb_width_long[sb]; + } + coderInfo[channel].sfb_offset[coderInfo[channel].nr_of_sfb] = offset; + } + } + + /* Perform TNS analysis and filtering */ + for (channel = 0; channel < numChannels; channel++) { + if ((!channelInfo[channel].lfe) && (useTns)) { + TnsEncode(&(coderInfo[channel].tnsInfo), + coderInfo[channel].max_sfb, + coderInfo[channel].max_sfb, + coderInfo[channel].block_type, + coderInfo[channel].sfb_offset, + hEncoder->freqBuff[channel]); + } else { + coderInfo[channel].tnsInfo.tnsDataPresent = 0; /* TNS not used for LFE */ + } + } + + for(channel = 0; channel < numChannels; channel++) + { + if((coderInfo[channel].tnsInfo.tnsDataPresent != 0) && (useTns)) + tnsInfo_for_LTP = &(coderInfo[channel].tnsInfo); + else + tnsInfo_for_LTP = NULL; + + if(channelInfo[channel].present && (!channelInfo[channel].lfe) && + (coderInfo[channel].block_type != ONLY_SHORT_WINDOW) && + (mpegVersion == MPEG4) && (aacObjectType == LTP)) + { + LtpEncode(hEncoder, + &coderInfo[channel], + &(coderInfo[channel].ltpInfo), + tnsInfo_for_LTP, + hEncoder->freqBuff[channel], + hEncoder->ltpTimeBuff[channel]); + } else { + coderInfo[channel].ltpInfo.global_pred_flag = 0; + } + } + + for(channel = 0; channel < numChannels; channel++) + { + if ((aacObjectType == MAIN) && (!channelInfo[channel].lfe)) { + int numPredBands = min(coderInfo[channel].max_pred_sfb, coderInfo[channel].nr_of_sfb); + PredCalcPrediction(hEncoder->freqBuff[channel], + coderInfo[channel].requantFreq, + coderInfo[channel].block_type, + numPredBands, + (coderInfo[channel].block_type==ONLY_SHORT_WINDOW)? + hEncoder->srInfo->cb_width_short:hEncoder->srInfo->cb_width_long, + coderInfo, + channelInfo, + channel); + } else { + coderInfo[channel].pred_global_flag = 0; + } + } + + for (channel = 0; channel < numChannels; channel++) { + if (coderInfo[channel].block_type == ONLY_SHORT_WINDOW) { + SortForGrouping(&coderInfo[channel], + &hEncoder->psyInfo[channel], + &channelInfo[channel], + hEncoder->srInfo->cb_width_short, + hEncoder->freqBuff[channel]); + } + CalcAvgEnrg(&coderInfo[channel], hEncoder->freqBuff[channel]); + + // reduce LFE bandwidth + if (!channelInfo[channel].cpe && channelInfo[channel].lfe) + { + coderInfo[channel].nr_of_sfb = coderInfo[channel].max_sfb = 3; + } + } + + MSEncode(coderInfo, channelInfo, hEncoder->freqBuff, numChannels, allowMidside); + + for (channel = 0; channel < numChannels; channel++) + { + CalcAvgEnrg(&coderInfo[channel], hEncoder->freqBuff[channel]); + } + +#ifdef DRM + /* loop the quantization until the desired bit-rate is reached */ + diff = 1; /* to enter while loop */ + hEncoder->aacquantCfg.quality = 120; /* init quality setting */ + while (diff > 0) { /* if too many bits, do it again */ +#endif + /* Quantize and code the signal */ + for (channel = 0; channel < numChannels; channel++) { + if (coderInfo[channel].block_type == ONLY_SHORT_WINDOW) { + AACQuantize(&coderInfo[channel], &hEncoder->psyInfo[channel], + &channelInfo[channel], hEncoder->srInfo->cb_width_short, + hEncoder->srInfo->num_cb_short, hEncoder->freqBuff[channel], + &(hEncoder->aacquantCfg)); + } else { + AACQuantize(&coderInfo[channel], &hEncoder->psyInfo[channel], + &channelInfo[channel], hEncoder->srInfo->cb_width_long, + hEncoder->srInfo->num_cb_long, hEncoder->freqBuff[channel], + &(hEncoder->aacquantCfg)); + } + } + +#ifdef DRM + /* Write the AAC bitstream */ + bitStream = OpenBitStream(bufferSize, outputBuffer); + WriteBitstream(hEncoder, coderInfo, channelInfo, bitStream, numChannels); + + /* Close the bitstream and return the number of bytes written */ + frameBytes = CloseBitStream(bitStream); + + /* now calculate desired bits and compare with actual encoded bits */ + desbits = (int) ((double) numChannels * (hEncoder->config.bitRate * FRAME_LEN) + / hEncoder->sampleRate); + + diff = ((frameBytes - 1 /* CRC */) * 8) - desbits; + + /* do linear correction according to relative difference */ + fix = (double) desbits / ((frameBytes - 1 /* CRC */) * 8); + + /* speed up convergence. A value of 0.92 gives approx up to 10 iterations */ + if (fix > 0.92) + fix = 0.92; + + hEncoder->aacquantCfg.quality *= fix; + + /* quality should not go lower than 1, set diff to exit loop */ + if (hEncoder->aacquantCfg.quality <= 1) + diff = -1; + } +#endif + + // fix max_sfb in CPE mode + for (channel = 0; channel < numChannels; channel++) + { + if (channelInfo[channel].present + && (channelInfo[channel].cpe) + && (channelInfo[channel].ch_is_left)) + { + CoderInfo *cil, *cir; + + cil = &coderInfo[channel]; + cir = &coderInfo[channelInfo[channel].paired_ch]; + + cil->max_sfb = cir->max_sfb = max(cil->max_sfb, cir->max_sfb); + cil->nr_of_sfb = cir->nr_of_sfb = cil->max_sfb; + } + } + + MSReconstruct(coderInfo, channelInfo, numChannels); + + for (channel = 0; channel < numChannels; channel++) + { + /* If short window, reconstruction not needed for prediction */ + if ((coderInfo[channel].block_type == ONLY_SHORT_WINDOW)) { + int sind; + for (sind = 0; sind < BLOCK_LEN_LONG; sind++) { + coderInfo[channel].requantFreq[sind] = 0.0; + } + } else { + + if((coderInfo[channel].tnsInfo.tnsDataPresent != 0) && (useTns)) + tnsDecInfo = &(coderInfo[channel].tnsInfo); + else + tnsDecInfo = NULL; + + if ((!channelInfo[channel].lfe) && (aacObjectType == LTP)) { /* no reconstruction needed for LFE channel*/ + + LtpReconstruct(&coderInfo[channel], &(coderInfo[channel].ltpInfo), + coderInfo[channel].requantFreq); + + if(tnsDecInfo != NULL) + TnsDecodeFilterOnly(&(coderInfo[channel].tnsInfo), coderInfo[channel].nr_of_sfb, + coderInfo[channel].max_sfb, coderInfo[channel].block_type, + coderInfo[channel].sfb_offset, coderInfo[channel].requantFreq); + + IFilterBank(hEncoder, &coderInfo[channel], + coderInfo[channel].requantFreq, + coderInfo[channel].ltpInfo.time_buffer, + coderInfo[channel].ltpInfo.ltp_overlap_buffer, + MOVERLAPPED); + + LtpUpdate(&(coderInfo[channel].ltpInfo), + coderInfo[channel].ltpInfo.time_buffer, + coderInfo[channel].ltpInfo.ltp_overlap_buffer, + BLOCK_LEN_LONG); + } + } + } + +#ifndef DRM + /* Write the AAC bitstream */ + bitStream = OpenBitStream(bufferSize, outputBuffer); + + WriteBitstream(hEncoder, coderInfo, channelInfo, bitStream, numChannels); + + /* Close the bitstream and return the number of bytes written */ + frameBytes = CloseBitStream(bitStream); + + /* Adjust quality to get correct average bitrate */ + if (hEncoder->config.bitRate) + { + double fix; + int desbits = numChannels * (hEncoder->config.bitRate * FRAME_LEN) + / hEncoder->sampleRate; + int diff = (frameBytes * 8) - desbits; + + hEncoder->bitDiff += diff; + fix = (double)hEncoder->bitDiff / desbits; + fix *= 0.01; + fix = max(fix, -0.2); + fix = min(fix, 0.2); + + if (((diff > 0) && (fix > 0.0)) || ((diff < 0) && (fix < 0.0))) + { + hEncoder->aacquantCfg.quality *= (1.0 - fix); + if (hEncoder->aacquantCfg.quality > 300) + hEncoder->aacquantCfg.quality = 300; + if (hEncoder->aacquantCfg.quality < 50) + hEncoder->aacquantCfg.quality = 50; + } + } +#endif + + return frameBytes; +} + + +#ifdef DRM +/* Scalefactorband data table for 960 transform length */ +/* all parameters which are different from the 1024 transform length table are + marked with an "x" */ +static SR_INFO srInfo[12+1] = +{ + { 96000, 40/*x*/, 12, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 16, 16, 24, 28, + 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0/*x*/ + },{ + 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 28/*x*/ + } + }, { 88200, 40/*x*/, 12, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 16, 16, 24, 28, + 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0/*x*/ + },{ + 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 28/*x*/ + } + }, { 64000, 45/*x*/, 12, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 12, 12, 12, 16, 16, 16, 20, 24, 24, 28, + 36, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 16/*x*/, 0/*x*/ + },{ + 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 28/*x*/ + } + }, { 48000, 49, 14, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32/*x*/ + }, { + 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 8/*x*/ + } + }, { 44100, 49, 14, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32/*x*/ + }, { + 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 8/*x*/ + } + }, { 32000, 49/*x*/, 14, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, + 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 0/*x*/, 0/*x*/ + },{ + 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16 + } + }, { 24000, 46/*x*/, 15, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, 20, 20, 24, 24, 28, 28, 32, + 36, 36, 40, 44, 48, 52, 52, 64, 64, 64, 64, 0/*x*/ + }, { + 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 12/*x*/ + } + }, { 22050, 46/*x*/, 15, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, 20, 20, 24, 24, 28, 28, 32, + 36, 36, 40, 44, 48, 52, 52, 64, 64, 64, 64, 0/*x*/ + }, { + 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 12/*x*/ + } + }, { 16000, 42/*x*/, 15, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 0/*x*/ + }, { + 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 12/*x*/ + } + }, { 12000, 42/*x*/, 15, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 0/*x*/ + }, { + 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 12/*x*/ + } + }, { 11025, 42/*x*/, 15, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 0/*x*/ + }, { + 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 12/*x*/ + } + }, { 8000, 40, 15, + { + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, + 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 24, 24, 24, 28, + 28, 32, 36, 36, 40, 44, 48, 52, 56, 60, 64, 16/*x*/ + }, { + 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 12, 16, 20, 12/*x*/ + } + }, + { -1 } +}; +#else +/* Scalefactorband data table for 1024 transform length */ +static SR_INFO srInfo[12+1] = +{ + { 96000, 41, 12, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 16, 16, 24, 28, + 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + },{ + 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 36 + } + }, { 88200, 41, 12, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 16, 16, 24, 28, + 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + },{ + 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 36 + } + }, { 64000, 47, 12, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 12, 12, 12, 16, 16, 16, 20, 24, 24, 28, + 36, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40 + },{ + 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 32 + } + }, { 48000, 49, 14, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96 + }, { + 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16 + } + }, { 44100, 49, 14, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96 + }, { + 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16 + } + }, { 32000, 51, 14, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, + 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32 + },{ + 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16 + } + }, { 24000, 47, 15, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, 20, 20, 24, 24, 28, 28, 32, + 36, 36, 40, 44, 48, 52, 52, 64, 64, 64, 64, 64 + }, { + 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 20 + } + }, { 22050, 47, 15, + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, 20, 20, 24, 24, 28, 28, 32, + 36, 36, 40, 44, 48, 52, 52, 64, 64, 64, 64, 64 + }, { + 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 20 + } + }, { 16000, 43, 15, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 64 + }, { + 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 20 + } + }, { 12000, 43, 15, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 64 + }, { + 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 20 + } + }, { 11025, 43, 15, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 64 + }, { + 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 20 + } + }, { 8000, 40, 15, + { + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, + 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 24, 24, 24, 28, + 28, 32, 36, 36, 40, 44, 48, 52, 56, 60, 64, 80 + }, { + 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 12, 16, 20, 20 + } + }, + { -1 } +}; +#endif + +/* +$Log: frame.c,v $ +Revision 1.67 2004/11/17 14:26:06 menno +Infinite loop fix +dunno if this is good, encoder might be tuned to use energies from before MS encoding. But since the MS encoded samples are used in quantisation this might actually be better. Please test. + +Revision 1.66 2004/11/04 12:51:09 aforanna +version number updated to 1.24.1 due to changes in Winamp and CoolEdit plugins + +Revision 1.65 2004/07/18 09:34:24 corrados +New bandwidth settings for DRM, improved quantization quality adaptation (almost constant bit-rate now) + +Revision 1.64 2004/07/13 17:56:37 corrados +bug fix with new object type definitions + +Revision 1.63 2004/07/08 14:01:25 corrados +New scalefactorband table for 960 transform length, bug fix in HCR + +Revision 1.62 2004/07/04 12:10:52 corrados +made faac compliant with Digital Radio Mondiale (DRM) (DRM macro must be set) +implemented HCR tool, VCB11, CRC, scalable bitstream order +note: VCB11 only uses codebook 11! TODO: implement codebooks 16-32 +960 transform length is not yet implemented (TODO)! Use 1024 for encoding and 960 for decoding, resulting in a lot of artefacts + +Revision 1.61 2004/05/03 11:37:16 danchr +bump version to unstable 1.24+ + +Revision 1.60 2004/04/13 13:47:33 danchr +clarify release <> unstable status + +Revision 1.59 2004/04/02 14:56:17 danchr +fix name clash w/ libavcodec: fft_init -> fft_initialize +bump version number to 1.24 beta + +Revision 1.58 2004/03/17 13:34:20 danchr +Automatic, untuned setting of lowpass for VBR. + +Revision 1.57 2004/03/15 20:16:42 knik +fixed copyright notice + +Revision 1.56 2004/01/23 10:22:26 stux +*** empty log message *** + +Revision 1.55 2003/12/17 20:59:55 knik +changed default cutoff to 16k + +Revision 1.54 2003/11/24 18:09:12 knik +A safe version of faacEncGetVersion() without string length problem. +Removed Stux from copyright notice. I don't think he contributed something very +substantial to faac and this is not the right place to list all contributors. + +Revision 1.53 2003/11/16 05:02:52 stux +moved global tables from fft.c into hEncoder FFT_Tables. Add fft_init and fft_terminate, flowed through all necessary changes. This should remove at least one instance of a memory leak, and fix some thread-safety problems. Version update to 1.23.3 + +Revision 1.52 2003/11/15 08:13:42 stux +added FaacEncGetVersion(), version 1.23.2, added myself to faacCopyright :-P, does vanity know no bound ;) + +Revision 1.51 2003/11/10 17:48:00 knik +Allowed independent bitRate and bandWidth setting. +Small fixes. + +Revision 1.50 2003/10/29 10:31:25 stux +Added channel_map to FaacEncHandle, facilitates free generalised channel remapping in the faac core. Default is straight-through, should be *zero* performance hit... and even probably an immeasurable performance gain, updated FAAC_CFG_VERSION to 104 and FAAC_VERSION to 1.22.0 + +Revision 1.49 2003/10/12 16:43:39 knik +average bitrate control made more stable + +Revision 1.48 2003/10/12 14:29:53 knik +more accurate average bitrate control + +Revision 1.47 2003/09/24 16:26:54 knik +faacEncStruct: quantizer specific data enclosed in AACQuantCfg structure. +Added config option to enforce block type. + +Revision 1.46 2003/09/07 16:48:31 knik +Updated psymodel call. Updated bitrate/cutoff mapping table. + +Revision 1.45 2003/08/23 15:02:13 knik +last frame moved back to the library + +Revision 1.44 2003/08/15 11:42:08 knik +removed single silent flush frame + +Revision 1.43 2003/08/11 09:43:47 menno +thread safety, some tables added to the encoder context + +Revision 1.42 2003/08/09 11:39:30 knik +LFE support enabled by default + +Revision 1.41 2003/08/08 10:02:09 menno +Small fix + +Revision 1.40 2003/08/07 08:17:00 knik +Better LFE support (reduced bandwidth) + +Revision 1.39 2003/08/02 11:32:10 stux +added config.inputFormat, and associated defines and code, faac now handles native endian 16bit, 24bit and float input. Added faacEncGetDecoderSpecificInfo to the dll exports, needed for MP4. Updated DLL .dsp to compile without error. Updated CFG_VERSION to 102. Version number might need to be updated as the API has technically changed. Did not update libfaac.pdf + +Revision 1.38 2003/07/10 19:17:01 knik +24-bit input + +Revision 1.37 2003/06/26 19:20:09 knik +Mid/Side support. +Copyright info moved from frontend. +Fixed memory leak. + +Revision 1.36 2003/05/12 17:53:16 knik +updated ABR table + +Revision 1.35 2003/05/10 09:39:55 knik +added approximate ABR setting +modified default cutoff + +Revision 1.34 2003/05/01 09:31:39 knik +removed ISO psyodel +disabled m/s coding +fixed default bandwidth +reduced max_sfb check + +Revision 1.33 2003/04/13 08:37:23 knik +version number moved to version.h + +Revision 1.32 2003/03/27 17:08:23 knik +added quantizer quality and bandwidth setting + +Revision 1.31 2002/10/11 18:00:15 menno +small bugfix + +Revision 1.30 2002/10/08 18:53:01 menno +Fixed some memory leakage + +Revision 1.29 2002/08/19 16:34:43 knik +added one additional flush frame +fixed sample buffer memory allocation + +*/ diff --git a/external/libfaac/libfaac/frame.h b/external/libfaac/libfaac/frame.h new file mode 100644 index 0000000..a31efe6 --- /dev/null +++ b/external/libfaac/libfaac/frame.h @@ -0,0 +1,160 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: frame.h,v 1.30 2009/01/25 18:50:32 menno Exp $ + */ + +#ifndef FRAME_H +#define FRAME_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif + +#ifndef HAVE_INT32_T +typedef signed int int32_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "coder.h" +#include "channels.h" +#include "psych.h" +#include "aacquant.h" +#include "fft.h" + +#if defined(_WIN32) && !defined(__MINGW32__) + #ifndef FAACAPI + #define FAACAPI __stdcall + #endif +#else + #ifndef FAACAPI + #define FAACAPI + #endif +#endif + +#pragma pack(push, 1) + +typedef struct { + psymodel_t *model; + char *name; +} psymodellist_t; + +#include + +typedef struct { + /* number of channels in AAC file */ + unsigned int numChannels; + + /* samplerate of AAC file */ + unsigned long sampleRate; + unsigned int sampleRateIdx; + + unsigned int usedBytes; + + /* frame number */ + unsigned int frameNum; + unsigned int flushFrame; + + /* Scalefactorband data */ + SR_INFO *srInfo; + + /* sample buffers of current next and next next frame*/ + double *sampleBuff[MAX_CHANNELS]; + double *nextSampleBuff[MAX_CHANNELS]; + double *next2SampleBuff[MAX_CHANNELS]; + double *next3SampleBuff[MAX_CHANNELS]; + double *ltpTimeBuff[MAX_CHANNELS]; + + /* Filterbank buffers */ + double *sin_window_long; + double *sin_window_short; + double *kbd_window_long; + double *kbd_window_short; + double *freqBuff[MAX_CHANNELS]; + double *overlapBuff[MAX_CHANNELS]; + + double *msSpectrum[MAX_CHANNELS]; + + /* Channel and Coder data for all channels */ + CoderInfo coderInfo[MAX_CHANNELS]; + ChannelInfo channelInfo[MAX_CHANNELS]; + + /* Psychoacoustics data */ + PsyInfo psyInfo[MAX_CHANNELS]; + GlobalPsyInfo gpsyInfo; + + /* Configuration data */ + faacEncConfiguration config; + + psymodel_t *psymodel; + + /* quantizer specific config */ + AACQuantCfg aacquantCfg; + + /* FFT Tables */ + FFT_Tables fft_tables; + + /* output bits difference in average bitrate mode */ + int bitDiff; +} faacEncStruct, *faacEncHandle; + +int FAACAPI faacEncGetVersion(char **faac_id_string, + char **faac_copyright_string); + +int FAACAPI faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder, + unsigned char** ppBuffer, + unsigned long* pSizeOfDecoderSpecificInfo); + +faacEncConfigurationPtr FAACAPI faacEncGetCurrentConfiguration(faacEncHandle hEncoder); +int FAACAPI faacEncSetConfiguration (faacEncHandle hEncoder, faacEncConfigurationPtr config); + +faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate, + unsigned int numChannels, + unsigned long *inputSamples, + unsigned long *maxOutputBytes); + +int FAACAPI faacEncEncode(faacEncHandle hEncoder, + int32_t *inputBuffer, + unsigned int samplesInput, + unsigned char *outputBuffer, + unsigned int bufferSize + ); + +int FAACAPI faacEncClose(faacEncHandle hEncoder); + + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* FRAME_H */ diff --git a/external/libfaac/libfaac/huffman.c b/external/libfaac/libfaac/huffman.c new file mode 100644 index 0000000..968d2c6 --- /dev/null +++ b/external/libfaac/libfaac/huffman.c @@ -0,0 +1,1226 @@ +/*********** + +This software module was originally developed by Dolby +Laboratories and edited by Sony Corporation +in the course of development of the MPEG-2 NBC/MPEG-4 +Audio standard ISO/IEC13818-7, 14496-1, 2 and 3. This software module is an +implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools as +specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives users of the +MPEG-2NBC/MPEG-4 Audio standards free license to this software module +or modifications thereof for use in hardware or software products +claiming conformance to the MPEG-2 NBC/MPEG-4 Audio standards. Those +intending to use this software module in hardware or software products +are advised that this use may infringe existing patents. The original +developer of this software module, the subsequent +editors and their companies, and ISO/IEC have no liability for use of +this software module or modifications thereof in an +implementation. Copyright is not released for non MPEG-2 NBC/MPEG-4 +Audio conforming products. The original developer retains full right to +use the code for the developer's own purpose, assign or donate the code to a +third party and to inhibit third party from using the code for non +MPEG-2 NBC/MPEG-4 Audio conforming products. This copyright notice +must be included in all copies or derivative works. Copyright 1996. + +***********/ +/* + * $Id: huffman.c,v 1.11 2005/02/02 07:53:20 sur Exp $ + */ + +#include +#include + +#include "huffman.h" +#include "coder.h" +#include "bitstream.h" +#include "util.h" + +#include "hufftab.h" + +void HuffmanInit(CoderInfo *coderInfo, unsigned int numChannels) +{ + unsigned int channel; + + for (channel = 0; channel < numChannels; channel++) { + coderInfo[channel].data = (int*)AllocMemory(5*FRAME_LEN*sizeof(int)); + coderInfo[channel].len = (int*)AllocMemory(5*FRAME_LEN*sizeof(int)); + +#ifdef DRM + coderInfo[channel].num_data_cw = (int*)AllocMemory(FRAME_LEN*sizeof(int)); +#endif + } +} + +void HuffmanEnd(CoderInfo *coderInfo, unsigned int numChannels) +{ + unsigned int channel; + + for (channel = 0; channel < numChannels; channel++) { + if (coderInfo[channel].data) FreeMemory(coderInfo[channel].data); + if (coderInfo[channel].len) FreeMemory(coderInfo[channel].len); + +#ifdef DRM + if (coderInfo[channel].num_data_cw) FreeMemory(coderInfo[channel].num_data_cw); +#endif + } +} + +int BitSearch(CoderInfo *coderInfo, + int *quant) /* Quantized spectral values */ + /* + This function inputs a vector of quantized spectral data, quant[][], and returns a vector, + 'book_vector[]' that describes how to group together the scalefactor bands into a smaller + number of sections. There are MAX_SCFAC_BANDS elements in book_vector (equal to 49 in the + case of long blocks and 112 for short blocks), and each element has a huffman codebook + number assigned to it. + + For a quick and simple algorithm, this function performs a binary + search across the sfb's (scale factor bands). On the first approach, it calculates the + needed amount of bits if every sfb were its own section and transmitted its own huffman + codebook value side information (equal to 9 bits for a long block, 7 for a short). The + next iteration combines adjacent sfb's, and calculates the bit rate for length two sfb + sections. If any wider two-sfb section requires fewer bits than the sum of the two + single-sfb sections (below it in the binary tree), then the wider section will be chosen. + This process occurs until the sections are split into three uniform parts, each with an + equal amount of sfb's contained. + + The binary tree is stored as a two-dimensional array. Since this tree is not full, (there + are only 49 nodes, not 2^6 = 64), the numbering is a little complicated. If the tree were + full, the top node would be 1. It's children would be 2 and 3. But, since this tree + is not full, the top row of three nodes are numbered {4,5,6}. The row below it is + {8,9,10,11,12,13}, and so on. + + The binary tree is called bit_stats[112][3]. There are 112 total nodes (some are not + used since it's not full). bit_stats[x][0] holds the bit totals needed for the sfb sectioning + strategy represented by the node x in the tree. bit_stats[x][1] holds the optimal huffman + codebook table that minimizes the bit rate, given the sectioning boundaries dictated by node x. +*/ + +{ + int i,j,k; + int hop; + int min_book_choice[112][3]; + int bit_stats[240][3]; + int total_bit_count; + int levels; + int pow2levels; + int fracpow2lev; + + /* Set local pointer to coderInfo book_vector */ + int* book_vector = coderInfo -> book_vector; + + levels = (int) ((log((double)coderInfo->nr_of_sfb)/log((double)2.0))+1); + +/* #define SLOW */ + +#ifdef SLOW + for(i = 0; i < 5; i++) { +#else + i = 0; +#endif + hop = 1 << i; + + NoiselessBitCount(coderInfo, quant, hop, min_book_choice); + + /* load up the (not-full) binary search tree with the min_book_choice values */ + k=0; + total_bit_count = 0; + + pow2levels = 1 << (levels - i); + fracpow2lev = pow2levels + (coderInfo->nr_of_sfb >> i); + + for (j=pow2levels; j < fracpow2lev; j++) + { + bit_stats[j][0] = min_book_choice[k][0]; /* the minimum bit cost for this section */ + bit_stats[j][1] = min_book_choice[k][1]; /* used with this huffman book number */ + +#ifdef SLOW + if (i>0){ /* not on the lowest level, grouping more than one signle scalefactor band per section*/ + if (bit_stats[j][0] < bit_stats[2*j][0] + bit_stats[2*j+1][0]){ + + /* it is cheaper to combine surrounding sfb secionts into one larger huffman book section */ + for(n=k;nsfb_offset; + int nr_of_sfb = coderInfo->nr_of_sfb; + + /* each section is 'hop' scalefactor bands wide */ + for (i=0; i < nr_of_sfb; i=i+hop){ +#ifdef SLOW + if ((i+hop) > nr_of_sfb) + q = nr_of_sfb; + else +#endif + q = i+hop; + + { + + /* find the maximum absolute value in the current spectral section, to see what tables are available to use */ + max_sb_coeff = 0; + for (j=sfb_offset[i]; j max_sb_coeff) + max_sb_coeff = ABS(quant[j]); + } + + j = 0; + offset = sfb_offset[i]; +#ifdef SLOW + if ((i+hop) > nr_of_sfb){ + end = sfb_offset[nr_of_sfb]; + } else +#endif + end = sfb_offset[q]; + length = end - offset; + + /* all spectral coefficients in this section are zero */ + if (max_sb_coeff == 0) { + book_choice[j][0] = CalcBits(coderInfo,0,quant,offset,length); + book_choice[j++][1] = 0; + + } + else { /* if the section does have non-zero coefficients */ + if(max_sb_coeff < 2){ + book_choice[j][0] = CalcBits(coderInfo,1,quant,offset,length); + book_choice[j++][1] = 1; + book_choice[j][0] = CalcBits(coderInfo,2,quant,offset,length); + book_choice[j++][1] = 2; + book_choice[j][0] = CalcBits(coderInfo,3,quant,offset,length); + book_choice[j++][1] = 3; + } + else if (max_sb_coeff < 3){ + book_choice[j][0] = CalcBits(coderInfo,3,quant,offset,length); + book_choice[j++][1] = 3; + book_choice[j][0] = CalcBits(coderInfo,4,quant,offset,length); + book_choice[j++][1] = 4; + book_choice[j][0] = CalcBits(coderInfo,5,quant,offset,length); + book_choice[j++][1] = 5; + } + else if (max_sb_coeff < 5){ + book_choice[j][0] = CalcBits(coderInfo,5,quant,offset,length); + book_choice[j++][1] = 5; + book_choice[j][0] = CalcBits(coderInfo,6,quant,offset,length); + book_choice[j++][1] = 6; + book_choice[j][0] = CalcBits(coderInfo,7,quant,offset,length); + book_choice[j++][1] = 7; + } + else if (max_sb_coeff < 8){ + book_choice[j][0] = CalcBits(coderInfo,7,quant,offset,length); + book_choice[j++][1] = 7; + book_choice[j][0] = CalcBits(coderInfo,8,quant,offset,length); + book_choice[j++][1] = 8; + book_choice[j][0] = CalcBits(coderInfo,9,quant,offset,length); + book_choice[j++][1] = 9; + } + else if (max_sb_coeff < 13){ + book_choice[j][0] = CalcBits(coderInfo,9,quant,offset,length); + book_choice[j++][1] = 9; + book_choice[j][0] = CalcBits(coderInfo,10,quant,offset,length); + book_choice[j++][1] = 10; + } + /* (max_sb_coeff >= 13), choose table 11 */ + else { + book_choice[j][0] = CalcBits(coderInfo,11,quant,offset,length); + book_choice[j++][1] = 11; + } + } + + /* find the minimum bit cost and table number for huffman coding this scalefactor section */ + min_book_choice[i][1] = book_choice[0][1]; + min_book_choice[i][0] = book_choice[0][0]; + + for(k=1;k= 1) { + N++; + x = x/2; + } + + *len_esc_sequence = 2*N + 5; /* the length of the escape sequence in bits */ + + output = (int)((pow(2,N) - 1)*pow(2,N+5) + y - pow(2,N+4)); + return(output); +} + +int OutputBits(CoderInfo *coderInfo, +#ifdef DRM + int *book, /* we need to change book for VCB11 */ +#else + int book, +#endif + int *quant, + int offset, + int length) +{ + /* + This function inputs + - a specific codebook number, 'book' + - the quantized spectral data, 'quant[][]' + - the offset into the spectral data to begin scanning, 'offset' + - the 'length' of the segment to huffman code + -> therefore, the segment quant[offset] to quant[offset+length-1] + is huffman coded. + + This function outputs + - the number of bits required, 'bits' using the prescribed codebook, book applied to + the given segment of spectral data. + + There are three parameters that are passed back and forth into this function. data[] + and len[] are one-dimensional arrays that store the codebook values and their respective + bit lengths. These are used when packing the data for the bitstream in OutputBits(). The + index into these arrays is 'coderInfo->spectral_count''. It gets incremented internally in this + function as counter, then passed to the outside through outside_counter. The next time + OutputBits() is called, counter starts at the value it left off from the previous call. + + */ + int esc_sequence; + int len_esc; + int index; + int bits=0; + int tmp; + int codebook,i,j; + int counter; + + /* Set up local pointers to coderInfo elements data and len */ + int* data= coderInfo->data; + int* len= coderInfo->len; +#ifdef DRM + int* num_data = coderInfo->num_data_cw; + int cur_cw_len; + int max_esc_sequ = 0; +#endif + + counter = coderInfo->spectral_count; + +#ifdef DRM + switch (*book) { +#else + switch (book) { +#endif + case 0: + case INTENSITY_HCB2: + case INTENSITY_HCB: +#ifdef DRM + for(i=offset;idata[counter] = 0; + coderInfo->len[counter++] = 0; + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + +#ifdef DRM + num_data[coderInfo->cur_cw++] = 1; + } +#endif + return(bits); + case 1: + for(i=offset;icur_cw++] = 1; + coderInfo->iLenReordSpData += tmp; + if (tmp > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = tmp; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 2: + for(i=offset;icur_cw++] = 1; + coderInfo->iLenReordSpData += tmp; + if (tmp > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = tmp; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 3: + for(i=offset;icur_cw] = 1; + cur_cw_len = tmp; +#endif + for(j=0;j<4;j++){ + if(quant[i+j] > 0) { /* send out '0' if a positive value */ + data[counter] = 0; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } else + if(quant[i+j] < 0) { /* send out '1' if a negative value */ + data[counter] = 1; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } + } +#ifdef DRM + coderInfo->iLenReordSpData += cur_cw_len; + if (cur_cw_len > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = cur_cw_len; + + coderInfo->cur_cw++; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 4: + for(i=offset;icur_cw] = 1; + cur_cw_len = tmp; +#endif + for(j=0;j<4;j++){ + if(quant[i+j] > 0) { /* send out '0' if a positive value */ + data[counter] = 0; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } else + if(quant[i+j] < 0) { /* send out '1' if a negative value */ + data[counter] = 1; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } + } +#ifdef DRM + coderInfo->iLenReordSpData += cur_cw_len; + if (cur_cw_len > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = cur_cw_len; + + coderInfo->cur_cw++; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 5: + for(i=offset;icur_cw++] = 1; + coderInfo->iLenReordSpData += tmp; + if (tmp > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = tmp; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 6: + for(i=offset;icur_cw++] = 1; + coderInfo->iLenReordSpData += tmp; + if (tmp > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = tmp; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 7: + for(i=offset;icur_cw] = 1; + cur_cw_len = tmp; +#endif + for(j=0;j<2;j++){ + if(quant[i+j] > 0) { /* send out '0' if a positive value */ + data[counter] = 0; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } else + if(quant[i+j] < 0) { /* send out '1' if a negative value */ + data[counter] = 1; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } + } +#ifdef DRM + coderInfo->iLenReordSpData += cur_cw_len; + if (cur_cw_len > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = cur_cw_len; + + coderInfo->cur_cw++; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 8: + for(i=offset;icur_cw] = 1; + cur_cw_len = tmp; +#endif + for(j=0;j<2;j++){ + if(quant[i+j] > 0) { /* send out '0' if a positive value */ + data[counter] = 0; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } else + if(quant[i+j] < 0) { /* send out '1' if a negative value */ + data[counter] = 1; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } + } +#ifdef DRM + coderInfo->iLenReordSpData += cur_cw_len; + if (cur_cw_len > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = cur_cw_len; + + coderInfo->cur_cw++; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 9: + for(i=offset;icur_cw] = 1; + cur_cw_len = tmp; +#endif + for(j=0;j<2;j++){ + if(quant[i+j] > 0) { /* send out '0' if a positive value */ + data[counter] = 0; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } else + if(quant[i+j] < 0) { /* send out '1' if a negative value */ + data[counter] = 1; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } + } +#ifdef DRM + coderInfo->iLenReordSpData += cur_cw_len; + if (cur_cw_len > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = cur_cw_len; + + coderInfo->cur_cw++; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 10: + for(i=offset;icur_cw] = 1; + cur_cw_len = tmp; +#endif + for(j=0;j<2;j++){ + if(quant[i+j] > 0) { /* send out '0' if a positive value */ + data[counter] = 0; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } else + if(quant[i+j] < 0) { /* send out '1' if a negative value */ + data[counter] = 1; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } + } +#ifdef DRM + coderInfo->iLenReordSpData += cur_cw_len; + if (cur_cw_len > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = cur_cw_len; + + coderInfo->cur_cw++; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + return(bits); + case 11: + /* First, calculate the indecies into the huffman tables */ + for(i=offset;i= 16) && (ABS(quant[i+1]) >= 16)) { /* both codewords were above 16 */ + /* first, code the orignal pair, with the larger value saturated to +/- 16 */ + index = 17*16 + 16; + } + else if (ABS(quant[i]) >= 16) { /* the first codeword was above 16, not the second one */ + /* first, code the orignal pair, with the larger value saturated to +/- 16 */ + index = 17*16 + ABS(quant[i+1]); + } + else if (ABS(quant[i+1]) >= 16) { /* the second codeword was above 16, not the first one */ + index = 17*ABS(quant[i]) + 16; + } + else { /* there were no values above 16, so no escape sequences */ + index = 17*ABS(quant[i]) + ABS(quant[i+1]); + } + + /* write out the codewords */ + tmp = huff11[index][FIRSTINTAB]; + codebook = huff11[index][LASTINTAB]; + bits += tmp; + data[counter] = codebook; + len[counter++] = tmp; +#ifdef DRM + num_data[coderInfo->cur_cw] = 1; + cur_cw_len = tmp; +#endif + + /* Take care of the sign bits */ + for(j=0;j<2;j++){ + if(quant[i+j] > 0) { /* send out '0' if a positive value */ + data[counter] = 0; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } else + if(quant[i+j] < 0) { /* send out '1' if a negative value */ + data[counter] = 1; + len[counter++] = 1; + bits += 1; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += 1; +#endif + } + } + + /* write out the escape sequences */ + if ((ABS(quant[i]) >= 16) && (ABS(quant[i+1]) >= 16)) { /* both codewords were above 16 */ + /* code and transmit the first escape_sequence */ + esc_sequence = CalculateEscSequence(quant[i],&len_esc); + bits += len_esc; + data[counter] = esc_sequence; + len[counter++] = len_esc; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += len_esc; + + if (esc_sequence > max_esc_sequ) + max_esc_sequ = esc_sequence; +#endif + + /* then code and transmit the second escape_sequence */ + esc_sequence = CalculateEscSequence(quant[i+1],&len_esc); + bits += len_esc; + data[counter] = esc_sequence; + len[counter++] = len_esc; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += len_esc; + + if (esc_sequence > max_esc_sequ) + max_esc_sequ = esc_sequence; +#endif + } + else if (ABS(quant[i]) >= 16) { /* the first codeword was above 16, not the second one */ + /* code and transmit the escape_sequence */ + esc_sequence = CalculateEscSequence(quant[i],&len_esc); + bits += len_esc; + data[counter] = esc_sequence; + len[counter++] = len_esc; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += len_esc; + + if (esc_sequence > max_esc_sequ) + max_esc_sequ = esc_sequence; +#endif + } + else if (ABS(quant[i+1]) >= 16) { /* the second codeword was above 16, not the first one */ + /* code and transmit the escape_sequence */ + esc_sequence = CalculateEscSequence(quant[i+1],&len_esc); + bits += len_esc; + data[counter] = esc_sequence; + len[counter++] = len_esc; +#ifdef DRM + num_data[coderInfo->cur_cw]++; + cur_cw_len += len_esc; + + if (esc_sequence > max_esc_sequ) + max_esc_sequ = esc_sequence; +#endif + } +#ifdef DRM + coderInfo->iLenReordSpData += cur_cw_len; + if (cur_cw_len > coderInfo->iLenLongestCW) + coderInfo->iLenLongestCW = cur_cw_len; + + coderInfo->cur_cw++; +#endif + } + coderInfo->spectral_count = counter; /* send the current count back to the outside world */ + +#ifdef DRM + /* VCB11: check which codebook should be used using max escape sequence */ + /* 8.5.3.1.3, table 157 */ + if (max_esc_sequ <= 15) + *book = 16; + else if (max_esc_sequ <= 31) + *book = 17; + else if (max_esc_sequ <= 47) + *book = 18; + else if (max_esc_sequ <= 63) + *book = 19; + else if (max_esc_sequ <= 95) + *book = 20; + else if (max_esc_sequ <= 127) + *book = 21; + else if (max_esc_sequ <= 159) + *book = 22; + else if (max_esc_sequ <= 191) + *book = 23; + else if (max_esc_sequ <= 223) + *book = 24; + else if (max_esc_sequ <= 255) + *book = 25; + else if (max_esc_sequ <= 319) + *book = 26; + else if (max_esc_sequ <= 383) + *book = 27; + else if (max_esc_sequ <= 511) + *book = 28; + else if (max_esc_sequ <= 767) + *book = 29; + else if (max_esc_sequ <= 1023) + *book = 30; + else if (max_esc_sequ <= 2047) + *book = 31; + /* else: codebook 11 -> it is already 11 */ +#endif + + return(bits); + } + return 0; +} + +int CalcBits(CoderInfo *coderInfo, + int book, + int *quant, + int offset, + int length) +{ + /* + This function inputs + - a specific codebook number, 'book' + - the quantized spectral data, 'quant[]' + - the offset into the spectral data to begin scanning, 'offset' + - the 'length' of the segment to huffman code + -> therefore, the segment quant[offset] to quant[offset+length-1] + is huffman coded. + + This function outputs + - the number of bits required, 'bits' using the prescribed codebook, book applied to + the given segment of spectral data. + + */ + + int len_esc; + int index; + int bits = 0; + int i, j; + + switch (book) { + case 1: + for(i=offset;i= 16) && (ABS(quant[i+1]) >= 16)) { /* both codewords were above 16 */ + /* first, code the orignal pair, with the larger value saturated to +/- 16 */ + index = 17*16 + 16; + } else if (ABS(quant[i]) >= 16) { /* the first codeword was above 16, not the second one */ + /* first, code the orignal pair, with the larger value saturated to +/- 16 */ + index = 17*16 + ABS(quant[i+1]); + } else if (ABS(quant[i+1]) >= 16) { /* the second codeword was above 16, not the first one */ + index = 17*ABS(quant[i]) + 16; + } else { /* there were no values above 16, so no escape sequences */ + index = 17*ABS(quant[i]) + ABS(quant[i+1]); + } + + /* write out the codewords */ + bits += huff11[index][FIRSTINTAB]; + + /* Take care of the sign bits */ + for(j=0;j<2;j++){ + if(quant[i+j] != 0) bits += 1; /* only for non-zero spectral coefficients */ + } + + /* write out the escape sequences */ + if ((ABS(quant[i]) >= 16) && (ABS(quant[i+1]) >= 16)) { /* both codewords were above 16 */ + /* code and transmit the first escape_sequence */ + CalculateEscSequence(quant[i],&len_esc); + bits += len_esc; + + /* then code and transmit the second escape_sequence */ + CalculateEscSequence(quant[i+1],&len_esc); + bits += len_esc; + } else if (ABS(quant[i]) >= 16) { /* the first codeword was above 16, not the second one */ + /* code and transmit the escape_sequence */ + CalculateEscSequence(quant[i],&len_esc); + bits += len_esc; + } else if (ABS(quant[i+1]) >= 16) { /* the second codeword was above 16, not the first one */ + /* code and transmit the escape_sequence */ + CalculateEscSequence(quant[i+1],&len_esc); + bits += len_esc; + } + } + return (bits); + } + return 0; +} + +int SortBookNumbers(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag) +{ + /* + This function inputs the vector, 'book_vector[]', which is of length MAX_SCFAC_BANDS, + and contains the optimal huffman tables of each sfb. It returns the vector, 'output_book_vector[]', which + has it's elements formatted for the encoded bit stream. It's syntax is: + + {sect_cb[0], length_segment[0], ... ,sect_cb[num_of_sections], length_segment[num_of_sections]} + + The above syntax is true, unless there is an escape sequence. An + escape sequence occurs when a section is longer than 2 ^ (bit_len) + long in units of scalefactor bands. Also, the integer returned from + this function is the number of bits written in the bitstream, + 'bit_count'. + + This function supports both long and short blocks. + */ + + int i; + int repeat_counter; + int bit_count = 0; + int previous; + int max, bit_len/*,sfbs*/; + int max_sfb,g,band; + int sect_cb_bits = 4; + + /* Set local pointers to coderInfo elements */ + int* book_vector = coderInfo->book_vector; + +#ifdef DRM + sect_cb_bits = 5; /* 5 bits in case of VCB11 */ +#endif + + if (coderInfo->block_type == ONLY_SHORT_WINDOW){ + max = 7; + bit_len = 3; + } else { /* the block_type is a long,start, or stop window */ + max = 31; + bit_len = 5; + } + + /* Compute number of scalefactor bands */ + max_sfb = coderInfo->nr_of_sfb / coderInfo->num_window_groups; + + + for (g = 0; g < coderInfo->num_window_groups; g++) { + band=g*max_sfb; + + repeat_counter=1; + + previous = book_vector[band]; + if (writeFlag) { + PutBit(bitStream,book_vector[band],sect_cb_bits); + } + bit_count += sect_cb_bits; + + for (i=band+1;i= 16) && (previous <= 32))) + { + if (writeFlag) + PutBit(bitStream,book_vector[i],sect_cb_bits); + bit_count += sect_cb_bits; + previous = book_vector[i]; + repeat_counter=1; + + } else +#endif + if( (book_vector[i] != previous)) { + if (writeFlag) { + PutBit(bitStream,repeat_counter,bit_len); + } + bit_count += bit_len; + + if (repeat_counter == max){ /* in case you need to terminate an escape sequence */ + if (writeFlag) + PutBit(bitStream,0,bit_len); + bit_count += bit_len; + } + + if (writeFlag) + PutBit(bitStream,book_vector[i],sect_cb_bits); + bit_count += sect_cb_bits; + previous = book_vector[i]; + repeat_counter=1; + } + /* if the length of the section is longer than the amount of bits available in */ + /* the bitsream, "max", then start up an escape sequence */ + else if ((book_vector[i] == previous) && (repeat_counter == max)) { + if (writeFlag) { + PutBit(bitStream,repeat_counter,bit_len); + } + bit_count += bit_len; + repeat_counter = 1; + } + else { + repeat_counter++; + } + } + +#ifdef DRM + if (!((previous == 11) || ((previous >= 16) && (previous <= 32)))) +#endif + { + if (writeFlag) + PutBit(bitStream,repeat_counter,bit_len); + bit_count += bit_len; + + if (repeat_counter == max) { /* special case if the last section length is an */ + /* escape sequence */ + if (writeFlag) + PutBit(bitStream,0,bit_len); + bit_count += bit_len; + } + } + } /* Bottom of group iteration */ + + return bit_count; +} + +int WriteScalefactors(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag) + +{ + /* this function takes care of counting the number of bits necessary */ + /* to encode the scalefactors. In addition, if the writeFlag == 1, */ + /* then the scalefactors are written out the bitStream output bit */ + /* stream. it returns k, the number of bits written to the bitstream*/ + + int i,j,bit_count=0; + int diff,length,codeword; + int previous_scale_factor; + int previous_is_factor; /* Intensity stereo */ + int index = 0; + int nr_of_sfb_per_group; + + /* set local pointer to coderInfo elements */ + int* scale_factors = coderInfo->scale_factor; + + if (coderInfo->block_type == ONLY_SHORT_WINDOW) { /* short windows */ + nr_of_sfb_per_group = coderInfo->nr_of_sfb/coderInfo->num_window_groups; + } else { + nr_of_sfb_per_group = coderInfo->nr_of_sfb; + coderInfo->num_window_groups = 1; + coderInfo->window_group_length[0] = 1; + } + + previous_scale_factor = coderInfo->global_gain; + previous_is_factor = 0; + + for(j=0; jnum_window_groups; j++){ + for(i=0;ibook_vector[index]==INTENSITY_HCB) || + (coderInfo->book_vector[index]==INTENSITY_HCB2) ) { + /* only send scalefactors if using non-zero codebooks */ + diff = scale_factors[index] - previous_is_factor; + if ((diff < 60)&&(diff >= -60)) + length = huff12[diff+60][FIRSTINTAB]; + else length = 0; + bit_count+=length; + previous_is_factor = scale_factors[index]; + if (writeFlag == 1 ) { + codeword = huff12[diff+60][LASTINTAB]; + PutBit(bitStream,codeword,length); + } + } else if (coderInfo->book_vector[index]) { + /* only send scalefactors if using non-zero codebooks */ + diff = scale_factors[index] - previous_scale_factor; + if ((diff < 60)&&(diff >= -60)) + length = huff12[diff+60][FIRSTINTAB]; + else length = 0; + bit_count+=length; + previous_scale_factor = scale_factors[index]; + if (writeFlag == 1 ) { + codeword = huff12[diff+60][LASTINTAB]; + PutBit(bitStream,codeword,length); + } + } + index++; + } + } + return bit_count; +} + diff --git a/external/libfaac/libfaac/huffman.h b/external/libfaac/libfaac/huffman.h new file mode 100644 index 0000000..d3bd9c3 --- /dev/null +++ b/external/libfaac/libfaac/huffman.h @@ -0,0 +1,93 @@ +/*********** + +This software module was originally developed by Dolby +Laboratories in the course of development of the MPEG-2 AAC/MPEG-4 +Audio standard ISO/IEC13818-7, 14496-1, 2 and 3. This software module is an implementation of a part +of one or more MPEG-2 AAC/MPEG-4 Audio tools as specified by the +MPEG-2 aac/MPEG-4 Audio standard. ISO/IEC gives users of the +MPEG-2aac/MPEG-4 Audio standards free license to this software module +or modifications thereof for use in hardware or software products +claiming conformance to the MPEG-2 aac/MPEG-4 Audio standards. Those +intending to use this software module in hardware or software products +are advised that this use may infringe existing patents. The original +developer of this software module, the subsequent +editors and their companies, and ISO/IEC have no liability for use of +this software module or modifications thereof in an +implementation. Copyright is not released for non MPEG-2 aac/MPEG-4 +Audio conforming products. The original developer retains full right to +use the code for the developer's own purpose, assign or donate the code to a +third party and to inhibit third party from using the code for non +MPEG-2 aac/MPEG-4 Audio conforming products. This copyright notice +must be included in all copies or derivative works. Copyright 1996. + +***********/ +/* + * $Id: huffman.h,v 1.6 2004/07/12 08:46:43 corrados Exp $ + */ + +#ifndef HUFFMAN_H +#define HUFFMAN_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "bitstream.h" +#include "coder.h" + +/* Huffman tables */ +#define MAXINDEX 289 +#define NUMINTAB 2 +#define FIRSTINTAB 0 +#define LASTINTAB 1 + +#define INTENSITY_HCB 15 +#define INTENSITY_HCB2 14 + + +#define ABS(A) ((A) < 0 ? (-A) : (A)) + +#include "frame.h" + +void HuffmanInit(CoderInfo *coderInfo, unsigned int numChannels); +void HuffmanEnd(CoderInfo *coderInfo, unsigned int numChannels); + +int BitSearch(CoderInfo *coderInfo, + int *quant); + +int NoiselessBitCount(CoderInfo *coderInfo, + int *quant, + int hop, + int min_book_choice[112][3]); + +static int CalculateEscSequence(int input, int *len_esc_sequence); + +int CalcBits(CoderInfo *coderInfo, + int book, + int *quant, + int offset, + int length); + +int OutputBits(CoderInfo *coderInfo, +#ifdef DRM + int *book, /* we need to change book for VCB11 */ +#else + int book, +#endif + int *quant, + int offset, + int length); + +int SortBookNumbers(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); + +int WriteScalefactors(CoderInfo *coderInfo, + BitStream *bitStream, + int writeFlag); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* HUFFMAN_H */ diff --git a/external/libfaac/libfaac/hufftab.h b/external/libfaac/libfaac/hufftab.h new file mode 100644 index 0000000..5892679 --- /dev/null +++ b/external/libfaac/libfaac/hufftab.h @@ -0,0 +1,331 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: hufftab.h,v 1.3 2001/06/08 18:01:09 menno Exp $ + */ + +unsigned short huff1[][2] = { + { 11, 2040}, + { 9, 497},{ 11, 2045},{ 10, 1013},{ 7, 104},{ 10, 1008}, + { 11, 2039},{ 9, 492},{ 11, 2037},{ 10, 1009},{ 7, 114}, + { 10, 1012},{ 7, 116},{ 5, 17},{ 7, 118},{ 9, 491}, + { 7, 108},{ 10, 1014},{ 11, 2044},{ 9, 481},{ 11, 2033}, + { 9, 496},{ 7, 97},{ 9, 502},{ 11, 2034},{ 9, 490}, + { 11, 2043},{ 9, 498},{ 7, 105},{ 9, 493},{ 7, 119}, + { 5, 23},{ 7, 111},{ 9, 486},{ 7, 100},{ 9, 485}, + { 7, 103},{ 5, 21},{ 7, 98},{ 5, 18},{ 1, 0}, + { 5, 20},{ 7, 101},{ 5, 22},{ 7, 109},{ 9, 489}, + { 7, 99},{ 9, 484},{ 7, 107},{ 5, 19},{ 7, 113}, + { 9, 483},{ 7, 112},{ 9, 499},{ 11, 2046},{ 9, 487}, + { 11, 2035},{ 9, 495},{ 7, 96},{ 9, 494},{ 11, 2032}, + { 9, 482},{ 11, 2042},{ 10, 1011},{ 7, 106},{ 9, 488}, + { 7, 117},{ 5, 16},{ 7, 115},{ 9, 500},{ 7, 110}, + { 10, 1015},{ 11, 2038},{ 9, 480},{ 11, 2041},{ 10, 1010}, + { 7, 102},{ 9, 501},{ 11, 2047},{ 9, 503},{ 11, 2036} + }; +unsigned short huff2[][2] = { + { 9, 499}, + { 7, 111},{ 9, 509},{ 8, 235},{ 6, 35},{ 8, 234}, + { 9, 503},{ 8, 232},{ 9, 506},{ 8, 242},{ 6, 45}, + { 7, 112},{ 6, 32},{ 5, 6},{ 6, 43},{ 7, 110}, + { 6, 40},{ 8, 233},{ 9, 505},{ 7, 102},{ 8, 248}, + { 8, 231},{ 6, 27},{ 8, 241},{ 9, 500},{ 7, 107}, + { 9, 501},{ 8, 236},{ 6, 42},{ 7, 108},{ 6, 44}, + { 5, 10},{ 6, 39},{ 7, 103},{ 6, 26},{ 8, 245}, + { 6, 36},{ 5, 8},{ 6, 31},{ 5, 9},{ 3, 0}, + { 5, 7},{ 6, 29},{ 5, 11},{ 6, 48},{ 8, 239}, + { 6, 28},{ 7, 100},{ 6, 30},{ 5, 12},{ 6, 41}, + { 8, 243},{ 6, 47},{ 8, 240},{ 9, 508},{ 7, 113}, + { 9, 498},{ 8, 244},{ 6, 33},{ 8, 230},{ 8, 247}, + { 7, 104},{ 9, 504},{ 8, 238},{ 6, 34},{ 7, 101}, + { 6, 49},{ 4, 2},{ 6, 38},{ 8, 237},{ 6, 37}, + { 7, 106},{ 9, 507},{ 7, 114},{ 9, 510},{ 7, 105}, + { 6, 46},{ 8, 246},{ 9, 511},{ 7, 109},{ 9, 502} + }; +unsigned short huff3[][2] = { + { 1, 0}, + { 4, 9},{ 8, 239},{ 4, 11},{ 5, 25},{ 8, 240}, + { 9, 491},{ 9, 486},{ 10, 1010},{ 4, 10},{ 6, 53}, + { 9, 495},{ 6, 52},{ 6, 55},{ 9, 489},{ 9, 493}, + { 9, 487},{ 10, 1011},{ 9, 494},{ 10, 1005},{ 13, 8186}, + { 9, 492},{ 9, 498},{ 11, 2041},{ 11, 2040},{ 10, 1016}, + { 12, 4088},{ 4, 8},{ 6, 56},{ 10, 1014},{ 6, 54}, + { 7, 117},{ 10, 1009},{ 10, 1003},{ 10, 1004},{ 12, 4084}, + { 5, 24},{ 7, 118},{ 11, 2036},{ 6, 57},{ 7, 116}, + { 10, 1007},{ 9, 499},{ 9, 500},{ 11, 2038},{ 9, 488}, + { 10, 1002},{ 13, 8188},{ 8, 242},{ 9, 497},{ 12, 4091}, + { 10, 1013},{ 11, 2035},{ 12, 4092},{ 8, 238},{ 10, 1015}, + { 15, 32766},{ 9, 496},{ 11, 2037},{ 15, 32765},{ 13, 8187}, + { 14, 16378},{ 16, 65535},{ 8, 241},{ 10, 1008},{ 14, 16380}, + { 9, 490},{ 10, 1006},{ 14, 16379},{ 12, 4086},{ 12, 4090}, + { 15, 32764},{ 11, 2034},{ 12, 4085},{ 16, 65534},{ 10, 1012}, + { 11, 2039},{ 15, 32763},{ 12, 4087},{ 12, 4089},{ 15, 32762} + }; +unsigned short huff4[][2] = { + { 4, 7}, + { 5, 22},{ 8, 246},{ 5, 24},{ 4, 8},{ 8, 239}, + { 9, 495},{ 8, 243},{ 11, 2040},{ 5, 25},{ 5, 23}, + { 8, 237},{ 5, 21},{ 4, 1},{ 8, 226},{ 8, 240}, + { 7, 112},{ 10, 1008},{ 9, 494},{ 8, 241},{ 11, 2042}, + { 8, 238},{ 8, 228},{ 10, 1010},{ 11, 2038},{ 10, 1007}, + { 11, 2045},{ 4, 5},{ 5, 20},{ 8, 242},{ 4, 9}, + { 4, 4},{ 8, 229},{ 8, 244},{ 8, 232},{ 10, 1012}, + { 4, 6},{ 4, 2},{ 8, 231},{ 4, 3},{ 4, 0}, + { 7, 107},{ 8, 227},{ 7, 105},{ 9, 499},{ 8, 235}, + { 8, 230},{ 10, 1014},{ 7, 110},{ 7, 106},{ 9, 500}, + { 10, 1004},{ 9, 496},{ 10, 1017},{ 8, 245},{ 8, 236}, + { 11, 2043},{ 8, 234},{ 7, 111},{ 10, 1015},{ 11, 2041}, + { 10, 1011},{ 12, 4095},{ 8, 233},{ 7, 109},{ 10, 1016}, + { 7, 108},{ 7, 104},{ 9, 501},{ 10, 1006},{ 9, 498}, + { 11, 2036},{ 11, 2039},{ 10, 1009},{ 12, 4094},{ 10, 1005}, + { 9, 497},{ 11, 2037},{ 11, 2046},{ 10, 1013},{ 11, 2044} + }; +unsigned short huff5[][2] = { + { 13, 8191}, + { 12, 4087},{ 11, 2036},{ 11, 2024},{ 10, 1009},{ 11, 2030}, + { 11, 2041},{ 12, 4088},{ 13, 8189},{ 12, 4093},{ 11, 2033}, + { 10, 1000},{ 9, 488},{ 8, 240},{ 9, 492},{ 10, 1006}, + { 11, 2034},{ 12, 4090},{ 12, 4084},{ 10, 1007},{ 9, 498}, + { 8, 232},{ 7, 112},{ 8, 236},{ 9, 496},{ 10, 1002}, + { 11, 2035},{ 11, 2027},{ 9, 491},{ 8, 234},{ 5, 26}, + { 4, 8},{ 5, 25},{ 8, 238},{ 9, 495},{ 11, 2029}, + { 10, 1008},{ 8, 242},{ 7, 115},{ 4, 11},{ 1, 0}, + { 4, 10},{ 7, 113},{ 8, 243},{ 11, 2025},{ 11, 2031}, + { 9, 494},{ 8, 239},{ 5, 24},{ 4, 9},{ 5, 27}, + { 8, 235},{ 9, 489},{ 11, 2028},{ 11, 2038},{ 10, 1003}, + { 9, 499},{ 8, 237},{ 7, 114},{ 8, 233},{ 9, 497}, + { 10, 1005},{ 11, 2039},{ 12, 4086},{ 11, 2032},{ 10, 1001}, + { 9, 493},{ 8, 241},{ 9, 490},{ 10, 1004},{ 11, 2040}, + { 12, 4089},{ 13, 8188},{ 12, 4092},{ 12, 4085},{ 11, 2026}, + { 10, 1011},{ 10, 1010},{ 11, 2037},{ 12, 4091},{ 13, 8190} + }; +unsigned short huff6[][2] = { + { 11, 2046}, + { 10, 1021},{ 9, 497},{ 9, 491},{ 9, 500},{ 9, 490}, + { 9, 496},{ 10, 1020},{ 11, 2045},{ 10, 1014},{ 9, 485}, + { 8, 234},{ 7, 108},{ 7, 113},{ 7, 104},{ 8, 240}, + { 9, 486},{ 10, 1015},{ 9, 499},{ 8, 239},{ 6, 50}, + { 6, 39},{ 6, 40},{ 6, 38},{ 6, 49},{ 8, 235}, + { 9, 503},{ 9, 488},{ 7, 111},{ 6, 46},{ 4, 8}, + { 4, 4},{ 4, 6},{ 6, 41},{ 7, 107},{ 9, 494}, + { 9, 495},{ 7, 114},{ 6, 45},{ 4, 2},{ 4, 0}, + { 4, 3},{ 6, 47},{ 7, 115},{ 9, 506},{ 9, 487}, + { 7, 110},{ 6, 43},{ 4, 7},{ 4, 1},{ 4, 5}, + { 6, 44},{ 7, 109},{ 9, 492},{ 9, 505},{ 8, 238}, + { 6, 48},{ 6, 36},{ 6, 42},{ 6, 37},{ 6, 51}, + { 8, 236},{ 9, 498},{ 10, 1016},{ 9, 484},{ 8, 237}, + { 7, 106},{ 7, 112},{ 7, 105},{ 7, 116},{ 8, 241}, + { 10, 1018},{ 11, 2047},{ 10, 1017},{ 9, 502},{ 9, 493}, + { 9, 504},{ 9, 489},{ 9, 501},{ 10, 1019},{ 11, 2044} + }; +unsigned short huff7[][2] = { + { 1, 0}, + { 3, 5},{ 6, 55},{ 7, 116},{ 8, 242},{ 9, 491}, + { 10, 1005},{ 11, 2039},{ 3, 4},{ 4, 12},{ 6, 53}, + { 7, 113},{ 8, 236},{ 8, 238},{ 9, 494},{ 9, 501}, + { 6, 54},{ 6, 52},{ 7, 114},{ 8, 234},{ 8, 241}, + { 9, 489},{ 9, 499},{ 10, 1013},{ 7, 115},{ 7, 112}, + { 8, 235},{ 8, 240},{ 9, 497},{ 9, 496},{ 10, 1004}, + { 10, 1018},{ 8, 243},{ 8, 237},{ 9, 488},{ 9, 495}, + { 10, 1007},{ 10, 1009},{ 10, 1017},{ 11, 2043},{ 9, 493}, + { 8, 239},{ 9, 490},{ 9, 498},{ 10, 1011},{ 10, 1016}, + { 11, 2041},{ 11, 2044},{ 10, 1006},{ 9, 492},{ 9, 500}, + { 10, 1012},{ 10, 1015},{ 11, 2040},{ 12, 4093},{ 12, 4094}, + { 11, 2038},{ 10, 1008},{ 10, 1010},{ 10, 1014},{ 11, 2042}, + { 11, 2045},{ 12, 4092},{ 12, 4095} + }; +unsigned short huff8[][2] = { + { 5, 14}, + { 4, 5},{ 5, 16},{ 6, 48},{ 7, 111},{ 8, 241}, + { 9, 506},{ 10, 1022},{ 4, 3},{ 3, 0},{ 4, 4}, + { 5, 18},{ 6, 44},{ 7, 106},{ 7, 117},{ 8, 248}, + { 5, 15},{ 4, 2},{ 4, 6},{ 5, 20},{ 6, 46}, + { 7, 105},{ 7, 114},{ 8, 245},{ 6, 47},{ 5, 17}, + { 5, 19},{ 6, 42},{ 6, 50},{ 7, 108},{ 8, 236}, + { 8, 250},{ 7, 113},{ 6, 43},{ 6, 45},{ 6, 49}, + { 7, 109},{ 7, 112},{ 8, 242},{ 9, 505},{ 8, 239}, + { 7, 104},{ 6, 51},{ 7, 107},{ 7, 110},{ 8, 238}, + { 8, 249},{ 10, 1020},{ 9, 504},{ 7, 116},{ 7, 115}, + { 8, 237},{ 8, 240},{ 8, 246},{ 9, 502},{ 9, 509}, + { 10, 1021},{ 8, 243},{ 8, 244},{ 8, 247},{ 9, 503}, + { 9, 507},{ 9, 508},{ 10, 1023} + }; +unsigned short huff9[][2] = { + { 1, 0}, + { 3, 5},{ 6, 55},{ 8, 231},{ 9, 478},{ 10, 974}, + { 10, 985},{ 11, 1992},{ 11, 1997},{ 12, 4040},{ 12, 4061}, + { 13, 8164},{ 13, 8172},{ 3, 4},{ 4, 12},{ 6, 53}, + { 7, 114},{ 8, 234},{ 8, 237},{ 9, 482},{ 10, 977}, + { 10, 979},{ 10, 992},{ 11, 2008},{ 12, 4047},{ 12, 4053}, + { 6, 54},{ 6, 52},{ 7, 113},{ 8, 232},{ 8, 236}, + { 9, 481},{ 10, 975},{ 10, 989},{ 10, 987},{ 11, 2000}, + { 12, 4039},{ 12, 4052},{ 12, 4068},{ 8, 230},{ 7, 112}, + { 8, 233},{ 9, 477},{ 9, 483},{ 10, 978},{ 10, 988}, + { 11, 1996},{ 11, 1994},{ 11, 2014},{ 12, 4056},{ 12, 4074}, + { 13, 8155},{ 9, 479},{ 8, 235},{ 9, 476},{ 9, 486}, + { 10, 981},{ 10, 990},{ 11, 1995},{ 11, 2013},{ 11, 2012}, + { 12, 4045},{ 12, 4066},{ 12, 4071},{ 13, 8161},{ 10, 976}, + { 9, 480},{ 9, 484},{ 10, 982},{ 11, 1989},{ 11, 2001}, + { 11, 2011},{ 12, 4050},{ 11, 2016},{ 12, 4057},{ 12, 4075}, + { 13, 8163},{ 13, 8169},{ 11, 1988},{ 9, 485},{ 10, 983}, + { 11, 1990},{ 11, 1999},{ 11, 2010},{ 12, 4043},{ 12, 4058}, + { 12, 4067},{ 12, 4073},{ 13, 8166},{ 13, 8179},{ 13, 8183}, + { 11, 2003},{ 10, 984},{ 10, 993},{ 11, 2004},{ 11, 2009}, + { 12, 4051},{ 12, 4062},{ 13, 8157},{ 13, 8153},{ 13, 8162}, + { 13, 8170},{ 13, 8177},{ 13, 8182},{ 11, 2002},{ 10, 980}, + { 10, 986},{ 11, 1991},{ 11, 2007},{ 11, 2018},{ 12, 4046}, + { 12, 4059},{ 13, 8152},{ 13, 8174},{ 14, 16368},{ 13, 8180}, + { 14, 16370},{ 11, 2017},{ 10, 991},{ 11, 1993},{ 11, 2006}, + { 12, 4042},{ 12, 4048},{ 12, 4069},{ 12, 4070},{ 13, 8171}, + { 13, 8175},{ 14, 16371},{ 14, 16372},{ 14, 16373},{ 12, 4064}, + { 11, 1998},{ 11, 2005},{ 12, 4038},{ 12, 4049},{ 12, 4065}, + { 13, 8160},{ 13, 8168},{ 13, 8176},{ 14, 16369},{ 14, 16376}, + { 14, 16374},{ 15, 32764},{ 12, 4072},{ 11, 2015},{ 12, 4041}, + { 12, 4055},{ 12, 4060},{ 13, 8156},{ 13, 8159},{ 13, 8173}, + { 13, 8181},{ 14, 16377},{ 14, 16379},{ 15, 32765},{ 15, 32766}, + { 13, 8167},{ 12, 4044},{ 12, 4054},{ 12, 4063},{ 13, 8158}, + { 13, 8154},{ 13, 8165},{ 13, 8178},{ 14, 16378},{ 14, 16375}, + { 14, 16380},{ 14, 16381},{ 15, 32767} + }; +unsigned short huff10[][2] = { + { 6, 34}, + { 5, 8},{ 6, 29},{ 6, 38},{ 7, 95},{ 8, 211}, + { 9, 463},{ 10, 976},{ 10, 983},{ 10, 1005},{ 11, 2032}, + { 11, 2038},{ 12, 4093},{ 5, 7},{ 4, 0},{ 4, 1}, + { 5, 9},{ 6, 32},{ 7, 84},{ 7, 96},{ 8, 213}, + { 8, 220},{ 9, 468},{ 10, 973},{ 10, 990},{ 11, 2023}, + { 6, 28},{ 4, 2},{ 5, 6},{ 5, 12},{ 6, 30}, + { 6, 40},{ 7, 91},{ 8, 205},{ 8, 217},{ 9, 462}, + { 9, 476},{ 10, 985},{ 10, 1009},{ 6, 37},{ 5, 11}, + { 5, 10},{ 5, 13},{ 6, 36},{ 7, 87},{ 7, 97}, + { 8, 204},{ 8, 221},{ 9, 460},{ 9, 478},{ 10, 979}, + { 10, 999},{ 7, 93},{ 6, 33},{ 6, 31},{ 6, 35}, + { 6, 39},{ 7, 89},{ 7, 100},{ 8, 216},{ 8, 223}, + { 9, 466},{ 9, 482},{ 10, 989},{ 10, 1006},{ 8, 209}, + { 7, 85},{ 6, 41},{ 7, 86},{ 7, 88},{ 7, 98}, + { 8, 206},{ 8, 224},{ 8, 226},{ 9, 474},{ 10, 980}, + { 10, 995},{ 11, 2027},{ 9, 457},{ 7, 94},{ 7, 90}, + { 7, 92},{ 7, 99},{ 8, 202},{ 8, 218},{ 9, 455}, + { 9, 458},{ 9, 480},{ 10, 987},{ 10, 1000},{ 11, 2028}, + { 9, 483},{ 8, 210},{ 8, 203},{ 8, 208},{ 8, 215}, + { 8, 219},{ 9, 454},{ 9, 469},{ 9, 472},{ 10, 970}, + { 10, 986},{ 11, 2026},{ 11, 2033},{ 9, 481},{ 8, 212}, + { 8, 207},{ 8, 214},{ 8, 222},{ 8, 225},{ 9, 464}, + { 9, 470},{ 10, 977},{ 10, 981},{ 10, 1010},{ 11, 2030}, + { 11, 2043},{ 10, 1001},{ 9, 461},{ 9, 456},{ 9, 459}, + { 9, 465},{ 9, 471},{ 9, 479},{ 10, 975},{ 10, 992}, + { 10, 1007},{ 11, 2022},{ 11, 2040},{ 12, 4090},{ 10, 1003}, + { 9, 477},{ 9, 467},{ 9, 473},{ 9, 475},{ 10, 978}, + { 10, 972},{ 10, 988},{ 10, 1002},{ 11, 2029},{ 11, 2035}, + { 11, 2041},{ 12, 4089},{ 11, 2034},{ 10, 974},{ 9, 484}, + { 10, 971},{ 10, 984},{ 10, 982},{ 10, 994},{ 10, 997}, + { 11, 2024},{ 11, 2036},{ 11, 2037},{ 11, 2039},{ 12, 4091}, + { 11, 2042},{ 10, 1004},{ 10, 991},{ 10, 993},{ 10, 996}, + { 10, 998},{ 10, 1008},{ 11, 2025},{ 11, 2031},{ 12, 4088}, + { 12, 4094},{ 12, 4092},{ 12, 4095} + }; +unsigned short huff11[][2] = { + { 4, 0}, + { 5, 6},{ 6, 25},{ 7, 61},{ 8, 156},{ 8, 198}, + { 9, 423},{ 10, 912},{ 10, 962},{ 10, 991},{ 11, 2022}, + { 11, 2035},{ 12, 4091},{ 11, 2028},{ 12, 4090},{ 12, 4094}, + { 10, 910},{ 5, 5},{ 4, 1},{ 5, 8},{ 6, 20}, + { 7, 55},{ 7, 66},{ 8, 146},{ 8, 175},{ 9, 401}, + { 9, 421},{ 9, 437},{ 10, 926},{ 10, 960},{ 10, 930}, + { 10, 973},{ 11, 2006},{ 8, 174},{ 6, 23},{ 5, 7}, + { 5, 9},{ 6, 24},{ 7, 57},{ 7, 64},{ 8, 142}, + { 8, 163},{ 8, 184},{ 9, 409},{ 9, 428},{ 9, 449}, + { 10, 945},{ 10, 918},{ 10, 958},{ 10, 970},{ 8, 157}, + { 7, 60},{ 6, 21},{ 6, 22},{ 6, 26},{ 7, 59}, + { 7, 68},{ 8, 145},{ 8, 165},{ 8, 190},{ 9, 406}, + { 9, 430},{ 9, 441},{ 10, 929},{ 10, 913},{ 10, 933}, + { 10, 981},{ 8, 148},{ 8, 154},{ 7, 54},{ 7, 56}, + { 7, 58},{ 7, 65},{ 8, 140},{ 8, 155},{ 8, 176}, + { 8, 195},{ 9, 414},{ 9, 427},{ 9, 444},{ 10, 927}, + { 10, 911},{ 10, 937},{ 10, 975},{ 8, 147},{ 8, 191}, + { 7, 62},{ 7, 63},{ 7, 67},{ 7, 69},{ 8, 158}, + { 8, 167},{ 8, 185},{ 9, 404},{ 9, 418},{ 9, 442}, + { 9, 451},{ 10, 934},{ 10, 935},{ 10, 955},{ 10, 980}, + { 8, 159},{ 9, 416},{ 8, 143},{ 8, 141},{ 8, 144}, + { 8, 152},{ 8, 166},{ 8, 182},{ 8, 196},{ 9, 415}, + { 9, 431},{ 9, 447},{ 10, 921},{ 10, 959},{ 10, 948}, + { 10, 969},{ 10, 999},{ 8, 168},{ 9, 438},{ 8, 171}, + { 8, 164},{ 8, 170},{ 8, 178},{ 8, 194},{ 8, 197}, + { 9, 408},{ 9, 420},{ 9, 440},{ 10, 908},{ 10, 932}, + { 10, 964},{ 10, 966},{ 10, 989},{ 10, 1000},{ 8, 173}, + { 10, 943},{ 9, 402},{ 8, 189},{ 8, 188},{ 9, 398}, + { 9, 407},{ 9, 410},{ 9, 419},{ 9, 433},{ 10, 909}, + { 10, 920},{ 10, 951},{ 10, 979},{ 10, 977},{ 10, 987}, + { 11, 2013},{ 8, 180},{ 10, 990},{ 9, 425},{ 9, 411}, + { 9, 412},{ 9, 417},{ 9, 426},{ 9, 429},{ 9, 435}, + { 10, 907},{ 10, 946},{ 10, 952},{ 10, 974},{ 10, 993}, + { 10, 992},{ 11, 2002},{ 11, 2021},{ 8, 183},{ 11, 2019}, + { 9, 443},{ 9, 424},{ 9, 422},{ 9, 432},{ 9, 434}, + { 9, 439},{ 10, 923},{ 10, 922},{ 10, 954},{ 10, 949}, + { 10, 982},{ 11, 2007},{ 10, 996},{ 11, 2008},{ 11, 2026}, + { 8, 186},{ 11, 2024},{ 10, 928},{ 9, 445},{ 9, 436}, + { 10, 906},{ 9, 452},{ 10, 914},{ 10, 938},{ 10, 944}, + { 10, 956},{ 10, 983},{ 11, 2004},{ 11, 2012},{ 11, 2011}, + { 11, 2005},{ 11, 2032},{ 8, 193},{ 11, 2043},{ 10, 968}, + { 10, 931},{ 10, 917},{ 10, 925},{ 10, 940},{ 10, 942}, + { 10, 965},{ 10, 984},{ 10, 994},{ 10, 998},{ 11, 2020}, + { 11, 2023},{ 11, 2016},{ 11, 2025},{ 11, 2039},{ 9, 400}, + { 11, 2034},{ 10, 915},{ 9, 446},{ 9, 448},{ 10, 916}, + { 10, 919},{ 10, 941},{ 10, 963},{ 10, 961},{ 10, 978}, + { 11, 2010},{ 11, 2009},{ 11, 2015},{ 11, 2027},{ 11, 2036}, + { 11, 2042},{ 9, 405},{ 11, 2040},{ 10, 957},{ 10, 924}, + { 10, 939},{ 10, 936},{ 10, 947},{ 10, 953},{ 10, 976}, + { 10, 995},{ 10, 997},{ 11, 2018},{ 11, 2014},{ 11, 2029}, + { 11, 2033},{ 11, 2041},{ 11, 2044},{ 9, 403},{ 12, 4093}, + { 10, 988},{ 10, 950},{ 10, 967},{ 10, 972},{ 10, 971}, + { 10, 985},{ 10, 986},{ 11, 2003},{ 11, 2017},{ 11, 2030}, + { 11, 2031},{ 11, 2037},{ 11, 2038},{ 12, 4092},{ 12, 4095}, + { 9, 413},{ 9, 450},{ 8, 181},{ 8, 161},{ 8, 150}, + { 8, 151},{ 8, 149},{ 8, 153},{ 8, 160},{ 8, 162}, + { 8, 172},{ 8, 169},{ 8, 177},{ 8, 179},{ 8, 187}, + { 8, 192},{ 9, 399},{ 5, 4} + }; +unsigned int huff12[][2] = { + { 18, 262120}, + { 18, 262118},{ 18, 262119},{ 18, 262117},{ 19, 524277},{ 19, 524273}, + { 19, 524269},{ 19, 524278},{ 19, 524270},{ 19, 524271},{ 19, 524272}, + { 19, 524284},{ 19, 524285},{ 19, 524287},{ 19, 524286},{ 19, 524279}, + { 19, 524280},{ 19, 524283},{ 19, 524281},{ 18, 262116},{ 19, 524282}, + { 18, 262115},{ 17, 131055},{ 17, 131056},{ 16, 65525},{ 17, 131054}, + { 16, 65522},{ 16, 65523},{ 16, 65524},{ 16, 65521},{ 15, 32758}, + { 15, 32759},{ 14, 16377},{ 14, 16373},{ 14, 16375},{ 14, 16371}, + { 14, 16374},{ 14, 16370},{ 13, 8183},{ 13, 8181},{ 12, 4089}, + { 12, 4087},{ 12, 4086},{ 11, 2041},{ 12, 4084},{ 11, 2040}, + { 10, 1017},{ 10, 1015},{ 10, 1013},{ 9, 504},{ 9, 503}, + { 8, 250},{ 8, 248},{ 8, 246},{ 7, 121},{ 6, 58}, + { 6, 56},{ 5, 26},{ 4, 11},{ 3, 4},{ 1, 0}, + { 4, 10},{ 4, 12},{ 5, 27},{ 6, 57},{ 6, 59}, + { 7, 120},{ 7, 122},{ 8, 247},{ 8, 249},{ 9, 502}, + { 9, 505},{ 10, 1012},{ 10, 1014},{ 10, 1016},{ 11, 2037}, + { 11, 2036},{ 11, 2038},{ 11, 2039},{ 12, 4085},{ 12, 4088}, + { 13, 8180},{ 13, 8182},{ 13, 8184},{ 14, 16376},{ 14, 16372}, + { 16, 65520},{ 15, 32756},{ 16, 65526},{ 15, 32757},{ 18, 262114}, + { 19, 524249},{ 19, 524250},{ 19, 524251},{ 19, 524252},{ 19, 524253}, + { 19, 524254},{ 19, 524248},{ 19, 524242},{ 19, 524243},{ 19, 524244}, + { 19, 524245},{ 19, 524246},{ 19, 524274},{ 19, 524255},{ 19, 524263}, + { 19, 524264},{ 19, 524265},{ 19, 524266},{ 19, 524267},{ 19, 524262}, + { 19, 524256},{ 19, 524257},{ 19, 524258},{ 19, 524259},{ 19, 524260}, + { 19, 524261},{ 19, 524247},{ 19, 524268},{ 19, 524276},{ 19, 524275} + }; + + diff --git a/external/libfaac/libfaac/kiss_fft/CHANGELOG b/external/libfaac/libfaac/kiss_fft/CHANGELOG new file mode 100644 index 0000000..49446ff --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/CHANGELOG @@ -0,0 +1,58 @@ +1.2.1 (April 4, 2004) + compiles cleanly with just about every -W warning flag under the sun + + reorganized kiss_fft_state so it could be read-only/const. This may be useful for embedded systems + that are willing to predeclare twiddle factors, factorization. + + Fixed C_MUL,S_MUL on 16-bit platforms. + + tmpbuf will only be allocated if input & output buffers are same + scratchbuf will only be allocated for ffts that are not multiples of 2,3,5 + + NOTE: The tmpbuf,scratchbuf changes may require synchronization code for multi-threaded apps. + + +1.2 (Feb 23, 2004) + interface change -- cfg object is forward declaration of struct instead of void* + This maintains type saftey and lets the compiler warn/error about stupid mistakes. + (prompted by suggestion from Erik de Castro Lopo) + + small speed improvements + + added psdpng.c -- sample utility that will create png spectrum "waterfalls" from an input file + ( not terribly useful yet) + +1.1.1 (Feb 1, 2004 ) + minor bug fix -- only affects odd rank, in-place, multi-dimensional FFTs + +1.1 : (Jan 30,2004) + split sample_code/ into test/ and tools/ + + Removed 2-D fft and added N-D fft (arbitrary) + + modified fftutil.c to allow multi-d FFTs + + Modified core fft routine to allow an input stride via kiss_fft_stride() + (eased support of multi-D ffts) + + Added fast convolution filtering (FIR filtering using overlap-scrap method, with tail scrap) + + Add kfc.[ch]: the KISS FFT Cache. It takes care of allocs for you ( suggested by Oscar Lesta ). + +1.0.1 (Dec 15, 2003) + fixed bug that occurred when nfft==1 + +1.0 : (Dec 14, 2003) + changed kiss_fft function from using a single buffer, to two buffers. + If the same buffer pointer is supplied for both in and out, kiss will + manage the buffer copies. + + added kiss_fft2d and kiss_fftr as separate source files (declarations in kiss_fft.h ) + +0.4 :(Nov 4,2003) optimized for radix 2,3,4,5 + +0.3 :(Oct 28, 2003) woops, version 2 didn't actually factor out any radices other than 2 + +0.2 :(Oct 27, 2003) added mixed radix, only radix 2,4 optimized versions + +0.1 :(May 19 2003) initial release, radix 2 only diff --git a/external/libfaac/libfaac/kiss_fft/COPYING b/external/libfaac/libfaac/kiss_fft/COPYING new file mode 100644 index 0000000..472830e --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/COPYING @@ -0,0 +1,11 @@ +Copyright (c) 2003-2004 Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/libfaac/libfaac/kiss_fft/README b/external/libfaac/libfaac/kiss_fft/README new file mode 100644 index 0000000..f5ac197 --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/README @@ -0,0 +1,109 @@ +KISS FFT - A mixed-radix Fast Fourier Transform based up on the principle, +"Keep It Simple, Stupid." + + There are many great fft libraries already around. Kiss FFT is not trying +to be better than any of them. It only attempts to be a reasonably efficient, +moderately useful FFT that can use fixed or floating data types and can be +incorporated into someone's C program in a few minutes with trivial licensing. + +USAGE: + + The basic usage for 1-d complex FFT is: + + #include "kiss_fft.h" + + kiss_fft_cfg cfg = kiss_fft_alloc( nfft ,inverse_fft ); + + while ... + + ... // put kth sample in cx_in[k].r and cx_in[k].i + + kiss_fft( cfg , cx_in , cx_out ); + + ... // transformed. DC is in cx_out[0].r and cx_out[0].i + + free(cfg); + + Note: frequency-domain data is stored from dc up to 2pi. + so cx_out[0] is the dc bin of the FFT + and cx_out[nfft/2] is the Nyquist bin (if exists) + + Declarations are in "kiss_fft.h", along with a brief description of the +functions you'll need to use. + +Code definitions for 1d complex FFTs are in kiss_fft.c. + +You can do other cool stuff with the extras you'll find in tools/ + + * arbitrary dimension complex FFTs + * 1-d real FFTs + * fast convolution FIR filtering + * spectrum image creation + +The core fft and most tools/ code can be compiled to use float, double +or Q15 short samples. The default is float. + + +BACKGROUND: + + I started coding this because I couldn't find a fixed point FFT that didn't +use assembly code. I started with floating point numbers so I could get the +theory straight before working on fixed point issues. In the end, I had a +little bit of code that could be recompiled easily to do ffts with short, float +or double (other types should be easy too). + + Once I got my FFT working, I was curious about the speed compared to +a well respected and highly optimized fft library. I don't want to criticize +this great library, so let's call it FFT_BRANDX. +During this process, I learned: + + 1. FFT_BRANDX has more than 100K lines of code. The core of kiss_fft is about 500 lines (cpx 1-d). + 2. It took me an embarrassingly long time to get FFT_BRANDX working. + 3. A simple program using FFT_BRANDX is 522KB. A similar program using kiss_fft is 18KB. + 4. FFT_BRANDX is roughly twice as fast as KISS FFT. + + It is wonderful that free, highly optimized libraries like FFT_BRANDX exist. +But such libraries carry a huge burden of complexity necessary to extract every +last bit of performance. + + Sometimes simpler is better, even if it's not better. + +PERFORMANCE: + (on Athlon XP 2100+, with gcc 2.96, float data type) + + Kiss performed 10000 1024-pt cpx ffts in .63 s of cpu time. + For comparison, it took md5sum twice as long to process the same amount of data. + + Transforming 5 minutes of CD quality audio takes less than a second (nfft=1024). + +DO NOT: + ... use Kiss if you need the Fastest Fourier Transform in the World + ... ask me to add features that will bloat the code + +UNDER THE HOOD: + + Kiss FFT uses a time decimation, mixed-radix, out-of-place FFT. +No scaling is done. Optimized butterflies are used for factors 2,3,4, and 5. + + The real optimization code only works for even length ffts. It does two half-length + FFTs in parallel (packed into real&imag), and then combines them via twiddling. + + The fast convolution filtering uses the overlap-scrap method, slightly + modified to put the scrap at the tail. + +LICENSE: + BSD, see COPYING for details. Basically, "free to use&change, give credit where due, no guarantees" + +TODO: + *) Add real optimization for odd length FFTs (DST?) + *) Add real optimization to the n-dimensional FFT + *) Add simple windowing function, e.g. Hamming : w(i)=.54-.46*cos(2pi*i/(n-1)) + *) Make the fixed point scaling and bit shifts more easily configurable. + *) Document/revisit the input/output fft scaling + *) See if the fixed point code can be optimized a little without adding complexity. + *) Make doc describing the overlap (tail) scrap fast convolution filtering in kiss_fastfir.c + *) Test all the ./tools/ code with fixed point (kiss_fastfir.c doesn't work, maybe others) + +AUTHOR: + Mark Borgerding + Mark@Borgerding.net diff --git a/external/libfaac/libfaac/kiss_fft/README.kiss_fft b/external/libfaac/libfaac/kiss_fft/README.kiss_fft new file mode 100644 index 0000000..4197ffb --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/README.kiss_fft @@ -0,0 +1,6 @@ +See README and COPYING files for author and copyright information. + +kiss_fft.c is modified in order to eliminate static variables. + +-- sur. + diff --git a/external/libfaac/libfaac/kiss_fft/TIPS b/external/libfaac/libfaac/kiss_fft/TIPS new file mode 100644 index 0000000..0709032 --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/TIPS @@ -0,0 +1,23 @@ +Speed: + * experiment with compiler flags + Special thanks to Oscar Lesta. He suggested some compiler flags + for gcc that make a big difference. They shave 10-15% off + execution time on some systems. Try some combination of: + -march=pentiumpro + -ffast-math + -fomit-frame-pointer + + * If the input data has no imaginary component, use the kiss_fftr code under tools/. + Real ffts are roughly twice as fast as complex. + +Reducing code size: + * remove some of the butterflies. There are currently butterflies optimized for radices + 2,3,4,5. It is worth mentioning that you can still use FFT sizes that contain + these factors, they just won't be quite as fast. You can decide for yourself + whether to keep radix 2 or 4. If you do some work in this area, let me + know what you find. + + * For platforms where ROM/code space is more plentiful than RAM, + consider creating a hardcoded kiss_fft_state. In other words, decide which + FFT size(s) you want and make a structure with the correct factors and twiddles. + diff --git a/external/libfaac/libfaac/kiss_fft/_kiss_fft_guts.h b/external/libfaac/libfaac/kiss_fft/_kiss_fft_guts.h new file mode 100644 index 0000000..c572473 --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/_kiss_fft_guts.h @@ -0,0 +1,97 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* kiss_fft.h + defines kiss_fft_scalar as either short or a float type + and defines + typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ +#include "kiss_fft.h" + + +#define MAXFACTORS 32 +/* e.g. an fft of length 128 has 4 factors + as far as kissfft is concerned + 4*4*4*2 + */ + +struct kiss_fft_state{ + int nfft; + int inverse; + int factors[2*MAXFACTORS]; + kiss_fft_cpx twiddles[1]; +}; + +/* + Explanation of macros dealing with complex math: + + C_MUL(m,a,b) : m = a*b + C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise + C_SUB( res, a,b) : res = a - b + C_SUBFROM( res , a) : res -= a + C_ADDTO( res , a) : res += a + * */ +#ifdef FIXED_POINT + +# define smul(a,b) ( (long)(a)*(b) ) +# define sround( x ) (short)( ( (x) + (1<<14) ) >>15 ) + +# define S_MUL(a,b) sround( smul(a,b) ) + +# define C_MUL(m,a,b) \ + do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ + (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) + +# define C_FIXDIV(c,div) \ + do{ (c).r /= div; (c).i /=div; }while(0) + +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r = sround( smul( (c).r , s ) ) ;\ + (c).i = sround( smul( (c).i , s ) ) ; }while(0) + +#else /* not FIXED_POINT*/ + +# define S_MUL(a,b) ( (a)*(b) ) +#define C_MUL(m,a,b) \ + do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ + (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) +# define C_FIXDIV(c,div) /* NOOP */ +# define C_MULBYSCALAR( c, s ) \ + do{ (c).r *= (s);\ + (c).i *= (s); }while(0) +#endif + +#define C_ADD( res, a,b)\ + do { (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; }while(0) +#define C_SUB( res, a,b)\ + do { (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; }while(0) +#define C_ADDTO( res , a)\ + do { (res).r += (a).r; (res).i += (a).i; }while(0) +#define C_SUBFROM( res , a)\ + do { (res).r -= (a).r; (res).i -= (a).i; }while(0) + +static +void kf_cexp(kiss_fft_cpx * x,double phase) /* returns e ** (j*phase) */ +{ +#ifdef FIXED_POINT + x->r = (kiss_fft_scalar) (32767 * cos (phase)); + x->i = (kiss_fft_scalar) (32767 * sin (phase)); +#else + x->r = cos (phase); + x->i = sin (phase); +#endif +} + +/* a debugging function */ +#define pcpx(c)\ + fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) diff --git a/external/libfaac/libfaac/kiss_fft/kiss_fft.c b/external/libfaac/libfaac/kiss_fft/kiss_fft.c new file mode 100644 index 0000000..1634cd8 --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/kiss_fft.c @@ -0,0 +1,362 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "_kiss_fft_guts.h" +/* The guts header contains all the multiplication and addition macros that are defined for + fixed or floating point complex numbers. It also delares the kf_ internal functions. + */ + +static void kf_bfly2( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m + ) +{ + kiss_fft_cpx * Fout2; + kiss_fft_cpx * tw1 = st->twiddles; + kiss_fft_cpx t; + Fout2 = Fout + m; + do{ + C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); + + C_MUL (t, *Fout2 , *tw1); + tw1 += fstride; + C_SUB( *Fout2 , *Fout , t ); + C_ADDTO( *Fout , t ); + ++Fout2; + ++Fout; + }while (--m); +} + +static void kf_bfly4( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + const size_t m + ) +{ + kiss_fft_cpx *tw1,*tw2,*tw3; + kiss_fft_cpx scratch[6]; + size_t k=m; + const size_t m2=2*m; + const size_t m3=3*m; + + tw3 = tw2 = tw1 = st->twiddles; + + do { + C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4); + + C_MUL(scratch[0],Fout[m] , *tw1 ); + C_MUL(scratch[1],Fout[m2] , *tw2 ); + C_MUL(scratch[2],Fout[m3] , *tw3 ); + + C_SUB( scratch[5] , *Fout, scratch[1] ); + C_ADDTO(*Fout, scratch[1]); + C_ADD( scratch[3] , scratch[0] , scratch[2] ); + C_SUB( scratch[4] , scratch[0] , scratch[2] ); + C_SUB( Fout[m2], *Fout, scratch[3] ); + tw1 += fstride; + tw2 += fstride*2; + tw3 += fstride*3; + C_ADDTO( *Fout , scratch[3] ); + + if(st->inverse) { + Fout[m].r = scratch[5].r - scratch[4].i; + Fout[m].i = scratch[5].i + scratch[4].r; + Fout[m3].r = scratch[5].r + scratch[4].i; + Fout[m3].i = scratch[5].i - scratch[4].r; + }else{ + Fout[m].r = scratch[5].r + scratch[4].i; + Fout[m].i = scratch[5].i - scratch[4].r; + Fout[m3].r = scratch[5].r - scratch[4].i; + Fout[m3].i = scratch[5].i + scratch[4].r; + } + ++Fout; + }while(--k); +} + +static void kf_bfly3( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + size_t m + ) +{ + size_t k=m; + const size_t m2 = 2*m; + kiss_fft_cpx *tw1,*tw2; + kiss_fft_cpx scratch[5]; + kiss_fft_cpx epi3; + epi3 = st->twiddles[fstride*m]; + + tw1=tw2=st->twiddles; + + do{ + C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); + + C_MUL(scratch[1],Fout[m] , *tw1); + C_MUL(scratch[2],Fout[m2] , *tw2); + + C_ADD(scratch[3],scratch[1],scratch[2]); + C_SUB(scratch[0],scratch[1],scratch[2]); + tw1 += fstride; + tw2 += fstride*2; + + Fout[m].r = Fout->r - scratch[3].r/2; + Fout[m].i = Fout->i - scratch[3].i/2; + + C_MULBYSCALAR( scratch[0] , epi3.i ); + + C_ADDTO(*Fout,scratch[3]); + + Fout[m2].r = Fout[m].r + scratch[0].i; + Fout[m2].i = Fout[m].i - scratch[0].r; + + Fout[m].r -= scratch[0].i; + Fout[m].i += scratch[0].r; + + ++Fout; + }while(--k); +} + +static void kf_bfly5( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m + ) +{ + kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; + int u; + kiss_fft_cpx scratch[13]; + kiss_fft_cpx * twiddles = st->twiddles; + kiss_fft_cpx *tw; + kiss_fft_cpx ya,yb; + ya = twiddles[fstride*m]; + yb = twiddles[fstride*2*m]; + + Fout0=Fout; + Fout1=Fout0+m; + Fout2=Fout0+2*m; + Fout3=Fout0+3*m; + Fout4=Fout0+4*m; + + tw=st->twiddles; + for ( u=0; ur += scratch[7].r + scratch[8].r; + Fout0->i += scratch[7].i + scratch[8].i; + + scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); + scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); + + scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); + scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); + + C_SUB(*Fout1,scratch[5],scratch[6]); + C_ADD(*Fout4,scratch[5],scratch[6]); + + scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); + scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); + scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); + scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); + + C_ADD(*Fout2,scratch[11],scratch[12]); + C_SUB(*Fout3,scratch[11],scratch[12]); + + ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; + } +} + +/* perform the butterfly for one stage of a mixed radix FFT */ +static void kf_bfly_generic( + kiss_fft_cpx * Fout, + const size_t fstride, + const kiss_fft_cfg st, + int m, + int p + ) +{ + int u,k,q1,q; + kiss_fft_cpx * twiddles = st->twiddles; + kiss_fft_cpx t; + int Norig = st->nfft; + + kiss_fft_cpx *scratchbuf=(kiss_fft_cpx *)malloc( sizeof(kiss_fft_cpx) * p ); + + for ( u=0; u=Norig) twidx-=Norig; + C_MUL(t,scratchbuf[q] , twiddles[twidx] ); + C_ADDTO( Fout[ k ] ,t); + } + k += m; + } + } + + free( scratchbuf ); +} + +static +void kf_work( + kiss_fft_cpx * Fout, + const kiss_fft_cpx * f, + const size_t fstride, + int in_stride, + int * factors, + const kiss_fft_cfg st + ) +{ + kiss_fft_cpx * Fout_beg=Fout; + const int p=*factors++; /* the radix */ + const int m=*factors++; /* stage's fft length/p */ + const kiss_fft_cpx * Fout_end = Fout + p*m; + + if (m==1) { + do{ + *Fout = *f; + f += fstride*in_stride; + }while(++Fout != Fout_end ); + }else{ + do{ + kf_work( Fout , f, fstride*p, in_stride, factors,st); + f += fstride*in_stride; + }while( (Fout += m) != Fout_end ); + } + + Fout=Fout_beg; + + switch (p) { + case 2: kf_bfly2(Fout,fstride,st,m); break; + case 3: kf_bfly3(Fout,fstride,st,m); break; + case 4: kf_bfly4(Fout,fstride,st,m); break; + case 5: kf_bfly5(Fout,fstride,st,m); break; + default: kf_bfly_generic(Fout,fstride,st,m,p); break; + } +} + +/* facbuf is populated by p1,m1,p2,m2, ... + where + p[i] * m[i] = m[i-1] + m0 = n */ +static +void kf_factor(int n,int * facbuf) +{ + int p=4; + double floor_sqrt; + floor_sqrt = floor( sqrt((double)n) ); + + /*factor out powers of 4, powers of 2, then any remaining primes */ + do { + while (n % p) { + switch (p) { + case 4: p = 2; break; + case 2: p = 3; break; + default: p += 2; break; + } + if (p > floor_sqrt) + p = n; /* no more factors, skip to end */ + } + n /= p; + *facbuf++ = p; + *facbuf++ = n; + } while (n > 1); +} + +/* + * + * User-callable function to allocate all necessary storage space for the fft. + * + * The return value is a contiguous block of memory, allocated with malloc. As such, + * It can be freed with free(), rather than a kiss_fft-specific function. + * */ +kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) +{ + kiss_fft_cfg st=NULL; + size_t memneeded = sizeof(struct kiss_fft_state) + + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ + + if ( lenmem==NULL ) { + st = ( kiss_fft_cfg)malloc( memneeded ); + }else{ + if (*lenmem >= memneeded) + st = (kiss_fft_cfg)mem; + *lenmem = memneeded; + } + if (st) { + int i; + const double pi=3.14159265358979323846264338327; + const double phase0 = -2.0 * pi / (double)( nfft ); + + st->nfft=nfft; + st->inverse = inverse_fft; + + for (i=0;iinverse) + phase *= -1; + kf_cexp(st->twiddles+i, phase ); + } + + kf_factor(nfft,st->factors); + } + return st; +} + + + + +void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) +{ + if (fin == fout) { + kiss_fft_cpx *tmpbuf=(kiss_fft_cpx*)malloc( sizeof(kiss_fft_cpx) * st->nfft ); + kf_work(tmpbuf,fin,1,in_stride, st->factors,st); + memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); + free( tmpbuf ); + }else{ + kf_work( fout, fin, 1,in_stride, st->factors,st ); + } +} + +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) +{ + kiss_fft_stride(cfg,fin,fout,1); +} + diff --git a/external/libfaac/libfaac/kiss_fft/kiss_fft.h b/external/libfaac/libfaac/kiss_fft/kiss_fft.h new file mode 100644 index 0000000..146a45d --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/kiss_fft.h @@ -0,0 +1,94 @@ +/* + Copyright (c) 2013-2016 EasyDarwin.ORG. All rights reserved. + Github: https://github.com/EasyDarwin + WEChat: EasyDarwin + Website: http://www.easydarwin.org +*/ +#ifndef KISS_FFT_H +#define KISS_FFT_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ATTENTION! + If you would like a : + -- a utility that will handle the caching of fft objects + -- real-only FFT + -- a multi-dimensional FFT + -- a command-line utility to perform ffts + -- a command-line utility to perform fast-convolution filtering + + then see tools/ + */ + +#ifdef FIXED_POINT +# define kiss_fft_scalar short +#else +# ifndef kiss_fft_scalar +/* default is float */ +# define kiss_fft_scalar float +# endif +#endif + +typedef struct { + kiss_fft_scalar r; + kiss_fft_scalar i; +}kiss_fft_cpx; + +typedef struct kiss_fft_state* kiss_fft_cfg; + +/* + * kiss_fft_alloc + * + * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. + * + * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); + * + * The return value from fft_alloc is a cfg buffer used internally + * by the fft routine or NULL. + * + * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. + * The returned value should be free()d when done to avoid memory leaks. + * + * The state can be placed in a user supplied buffer 'mem': + * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, + * then the function places the cfg in mem and the size used in *lenmem + * and returns mem. + * + * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), + * then the function returns NULL and places the minimum cfg + * buffer size in *lenmem. + * */ + +kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); + +/* + * kiss_fft(cfg,in_out_buf) + * + * Perform an FFT on a complex input buffer. + * for a forward FFT, + * fin should be f[0] , f[1] , ... ,f[nfft-1] + * fout will be F[0] , F[1] , ... ,F[nfft-1] + * Note that each element is complex and can be accessed like + f[k].r and f[k].i + * */ +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); + +void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); + +/* If kiss_fft_alloc allocated a buffer, it is one contiguous + buffer and can be simply free()d when no longer needed*/ +#define kiss_fft_free free + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/external/libfaac/libfaac/kiss_fft/kiss_fftr.c b/external/libfaac/libfaac/kiss_fft/kiss_fftr.c new file mode 100644 index 0000000..6baeb21 --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/kiss_fftr.c @@ -0,0 +1,137 @@ +/* +Copyright (c) 2003-2004, Mark Borgerding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "kiss_fftr.h" +#include "_kiss_fft_guts.h" + +struct kiss_fftr_state{ + kiss_fft_cfg substate; + kiss_fft_cpx * tmpbuf; + kiss_fft_cpx * super_twiddles; +}; + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) +{ + int i; + kiss_fftr_cfg st = NULL; + size_t subsize, memneeded; + + if (nfft & 1) { + fprintf(stderr,"Real FFT optimization must be even.\n"); + return NULL; + } + nfft >>= 1; + + kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); + memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 2); + + if (lenmem == NULL) { + st = (kiss_fftr_cfg) malloc (memneeded); + } else { + if (*lenmem >= memneeded) + st = (kiss_fftr_cfg) mem; + *lenmem = memneeded; + } + if (!st) + return NULL; + + st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ + st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); + st->super_twiddles = st->tmpbuf + nfft; + kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); + + for (i = 0; i < nfft; ++i) { + double phase = + -3.14159265358979323846264338327 * ((double) i / nfft + .5); + if (inverse_fft) + phase *= -1; + kf_cexp (st->super_twiddles+i,phase); + } + return st; +} + +void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) +{ + /* input buffer timedata is stored row-wise */ + int k,N; + + if ( st->substate->inverse) { + fprintf(stderr,"kiss fft usage error: improper alloc\n"); + exit(1); + } + + N = st->substate->nfft; + + /*perform the parallel fft of two real signals packed in real,imag*/ + kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); + + freqdata[0].r = st->tmpbuf[0].r + st->tmpbuf[0].i; + freqdata[0].i = 0; + C_FIXDIV(freqdata[0],2); + + for (k=1;k <= N/2 ; ++k ) { + kiss_fft_cpx fpnk,fpk,f1k,f2k,tw; + + fpk = st->tmpbuf[k]; + fpnk.r = st->tmpbuf[N-k].r; + fpnk.i = -st->tmpbuf[N-k].i; + C_FIXDIV(fpk,2); + C_FIXDIV(fpnk,2); + + C_ADD( f1k, fpk , fpnk ); + C_SUB( f2k, fpk , fpnk ); + C_MUL( tw , f2k , st->super_twiddles[k]); + + C_ADD( freqdata[k] , f1k ,tw); + freqdata[k].r = (f1k.r + tw.r) / 2; + freqdata[k].i = (f1k.i + tw.i) / 2; + + freqdata[N-k].r = (f1k.r - tw.r)/2; + freqdata[N-k].i = - (f1k.i - tw.i)/2; + } + freqdata[N].r = st->tmpbuf[0].r - st->tmpbuf[0].i; + freqdata[N].i = 0; + C_FIXDIV(freqdata[N],2); +} + +void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) +{ + /* input buffer timedata is stored row-wise */ + int k, N; + + if (st->substate->inverse == 0) { + fprintf (stderr, "kiss fft usage error: improper alloc\n"); + exit (1); + } + + N = st->substate->nfft; + + st->tmpbuf[0].r = freqdata[0].r + freqdata[N].r; + st->tmpbuf[0].i = freqdata[0].r - freqdata[N].r; + + for (k = 1; k <= N / 2; ++k) { + kiss_fft_cpx fk, fnkc, fek, fok, tmpbuf; + fk = freqdata[k]; + fnkc.r = freqdata[N - k].r; + fnkc.i = -freqdata[N - k].i; + + C_ADD (fek, fk, fnkc); + C_SUB (tmpbuf, fk, fnkc); + C_MUL (fok, tmpbuf, st->super_twiddles[k]); + C_ADD (st->tmpbuf[k], fek, fok); + C_SUB (st->tmpbuf[N - k], fek, fok); + st->tmpbuf[N - k].i *= -1; + } + kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); +} diff --git a/external/libfaac/libfaac/kiss_fft/kiss_fftr.h b/external/libfaac/libfaac/kiss_fft/kiss_fftr.h new file mode 100644 index 0000000..38b77f5 --- /dev/null +++ b/external/libfaac/libfaac/kiss_fft/kiss_fftr.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2013-2016 EasyDarwin.ORG. All rights reserved. + Github: https://github.com/EasyDarwin + WEChat: EasyDarwin + Website: http://www.easydarwin.org +*/ +#ifndef KISS_FTR_H +#define KISS_FTR_H + +#include "kiss_fft.h" +#ifdef __cplusplus +extern "C" { +#endif + + +/* + + Real optimized version can save about 45% cpu time vs. complex fft of a real seq. + + + + */ + +typedef struct kiss_fftr_state *kiss_fftr_cfg; + + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); +/* + nfft must be even + + If you don't care to allocate space, use mem = lenmem = NULL +*/ + + +void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); +/* + input timedata has nfft scalar points + output freqdata has nfft/2+1 complex points +*/ + +void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); +/* + input freqdata has nfft/2+1 complex points + output timedata has nfft scalar points +*/ + +#define kiss_fftr_free free + +#ifdef __cplusplus +} +#endif +#endif diff --git a/external/libfaac/libfaac/ltp.c b/external/libfaac/libfaac/ltp.c new file mode 100644 index 0000000..f3fa86d --- /dev/null +++ b/external/libfaac/libfaac/ltp.c @@ -0,0 +1,420 @@ +/************************************************************************** + +This software module was originally developed by +Nokia in the course of development of the MPEG-2 AAC/MPEG-4 +Audio standard ISO/IEC13818-7, 14496-1, 2 and 3. +This software module is an implementation of a part +of one or more MPEG-2 AAC/MPEG-4 Audio tools as specified by the +MPEG-2 aac/MPEG-4 Audio standard. ISO/IEC gives users of the +MPEG-2aac/MPEG-4 Audio standards free license to this software module +or modifications thereof for use in hardware or software products +claiming conformance to the MPEG-2 aac/MPEG-4 Audio standards. Those +intending to use this software module in hardware or software products +are advised that this use may infringe existing patents. The original +developer of this software module, the subsequent +editors and their companies, and ISO/IEC have no liability for use of +this software module or modifications thereof in an +implementation. Copyright is not released for non MPEG-2 aac/MPEG-4 +Audio conforming products. The original developer retains full right to +use the code for the developer's own purpose, assign or donate the code to a +third party and to inhibit third party from using the code for non +MPEG-2 aac/MPEG-4 Audio conforming products. This copyright notice +must be included in all copies or derivative works. +Copyright (c)1997. + +***************************************************************************/ +/* + * $Id: ltp.c,v 1.9 2003/06/26 19:20:31 knik Exp $ + */ + +#include +#include + +#include "frame.h" +#include "coder.h" +#include "ltp.h" +#include "tns.h" +#include "filtbank.h" +#include "util.h" + + +/* short double_to_int(double sig_in); */ +#define double_to_int(sig_in) \ + ((sig_in) > 32767 ? 32767 : ( \ + (sig_in) < -32768 ? -32768 : (sig_in))) + +#define _MDCT_SCALE 512 + +/* Purpose: Codebook for LTP weight coefficients. */ +static double codebook[CODESIZE] = +{ + 0.570829, + 0.696616, + 0.813004, + 0.911304, + 0.984900, + 1.067894, + 1.194601, + 1.369533 +}; + + +static double snr_pred(double *mdct_in, double *mdct_pred, int *sfb_flag, int *sfb_offset, + int block_type, int side_info, int num_of_sfb) +{ + int i, j, flen; + double snr_limit; + double num_bit, snr[NSFB_LONG]; + double temp1, temp2; + double energy[BLOCK_LEN_LONG], snr_p[BLOCK_LEN_LONG]; + + if (block_type != ONLY_SHORT_WINDOW) + { + flen = BLOCK_LEN_LONG; + snr_limit = 1.e-30; + } else { + flen = BLOCK_LEN_SHORT; + snr_limit = 1.e-20; + } + + for (i = 0; i < flen; i++) + { + energy[i] = mdct_in[i] * mdct_in[i]; + snr_p[i] = (mdct_in[i] - mdct_pred[i]) * (mdct_in[i] - mdct_pred[i]); + } + + num_bit = 0.0; + + for (i = 0; i < num_of_sfb; i++) + { + temp1 = 0.0; + temp2 = 0.0; + for (j = sfb_offset[i]; j < sfb_offset[i + 1]; j++) + { + temp1 += energy[j]; + temp2 += snr_p[j]; + } + + if (temp2 < snr_limit) + temp2 = snr_limit; + + if (temp1 > 1.e-20) + snr[i] = -10. * log10 (temp2 / temp1); + else + snr[i] = 0.0; + + sfb_flag[i] = 1; + + if (block_type != ONLY_SHORT_WINDOW) + { + if (snr[i] <= 0.0) + { + sfb_flag[i] = 0; + for (j = sfb_offset[i]; j < sfb_offset[i + 1]; j++) + mdct_pred[j] = 0.0; + } else { + num_bit += snr[i] / 6. * (sfb_offset[i + 1] - sfb_offset[i]); + } + } + } + + if (num_bit < side_info) + { +// printf("LTP not used!, num_bit: %f ", num_bit); + num_bit = 0.0; + for (j = 0; j < flen; j++) + mdct_pred[j] = 0.0; + for (i = 0; i < num_of_sfb; i++) + sfb_flag[i] = 0; + } else { + num_bit -= side_info; +// printf("LTP used!, num_bit: %f ", num_bit); + } + + return (num_bit); +} + +static void prediction(double *buffer, double *predicted_samples, double *weight, int lag, + int flen) +{ + int i, offset; + int num_samples; + + offset = NOK_LT_BLEN - flen / 2 - lag; + + num_samples = flen; + if(NOK_LT_BLEN - offset < flen) + num_samples = NOK_LT_BLEN - offset; + + for(i = 0; i < num_samples; i++) + predicted_samples[i] = *weight * _MDCT_SCALE*buffer[offset++]; + for( ; i < flen; i++) + predicted_samples[i] = 0.0; + + +} + +static void w_quantize(double *freq, int *ltp_idx) +{ + int i; + double dist, low; + + low = 1.0e+10; + dist = 0.0; + for (i = 0; i < CODESIZE; i++) + { + dist = (*freq - codebook[i]) * (*freq - codebook[i]); + if (dist < low) + { + low = dist; + *ltp_idx = i; + } + } + + *freq = codebook[*ltp_idx]; +} + +static int pitch(double *sb_samples, double *x_buffer, int flen, int lag0, int lag1, + double *predicted_samples, double *gain, int *cb_idx) +{ + int i, j, delay; + double corr1, corr2, lag_corr; + double p_max, energy, lag_energy; + + /* + * Below is a figure illustrating how the lag and the + * samples in the buffer relate to each other. + * + * ------------------------------------------------------------------ + * | | | | | + * | slot 1 | 2 | 3 | 4 | + * | | | | | + * ------------------------------------------------------------------ + * + * lag = 0 refers to the end of slot 4 and lag = DELAY refers to the end + * of slot 2. The start of the predicted frame is then obtained by + * adding the length of the frame to the lag. Remember that slot 4 doesn't + * actually exist, since it is always filled with zeros. + * + * The above short explanation was for long blocks. For short blocks the + * zero lag doesn't refer to the end of slot 4 but to the start of slot + * 4 - the frame length of a short block. + * + * Some extra code is then needed to handle those lag values that refer + * to slot 4. + */ + + p_max = 0.0; + lag_corr = lag_energy = 0.0; + delay = lag0; + + + for (i = lag0; iblock_type, sfb_offset, + mdct_predicted); + + /* Get the prediction gain. */ + bit_gain = snr_pred(p_spectrum, mdct_predicted, sfb_prediction_used, + sfb_offset, side_info, last_band, coderInfo->nr_of_sfb); + + return (bit_gain); +} + +void LtpInit(faacEncHandle hEncoder) +{ + int i; + unsigned int channel; + + for (channel = 0; channel < hEncoder->numChannels; channel++) { + LtpInfo *ltpInfo = &(hEncoder->coderInfo[channel].ltpInfo); + + ltpInfo->buffer = AllocMemory(NOK_LT_BLEN * sizeof(double)); + ltpInfo->mdct_predicted = AllocMemory(2*BLOCK_LEN_LONG*sizeof(double)); + ltpInfo->time_buffer = AllocMemory(BLOCK_LEN_LONG*sizeof(double)); + ltpInfo->ltp_overlap_buffer = AllocMemory(BLOCK_LEN_LONG*sizeof(double)); + + for (i = 0; i < NOK_LT_BLEN; i++) + ltpInfo->buffer[i] = 0; + + ltpInfo->weight_idx = 0; + for(i = 0; i < MAX_SHORT_WINDOWS; i++) + ltpInfo->sbk_prediction_used[i] = ltpInfo->delay[i] = 0; + + for(i = 0; i < MAX_SCFAC_BANDS; i++) + ltpInfo->sfb_prediction_used[i] = 0; + + ltpInfo->side_info = LEN_LTP_DATA_PRESENT; + + for(i = 0; i < 2 * BLOCK_LEN_LONG; i++) + ltpInfo->mdct_predicted[i] = 0.0; + + } +} + +void LtpEnd(faacEncHandle hEncoder) +{ + unsigned int channel; + + for (channel = 0; channel < hEncoder->numChannels; channel++) { + LtpInfo *ltpInfo = &(hEncoder->coderInfo[channel].ltpInfo); + + if (ltpInfo->buffer) + FreeMemory(ltpInfo->buffer); + if (ltpInfo->mdct_predicted) + FreeMemory(ltpInfo->mdct_predicted); + if (ltpInfo->time_buffer) + FreeMemory(ltpInfo->time_buffer); + if (ltpInfo->ltp_overlap_buffer) + FreeMemory(ltpInfo->ltp_overlap_buffer); + } +} + +int LtpEncode(faacEncHandle hEncoder, + CoderInfo *coderInfo, + LtpInfo *ltpInfo, + TnsInfo *tnsInfo, + double *p_spectrum, + double *p_time_signal) +{ + int i, last_band; + double num_bit[MAX_SHORT_WINDOWS]; + double *predicted_samples; + + ltpInfo->global_pred_flag = 0; + ltpInfo->side_info = 0; + + predicted_samples = (double*)AllocMemory(2*BLOCK_LEN_LONG*sizeof(double)); + + switch(coderInfo->block_type) + { + case ONLY_LONG_WINDOW: + case LONG_SHORT_WINDOW: + case SHORT_LONG_WINDOW: + last_band = (coderInfo->nr_of_sfb < MAX_LT_PRED_LONG_SFB) ? coderInfo->nr_of_sfb : MAX_LT_PRED_LONG_SFB; + + ltpInfo->delay[0] = + pitch(p_time_signal, ltpInfo->buffer, 2 * BLOCK_LEN_LONG, + 0, 2 * BLOCK_LEN_LONG, predicted_samples, <pInfo->weight, + <pInfo->weight_idx); + + + num_bit[0] = + ltp_enc_tf(hEncoder, coderInfo, p_spectrum, predicted_samples, + ltpInfo->mdct_predicted, + coderInfo->sfb_offset, coderInfo->nr_of_sfb, + last_band, ltpInfo->side_info, ltpInfo->sfb_prediction_used, + tnsInfo); + + + ltpInfo->global_pred_flag = (num_bit[0] == 0.0) ? 0 : 1; + + if(ltpInfo->global_pred_flag) + for (i = 0; i < coderInfo->sfb_offset[last_band]; i++) + p_spectrum[i] -= ltpInfo->mdct_predicted[i]; + else + ltpInfo->side_info = 1; + + break; + + default: + break; + } + + if (predicted_samples) FreeMemory(predicted_samples); + + return (ltpInfo->global_pred_flag); +} + +void LtpReconstruct(CoderInfo *coderInfo, LtpInfo *ltpInfo, double *p_spectrum) +{ + int i, last_band; + + if(ltpInfo->global_pred_flag) + { + switch(coderInfo->block_type) + { + case ONLY_LONG_WINDOW: + case LONG_SHORT_WINDOW: + case SHORT_LONG_WINDOW: + last_band = (coderInfo->nr_of_sfb < MAX_LT_PRED_LONG_SFB) ? + coderInfo->nr_of_sfb : MAX_LT_PRED_LONG_SFB; + + for (i = 0; i < coderInfo->sfb_offset[last_band]; i++) + p_spectrum[i] += ltpInfo->mdct_predicted[i]; + break; + + default: + break; + } + } +} + +void LtpUpdate(LtpInfo *ltpInfo, double *time_signal, + double *overlap_signal, int block_size_long) +{ + int i; + + for(i = 0; i < NOK_LT_BLEN - 2 * block_size_long; i++) + ltpInfo->buffer[i] = ltpInfo->buffer[i + block_size_long]; + + for(i = 0; i < block_size_long; i++) + { + ltpInfo->buffer[NOK_LT_BLEN - 2 * block_size_long + i] = time_signal[i]; + ltpInfo->buffer[NOK_LT_BLEN - block_size_long + i] = overlap_signal[i]; + } +} diff --git a/external/libfaac/libfaac/ltp.h b/external/libfaac/libfaac/ltp.h new file mode 100644 index 0000000..31afd00 --- /dev/null +++ b/external/libfaac/libfaac/ltp.h @@ -0,0 +1,48 @@ +/* + Copyright (c) 2013-2016 EasyDarwin.ORG. All rights reserved. + Github: https://github.com/EasyDarwin + WEChat: EasyDarwin + Website: http://www.easydarwin.org +*/ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: ltp.h,v 1.3 2001/06/08 18:01:09 menno Exp $ + */ + +#ifndef LTP_H +#define LTP_H + +#include "coder.h" + + + +void LtpInit(faacEncHandle hEncoder); +void LtpEnd(faacEncHandle hEncoder); +int LtpEncode(faacEncHandle hEncoder, + CoderInfo *coderInfo, + LtpInfo *ltpInfo, + TnsInfo *tnsInfo, + double *p_spectrum, + double *p_time_signal); +void LtpReconstruct(CoderInfo *coderInfo, LtpInfo *ltpInfo, double *p_spectrum); +void LtpUpdate(LtpInfo *ltpInfo, double *time_signal, + double *overlap_signal, int block_size_long); + +#endif /* not defined LTP_H */ + diff --git a/external/libfaac/libfaac/midside.c b/external/libfaac/libfaac/midside.c new file mode 100644 index 0000000..fa6f611 --- /dev/null +++ b/external/libfaac/libfaac/midside.c @@ -0,0 +1,169 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2003 Krzysztof Nikiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: midside.c,v 1.1 2003/06/26 19:39:54 knik Exp $ + */ + +#include +#include "channels.h" +#include "util.h" + + +void MSEncode(CoderInfo *coderInfo, + ChannelInfo *channelInfo, + double *spectrum[MAX_CHANNELS], + int maxchan, + int allowms) +{ + int chn; + + for (chn = 0; chn < maxchan; chn++) + { + if (channelInfo[chn].present) + { + if ((channelInfo[chn].cpe) && (channelInfo[chn].ch_is_left)) + { + int rch = channelInfo[chn].paired_ch; + + channelInfo[chn].msInfo.is_present = 0; + channelInfo[rch].msInfo.is_present = 0; + + /* Perform MS if block_types are the same */ + if ((coderInfo[chn].block_type == coderInfo[rch].block_type) + && allowms) + { + int nsfb = coderInfo[chn].nr_of_sfb; + MSInfo *msInfoL = &(channelInfo[chn].msInfo); + MSInfo *msInfoR = &(channelInfo[rch].msInfo); + int sfb; + + channelInfo[chn].common_window = 1; /* Use common window */ + channelInfo[chn].msInfo.is_present = 1; + channelInfo[rch].msInfo.is_present = 1; + + // make the same reference energy in both channels + coderInfo[chn].avgenrg = coderInfo[rch].avgenrg = + 0.5 * (coderInfo[chn].avgenrg + coderInfo[rch].avgenrg); + + for (sfb = 0; sfb < nsfb; sfb++) + { + int ms = 0; + int l, start, end; + double sum, diff; + double enrgs, enrgd, enrgl, enrgr; + double maxs, maxd, maxl, maxr; + + start = coderInfo[chn].sfb_offset[sfb]; + end = coderInfo[chn].sfb_offset[sfb + 1]; + + enrgs = enrgd = enrgl = enrgr = 0.0; + maxs = maxd = maxl = maxr = 0.0; + for (l = start; l < end; l++) + { + double lx = spectrum[chn][l]; + double rx = spectrum[rch][l]; + + sum = 0.5 * (lx + rx); + diff = 0.5 * (lx - rx); + + enrgs += sum * sum; + maxs = max(maxs, fabs(sum)); + + enrgd += diff * diff; + maxd = max(maxd, fabs(diff)); + + enrgl += lx * lx; + enrgr += rx * rx; + + maxl = max(maxl, fabs(lx)); + maxr = max(maxr, fabs(rx)); + } + +#if 1 + if ((min(enrgs, enrgd) < min(enrgl, enrgr)) + && (min(maxs, maxd) < min(maxl, maxr))) + ms = 1; +#else + if (min(enrgs, enrgd) < min(enrgl, enrgr)) + ms = 1; +#endif + + //printf("%d:%d\n", sfb, ms); + + msInfoR->ms_used[sfb] = msInfoL->ms_used[sfb] = ms; + + if (ms) + for (l = start; l < end; l++) + { + sum = spectrum[chn][l] + spectrum[rch][l]; + diff = spectrum[chn][l] - spectrum[rch][l]; + spectrum[chn][l] = 0.5 * sum; + spectrum[rch][l] = 0.5 * diff; + } + } + } + } + } + } +} + +void MSReconstruct(CoderInfo *coderInfo, + ChannelInfo *channelInfo, + int maxchan) +{ + int chn; + + for (chn = 0; chn < maxchan; chn++) + { + if (channelInfo[chn].present) + { + if (channelInfo[chn].cpe && channelInfo[chn].ch_is_left) + { + int rch = channelInfo[chn].paired_ch; + + MSInfo *msInfoL = &(channelInfo[chn].msInfo); + + if (msInfoL->is_present) { + int nsfb = coderInfo[chn].nr_of_sfb; + int sfb; + + for (sfb = 0; sfb < nsfb; sfb++) + { + int l, start, end; + + start = coderInfo[chn].sfb_offset[sfb]; + end = coderInfo[chn].sfb_offset[sfb + 1]; + + if (msInfoL->ms_used[sfb]) + { + for (l = start; l < end; l++) + { + double sum, diff; + + sum = coderInfo[chn].requantFreq[l]; + diff = coderInfo[rch].requantFreq[l]; + coderInfo[chn].requantFreq[l] = sum + diff; + coderInfo[rch].requantFreq[l] = sum - diff; + } + } + } + } + } + } + } +} diff --git a/external/libfaac/libfaac/midside.h b/external/libfaac/libfaac/midside.h new file mode 100644 index 0000000..a47660f --- /dev/null +++ b/external/libfaac/libfaac/midside.h @@ -0,0 +1,40 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2003 Krzysztof Nikiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: midside.h,v 1.1 2003/06/26 19:40:23 knik Exp $ + */ + +#ifndef _MIDSIDE_H +#define _MIDSIDE_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "coder.h" + + +void MSEncode(CoderInfo *coderInfo, ChannelInfo *channelInfo, double *spectrum[MAX_CHANNELS], + unsigned int numberOfChannels, unsigned int msenable); +void MSReconstruct(CoderInfo *coderInfo, ChannelInfo *channelInfo, int numberOfChannels); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _MIDSIDE_H */ diff --git a/external/libfaac/libfaac/psych.h b/external/libfaac/libfaac/psych.h new file mode 100644 index 0000000..00769c0 --- /dev/null +++ b/external/libfaac/libfaac/psych.h @@ -0,0 +1,85 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: psych.h,v 1.14 2005/04/24 19:16:14 rjamorim Exp $ + */ + +#ifndef PSYCH_H +#define PSYCH_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include "coder.h" +#include "channels.h" +#include "fft.h" + +typedef struct { + int size; + int sizeS; + + /* Previous input samples */ + double *prevSamples; + double *prevSamplesS; + + int block_type; + + void *data; +} PsyInfo; + +typedef struct { + double sampleRate; + + /* Hann window */ + double *hannWindow; + double *hannWindowS; + + void *data; +} GlobalPsyInfo; + +typedef struct +{ +void (*PsyInit) (GlobalPsyInfo *gpsyInfo, PsyInfo *psyInfo, + unsigned int numChannels, unsigned int sampleRate, + int *cb_width_long, int num_cb_long, + int *cb_width_short, int num_cb_short); +void (*PsyEnd) (GlobalPsyInfo *gpsyInfo, PsyInfo *psyInfo, + unsigned int numChannels); +void (*PsyCalculate) (ChannelInfo *channelInfo, GlobalPsyInfo *gpsyInfo, + PsyInfo *psyInfo, int *cb_width_long, int num_cb_long, + int *cb_width_short, int num_cb_short, + unsigned int numChannels); +void (*PsyBufferUpdate) ( FFT_Tables *fft_tables, GlobalPsyInfo * gpsyInfo, PsyInfo * psyInfo, + double *newSamples, unsigned int bandwidth, + int *cb_width_short, int num_cb_short); +void (*BlockSwitch) (CoderInfo *coderInfo, PsyInfo *psyInfo, + unsigned int numChannels); +} psymodel_t; + +extern psymodel_t psymodel2; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PSYCH_H */ \ No newline at end of file diff --git a/external/libfaac/libfaac/psychkni.c b/external/libfaac/libfaac/psychkni.c new file mode 100644 index 0000000..1bf52ba --- /dev/null +++ b/external/libfaac/libfaac/psychkni.c @@ -0,0 +1,450 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2002 Krzysztof Nikiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: psychkni.c,v 1.17 2005/04/24 19:16:14 rjamorim Exp $ + */ +#include +#include +#include + +#include "psych.h" +#include "coder.h" +#include "fft.h" +#include "util.h" +#include "frame.h" + +typedef float psyfloat; + +typedef struct +{ + /* bandwidth */ + int bandS; + int lastband; + + /* SFB energy */ + psyfloat *fftEnrgS[8]; + psyfloat *fftEnrgNextS[8]; + psyfloat *fftEnrgNext2S[8]; + psyfloat *fftEnrgPrevS[8]; +} +psydata_t; + + +static void Hann(GlobalPsyInfo * gpsyInfo, double *inSamples, int size) +{ + int i; + + /* Applying Hann window */ + if (size == BLOCK_LEN_LONG * 2) + { + for (i = 0; i < size; i++) + inSamples[i] *= gpsyInfo->hannWindow[i]; + } + else + { + for (i = 0; i < size; i++) + inSamples[i] *= gpsyInfo->hannWindowS[i]; + } +} + +static void PsyCheckShort(PsyInfo * psyInfo) +{ + double totvol = 0.0; + double totchg, totchg2; + psydata_t *psydata = psyInfo->data; + int lastband = psydata->lastband; + int firstband = 1; + int sfb; + + /* long/short block switch */ + totchg = totchg2 = 0.0; + for (sfb = 0; sfb < lastband; sfb++) + { + int win; + double volb[16]; + double vavg[13]; + double maxdif = 0.0; + double totmaxdif = 0.0; + double e, v; + + // previous frame + for (win = 0; win < 4; win++) + { + e = psydata->fftEnrgPrevS[win + 4][sfb]; + + volb[win] = sqrt(e); + totvol += e; + } + + // current frame + for (win = 0; win < 8; win++) + { + e = psydata->fftEnrgS[win][sfb]; + + volb[win + 4] = sqrt(e); + totvol += e; + } + // next frame + for (win = 0; win < 4; win++) + { + e = psydata->fftEnrgNextS[win][sfb]; + + volb[win + 12] = sqrt(e); + totvol += e; + } + + // ignore lowest SFBs + if (sfb < firstband) + continue; + + v = 0.0; + for (win = 0; win < 4; win++) + { + v += volb[win]; + } + vavg[0] = 0.25 * v; + + for (win = 1; win < 13; win++) + { + v -= volb[win - 1]; + v += volb[win + 3]; + vavg[win] = 0.25 * v; + } + + for (win = 0; win < 8; win++) + { + int i; + double mina, maxv; + double voldif; + double totvoldif; + + mina = vavg[win]; + for (i = 1; i < 5; i++) + mina = min(mina, vavg[win + i]); + + maxv = volb[win + 2]; + for (i = 3; i < 6; i++) + maxv = max(maxv, volb[win + i]); + + if (!maxv || !mina) + continue; + + voldif = (maxv - mina) / mina; + totvoldif = (maxv - mina) * (maxv - mina); + + if (voldif > maxdif) + maxdif = voldif; + + if (totvoldif > totmaxdif) + totmaxdif = totvoldif; + } + totchg += maxdif; + totchg2 += totmaxdif; + } + + totvol = sqrt(totvol); + + totchg2 = sqrt(totchg2); + + totchg = totchg / lastband; + if (totvol) + totchg2 /= totvol; + else + totchg2 = 0.0; + + psyInfo->block_type = ((totchg > 1.0) && (totchg2 > 0.04)) + ? ONLY_SHORT_WINDOW : ONLY_LONG_WINDOW; + +#if 0 + { + static int total = 0, shorts = 0; + char *flash = " "; + + total++; + if (psyInfo->block_type == ONLY_SHORT_WINDOW) + { + flash = "****"; + shorts++; + } + + printf("totchg: %s %g %g\t%g\n", flash, totchg, totchg2, + (double)shorts/total); + } +#endif +} + +static void PsyInit(GlobalPsyInfo * gpsyInfo, PsyInfo * psyInfo, unsigned int numChannels, + unsigned int sampleRate, int *cb_width_long, int num_cb_long, + int *cb_width_short, int num_cb_short) +{ + unsigned int channel; + int i, j, size; + + gpsyInfo->hannWindow = + (double *) AllocMemory(2 * BLOCK_LEN_LONG * sizeof(double)); + gpsyInfo->hannWindowS = + (double *) AllocMemory(2 * BLOCK_LEN_SHORT * sizeof(double)); + + for (i = 0; i < BLOCK_LEN_LONG * 2; i++) + gpsyInfo->hannWindow[i] = 0.5 * (1 - cos(2.0 * M_PI * (i + 0.5) / + (BLOCK_LEN_LONG * 2))); + for (i = 0; i < BLOCK_LEN_SHORT * 2; i++) + gpsyInfo->hannWindowS[i] = 0.5 * (1 - cos(2.0 * M_PI * (i + 0.5) / + (BLOCK_LEN_SHORT * 2))); + gpsyInfo->sampleRate = (double) sampleRate; + + for (channel = 0; channel < numChannels; channel++) + { + psydata_t *psydata = AllocMemory(sizeof(psydata_t)); + psyInfo[channel].data = psydata; + } + + size = BLOCK_LEN_LONG; + for (channel = 0; channel < numChannels; channel++) + { + psyInfo[channel].size = size; + + psyInfo[channel].prevSamples = + (double *) AllocMemory(size * sizeof(double)); + memset(psyInfo[channel].prevSamples, 0, size * sizeof(double)); + } + + size = BLOCK_LEN_SHORT; + for (channel = 0; channel < numChannels; channel++) + { + psydata_t *psydata = psyInfo[channel].data; + + psyInfo[channel].sizeS = size; + + psyInfo[channel].prevSamplesS = + (double *) AllocMemory(size * sizeof(double)); + memset(psyInfo[channel].prevSamplesS, 0, size * sizeof(double)); + + for (j = 0; j < 8; j++) + { + psydata->fftEnrgPrevS[j] = + (psyfloat *) AllocMemory(NSFB_SHORT * sizeof(psyfloat)); + memset(psydata->fftEnrgPrevS[j], 0, NSFB_SHORT * sizeof(psyfloat)); + psydata->fftEnrgS[j] = + (psyfloat *) AllocMemory(NSFB_SHORT * sizeof(psyfloat)); + memset(psydata->fftEnrgS[j], 0, NSFB_SHORT * sizeof(psyfloat)); + psydata->fftEnrgNextS[j] = + (psyfloat *) AllocMemory(NSFB_SHORT * sizeof(psyfloat)); + memset(psydata->fftEnrgNextS[j], 0, NSFB_SHORT * sizeof(psyfloat)); + psydata->fftEnrgNext2S[j] = + (psyfloat *) AllocMemory(NSFB_SHORT * sizeof(psyfloat)); + memset(psydata->fftEnrgNext2S[j], 0, NSFB_SHORT * sizeof(psyfloat)); + } + } +} + +static void PsyEnd(GlobalPsyInfo * gpsyInfo, PsyInfo * psyInfo, unsigned int numChannels) +{ + unsigned int channel; + int j; + + if (gpsyInfo->hannWindow) + FreeMemory(gpsyInfo->hannWindow); + if (gpsyInfo->hannWindowS) + FreeMemory(gpsyInfo->hannWindowS); + + for (channel = 0; channel < numChannels; channel++) + { + if (psyInfo[channel].prevSamples) + FreeMemory(psyInfo[channel].prevSamples); + } + + for (channel = 0; channel < numChannels; channel++) + { + psydata_t *psydata = psyInfo[channel].data; + + if (psyInfo[channel].prevSamplesS) + FreeMemory(psyInfo[channel].prevSamplesS); + for (j = 0; j < 8; j++) + { + if (psydata->fftEnrgPrevS[j]) + FreeMemory(psydata->fftEnrgPrevS[j]); + if (psydata->fftEnrgS[j]) + FreeMemory(psydata->fftEnrgS[j]); + if (psydata->fftEnrgNextS[j]) + FreeMemory(psydata->fftEnrgNextS[j]); + if (psydata->fftEnrgNext2S[j]) + FreeMemory(psydata->fftEnrgNext2S[j]); + } + } + + for (channel = 0; channel < numChannels; channel++) + { + if (psyInfo[channel].data) + FreeMemory(psyInfo[channel].data); + } +} + +/* Do psychoacoustical analysis */ +static void PsyCalculate(ChannelInfo * channelInfo, GlobalPsyInfo * gpsyInfo, + PsyInfo * psyInfo, int *cb_width_long, int + num_cb_long, int *cb_width_short, + int num_cb_short, unsigned int numChannels) +{ + unsigned int channel; + + for (channel = 0; channel < numChannels; channel++) + { + if (channelInfo[channel].present) + { + + if (channelInfo[channel].cpe && + channelInfo[channel].ch_is_left) + { /* CPE */ + + int leftChan = channel; + int rightChan = channelInfo[channel].paired_ch; + + PsyCheckShort(&psyInfo[leftChan]); + PsyCheckShort(&psyInfo[rightChan]); + } + else if (!channelInfo[channel].cpe && + channelInfo[channel].lfe) + { /* LFE */ + // Only set block type and it should be OK + psyInfo[channel].block_type = ONLY_LONG_WINDOW; + } + else if (!channelInfo[channel].cpe) + { /* SCE */ + PsyCheckShort(&psyInfo[channel]); + } + } + } +} + +static void PsyBufferUpdate( FFT_Tables *fft_tables, GlobalPsyInfo * gpsyInfo, PsyInfo * psyInfo, + double *newSamples, unsigned int bandwidth, + int *cb_width_short, int num_cb_short) +{ + int win; + double transBuff[2 * BLOCK_LEN_LONG]; + double transBuffS[2 * BLOCK_LEN_SHORT]; + psydata_t *psydata = psyInfo->data; + psyfloat *tmp; + int sfb; + + psydata->bandS = psyInfo->sizeS * bandwidth * 2 / gpsyInfo->sampleRate; + + memcpy(transBuff, psyInfo->prevSamples, psyInfo->size * sizeof(double)); + memcpy(transBuff + psyInfo->size, newSamples, psyInfo->size * sizeof(double)); + + for (win = 0; win < 8; win++) + { + int first = 0; + int last = 0; + + memcpy(transBuffS, transBuff + (win * BLOCK_LEN_SHORT) + (BLOCK_LEN_LONG - BLOCK_LEN_SHORT) / 2, + 2 * psyInfo->sizeS * sizeof(double)); + + Hann(gpsyInfo, transBuffS, 2 * psyInfo->sizeS); + rfft( fft_tables, transBuffS, 8); + + // shift bufs + tmp = psydata->fftEnrgPrevS[win]; + psydata->fftEnrgPrevS[win] = psydata->fftEnrgS[win]; + psydata->fftEnrgS[win] = psydata->fftEnrgNextS[win]; + psydata->fftEnrgNextS[win] = psydata->fftEnrgNext2S[win]; + psydata->fftEnrgNext2S[win] = tmp; + + for (sfb = 0; sfb < num_cb_short; sfb++) + { + double e; + int l; + + first = last; + last = first + cb_width_short[sfb]; + + if (first < 1) + first = 1; + + //if (last > psydata->bandS) // band out of range + if (first >= psydata->bandS) // band out of range + break; + + e = 0.0; + for (l = first; l < last; l++) + { + double a = transBuffS[l]; + double b = transBuffS[l + psyInfo->sizeS]; + + e += a * a + b * b; + } + + psydata->fftEnrgNext2S[win][sfb] = e; + } + psydata->lastband = sfb; + for (; sfb < num_cb_short; sfb++) + { + psydata->fftEnrgNext2S[win][sfb] = 0; + } + } + + memcpy(psyInfo->prevSamples, newSamples, psyInfo->size * sizeof(double)); +} + +static void BlockSwitch(CoderInfo * coderInfo, PsyInfo * psyInfo, unsigned int numChannels) +{ + unsigned int channel; + int desire = ONLY_LONG_WINDOW; + + /* Use the same block type for all channels + If there is 1 channel that wants a short block, + use a short block on all channels. + */ + for (channel = 0; channel < numChannels; channel++) + { + if (psyInfo[channel].block_type == ONLY_SHORT_WINDOW) + desire = ONLY_SHORT_WINDOW; + } + + for (channel = 0; channel < numChannels; channel++) + { + int lasttype = coderInfo[channel].block_type; + + if (desire == ONLY_SHORT_WINDOW + || coderInfo[channel].desired_block_type == ONLY_SHORT_WINDOW) + { + if (lasttype == ONLY_LONG_WINDOW || lasttype == SHORT_LONG_WINDOW) + coderInfo[channel].block_type = LONG_SHORT_WINDOW; + else + coderInfo[channel].block_type = ONLY_SHORT_WINDOW; + } + else + { + if (lasttype == ONLY_SHORT_WINDOW || lasttype == LONG_SHORT_WINDOW) + coderInfo[channel].block_type = SHORT_LONG_WINDOW; + else + coderInfo[channel].block_type = ONLY_LONG_WINDOW; + } + coderInfo[channel].desired_block_type = desire; + } +} + +psymodel_t psymodel2 = +{ + PsyInit, + PsyEnd, + PsyCalculate, + PsyBufferUpdate, + BlockSwitch +}; diff --git a/external/libfaac/libfaac/tns.c b/external/libfaac/libfaac/tns.c new file mode 100644 index 0000000..858e2fa --- /dev/null +++ b/external/libfaac/libfaac/tns.c @@ -0,0 +1,609 @@ +/********************************************************************** + +This software module was originally developed by Texas Instruments +and edited by in the course of +development of the MPEG-2 NBC/MPEG-4 Audio standard +ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an +implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools +as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives +users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this +software module or modifications thereof for use in hardware or +software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio +standards. Those intending to use this software module in hardware or +software products are advised that this use may infringe existing +patents. The original developer of this software module and his/her +company, the subsequent editors and their companies, and ISO/IEC have +no liability for use of this software module or modifications thereof +in an implementation. Copyright is not released for non MPEG-2 +NBC/MPEG-4 Audio conforming products. The original developer retains +full right to use the code for his/her own purpose, assign or donate +the code to a third party and to inhibit third party from using the +code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This +copyright notice must be included in all copies or derivative works. + +Copyright (c) 1997. +**********************************************************************/ +/* + * $Id: tns.c,v 1.10 2003/11/24 18:08:28 knik Exp $ + */ + +#include +#include "frame.h" +#include "coder.h" +#include "bitstream.h" +#include "tns.h" +#include "util.h" + +/***********************************************/ +/* TNS Profile/Frequency Dependent Parameters */ +/***********************************************/ +static unsigned long tnsSupportedSamplingRates[13] = +{ 96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,0 }; + +/* Limit bands to > 2.0 kHz */ +static unsigned short tnsMinBandNumberLong[12] = +{ 11, 12, 15, 16, 17, 20, 25, 26, 24, 28, 30, 31 }; +static unsigned short tnsMinBandNumberShort[12] = +{ 2, 2, 2, 3, 3, 4, 6, 6, 8, 10, 10, 12 }; + +/**************************************/ +/* Main/Low Profile TNS Parameters */ +/**************************************/ +static unsigned short tnsMaxBandsLongMainLow[12] = +{ 31, 31, 34, 40, 42, 51, 46, 46, 42, 42, 42, 39 }; + +static unsigned short tnsMaxBandsShortMainLow[12] = +{ 9, 9, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14 }; + +static unsigned short tnsMaxOrderLongMain = 20; +static unsigned short tnsMaxOrderLongLow = 12; +static unsigned short tnsMaxOrderShortMainLow = 7; + + +/*************************/ +/* Function prototypes */ +/*************************/ +static void Autocorrelation(int maxOrder, /* Maximum autocorr order */ + int dataSize, /* Size of the data array */ + double* data, /* Data array */ + double* rArray); /* Autocorrelation array */ + +static double LevinsonDurbin(int maxOrder, /* Maximum filter order */ + int dataSize, /* Size of the data array */ + double* data, /* Data array */ + double* kArray); /* Reflection coeff array */ + +static void StepUp(int fOrder, double* kArray, double* aArray); + +static void QuantizeReflectionCoeffs(int fOrder,int coeffRes,double* rArray,int* indexArray); +static int TruncateCoeffs(int fOrder,double threshold,double* kArray); +static void TnsFilter(int length,double* spec,TnsFilterData* filter); +static void TnsInvFilter(int length,double* spec,TnsFilterData* filter); + + +/*****************************************************/ +/* InitTns: */ +/*****************************************************/ +void TnsInit(faacEncHandle hEncoder) +{ + unsigned int channel; + int fsIndex = hEncoder->sampleRateIdx; + int profile = hEncoder->config.aacObjectType; + + for (channel = 0; channel < hEncoder->numChannels; channel++) { + TnsInfo *tnsInfo = &hEncoder->coderInfo[channel].tnsInfo; + + switch( profile ) { + case MAIN: + case LTP: + tnsInfo->tnsMaxBandsLong = tnsMaxBandsLongMainLow[fsIndex]; + tnsInfo->tnsMaxBandsShort = tnsMaxBandsShortMainLow[fsIndex]; + if (hEncoder->config.mpegVersion == 1) { /* MPEG2 */ + tnsInfo->tnsMaxOrderLong = tnsMaxOrderLongMain; + } else { /* MPEG4 */ + if (fsIndex <= 5) /* fs > 32000Hz */ + tnsInfo->tnsMaxOrderLong = 12; + else + tnsInfo->tnsMaxOrderLong = 20; + } + tnsInfo->tnsMaxOrderShort = tnsMaxOrderShortMainLow; + break; + case LOW : + tnsInfo->tnsMaxBandsLong = tnsMaxBandsLongMainLow[fsIndex]; + tnsInfo->tnsMaxBandsShort = tnsMaxBandsShortMainLow[fsIndex]; + if (hEncoder->config.mpegVersion == 1) { /* MPEG2 */ + tnsInfo->tnsMaxOrderLong = tnsMaxOrderLongLow; + } else { /* MPEG4 */ + if (fsIndex <= 5) /* fs > 32000Hz */ + tnsInfo->tnsMaxOrderLong = 12; + else + tnsInfo->tnsMaxOrderLong = 20; + } + tnsInfo->tnsMaxOrderShort = tnsMaxOrderShortMainLow; + break; + } + tnsInfo->tnsMinBandNumberLong = tnsMinBandNumberLong[fsIndex]; + tnsInfo->tnsMinBandNumberShort = tnsMinBandNumberShort[fsIndex]; + } +} + + +/*****************************************************/ +/* TnsEncode: */ +/*****************************************************/ +void TnsEncode(TnsInfo* tnsInfo, /* TNS info */ + int numberOfBands, /* Number of bands per window */ + int maxSfb, /* max_sfb */ + enum WINDOW_TYPE blockType, /* block type */ + int* sfbOffsetTable, /* Scalefactor band offset table */ + double* spec) /* Spectral data array */ +{ + int numberOfWindows,windowSize; + int startBand,stopBand,order; /* Bands over which to apply TNS */ + int lengthInBands; /* Length to filter, in bands */ + int w; + int startIndex,length; + double gain; + + switch( blockType ) { + case ONLY_SHORT_WINDOW : + + /* TNS not used for short blocks currently */ + tnsInfo->tnsDataPresent = 0; + return; + + numberOfWindows = MAX_SHORT_WINDOWS; + windowSize = BLOCK_LEN_SHORT; + startBand = tnsInfo->tnsMinBandNumberShort; + stopBand = numberOfBands; + lengthInBands = stopBand-startBand; + order = tnsInfo->tnsMaxOrderShort; + startBand = min(startBand,tnsInfo->tnsMaxBandsShort); + stopBand = min(stopBand,tnsInfo->tnsMaxBandsShort); + break; + + default: + numberOfWindows = 1; + windowSize = BLOCK_LEN_SHORT; + startBand = tnsInfo->tnsMinBandNumberLong; + stopBand = numberOfBands; + lengthInBands = stopBand - startBand; + order = tnsInfo->tnsMaxOrderLong; + startBand = min(startBand,tnsInfo->tnsMaxBandsLong); + stopBand = min(stopBand,tnsInfo->tnsMaxBandsLong); + break; + } + + /* Make sure that start and stop bands < maxSfb */ + /* Make sure that start and stop bands >= 0 */ + startBand = min(startBand,maxSfb); + stopBand = min(stopBand,maxSfb); + startBand = max(startBand,0); + stopBand = max(stopBand,0); + + tnsInfo->tnsDataPresent = 0; /* default TNS not used */ + + /* Perform analysis and filtering for each window */ + for (w=0;wwindowData[w]; + TnsFilterData* tnsFilter = windowData->tnsFilter; + double* k = tnsFilter->kCoeffs; /* reflection coeffs */ + double* a = tnsFilter->aCoeffs; /* prediction coeffs */ + + windowData->numFilters=0; + windowData->coefResolution = DEF_TNS_COEFF_RES; + startIndex = w * windowSize + sfbOffsetTable[startBand]; + length = sfbOffsetTable[stopBand] - sfbOffsetTable[startBand]; + gain = LevinsonDurbin(order,length,&spec[startIndex],k); + + if (gain>DEF_TNS_GAIN_THRESH) { /* Use TNS */ + int truncatedOrder; + windowData->numFilters++; + tnsInfo->tnsDataPresent=1; + tnsFilter->direction = 0; + tnsFilter->coefCompress = 0; + tnsFilter->length = lengthInBands; + QuantizeReflectionCoeffs(order,DEF_TNS_COEFF_RES,k,tnsFilter->index); + truncatedOrder = TruncateCoeffs(order,DEF_TNS_COEFF_THRESH,k); + tnsFilter->order = truncatedOrder; + StepUp(truncatedOrder,k,a); /* Compute predictor coefficients */ + TnsInvFilter(length,&spec[startIndex],tnsFilter); /* Filter */ + } + } +} + + +/*****************************************************/ +/* TnsEncodeFilterOnly: */ +/* This is a stripped-down version of TnsEncode() */ +/* which performs TNS analysis filtering only */ +/*****************************************************/ +void TnsEncodeFilterOnly(TnsInfo* tnsInfo, /* TNS info */ + int numberOfBands, /* Number of bands per window */ + int maxSfb, /* max_sfb */ + enum WINDOW_TYPE blockType, /* block type */ + int* sfbOffsetTable, /* Scalefactor band offset table */ + double* spec) /* Spectral data array */ +{ + int numberOfWindows,windowSize; + int startBand,stopBand; /* Bands over which to apply TNS */ + int w; + int startIndex,length; + + switch( blockType ) { + case ONLY_SHORT_WINDOW : + numberOfWindows = MAX_SHORT_WINDOWS; + windowSize = BLOCK_LEN_SHORT; + startBand = tnsInfo->tnsMinBandNumberShort; + stopBand = numberOfBands; + startBand = min(startBand,tnsInfo->tnsMaxBandsShort); + stopBand = min(stopBand,tnsInfo->tnsMaxBandsShort); + break; + + default: + numberOfWindows = 1; + windowSize = BLOCK_LEN_LONG; + startBand = tnsInfo->tnsMinBandNumberLong; + stopBand = numberOfBands; + startBand = min(startBand,tnsInfo->tnsMaxBandsLong); + stopBand = min(stopBand,tnsInfo->tnsMaxBandsLong); + break; + } + + /* Make sure that start and stop bands < maxSfb */ + /* Make sure that start and stop bands >= 0 */ + startBand = min(startBand,maxSfb); + stopBand = min(stopBand,maxSfb); + startBand = max(startBand,0); + stopBand = max(stopBand,0); + + + /* Perform filtering for each window */ + for(w=0;wwindowData[w]; + TnsFilterData* tnsFilter = windowData->tnsFilter; + + startIndex = w * windowSize + sfbOffsetTable[startBand]; + length = sfbOffsetTable[stopBand] - sfbOffsetTable[startBand]; + + if (tnsInfo->tnsDataPresent && windowData->numFilters) { /* Use TNS */ + TnsInvFilter(length,&spec[startIndex],tnsFilter); + } + } +} + + +/*****************************************************/ +/* TnsDecodeFilterOnly: */ +/* This is a stripped-down version of TnsEncode() */ +/* which performs TNS synthesis filtering only */ +/*****************************************************/ +void TnsDecodeFilterOnly(TnsInfo* tnsInfo, /* TNS info */ + int numberOfBands, /* Number of bands per window */ + int maxSfb, /* max_sfb */ + enum WINDOW_TYPE blockType, /* block type */ + int* sfbOffsetTable, /* Scalefactor band offset table */ + double* spec) /* Spectral data array */ +{ + int numberOfWindows,windowSize; + int startBand,stopBand; /* Bands over which to apply TNS */ + int w; + int startIndex,length; + + switch( blockType ) { + case ONLY_SHORT_WINDOW : + numberOfWindows = MAX_SHORT_WINDOWS; + windowSize = BLOCK_LEN_SHORT; + startBand = tnsInfo->tnsMinBandNumberShort; + stopBand = numberOfBands; + startBand = min(startBand,tnsInfo->tnsMaxBandsShort); + stopBand = min(stopBand,tnsInfo->tnsMaxBandsShort); + break; + + default: + numberOfWindows = 1; + windowSize = BLOCK_LEN_LONG; + startBand = tnsInfo->tnsMinBandNumberLong; + stopBand = numberOfBands; + startBand = min(startBand,tnsInfo->tnsMaxBandsLong); + stopBand = min(stopBand,tnsInfo->tnsMaxBandsLong); + break; + } + + /* Make sure that start and stop bands < maxSfb */ + /* Make sure that start and stop bands >= 0 */ + startBand = min(startBand,maxSfb); + stopBand = min(stopBand,maxSfb); + startBand = max(startBand,0); + stopBand = max(stopBand,0); + + + /* Perform filtering for each window */ + for(w=0;wwindowData[w]; + TnsFilterData* tnsFilter = windowData->tnsFilter; + + startIndex = w * windowSize + sfbOffsetTable[startBand]; + length = sfbOffsetTable[stopBand] - sfbOffsetTable[startBand]; + + if (tnsInfo->tnsDataPresent && windowData->numFilters) { /* Use TNS */ + TnsFilter(length,&spec[startIndex],tnsFilter); + } + } +} + + +/*****************************************************/ +/* TnsFilter: */ +/* Filter the given spec with specified length */ +/* using the coefficients specified in filter. */ +/* Not that the order and direction are specified */ +/* withing the TNS_FILTER_DATA structure. */ +/*****************************************************/ +static void TnsFilter(int length,double* spec,TnsFilterData* filter) +{ + int i,j,k=0; + int order=filter->order; + double* a=filter->aCoeffs; + + /* Determine loop parameters for given direction */ + if (filter->direction) { + + /* Startup, initial state is zero */ + for (i=length-2;i>(length-1-order);i--) { + k++; + for (j=1;j<=k;j++) { + spec[i]-=spec[i+j]*a[j]; + } + } + + /* Now filter completely inplace */ + for (i=length-1-order;i>=0;i--) { + for (j=1;j<=order;j++) { + spec[i]-=spec[i+j]*a[j]; + } + } + + + } else { + + /* Startup, initial state is zero */ + for (i=1;iorder; + double* a=filter->aCoeffs; + double* temp; + + temp = (double *)AllocMemory(length * sizeof (double)); + + /* Determine loop parameters for given direction */ + if (filter->direction) { + + /* Startup, initial state is zero */ + temp[length-1]=spec[length-1]; + for (i=length-2;i>(length-1-order);i--) { + temp[i]=spec[i]; + k++; + for (j=1;j<=k;j++) { + spec[i]+=temp[i+j]*a[j]; + } + } + + /* Now filter the rest */ + for (i=length-1-order;i>=0;i--) { + temp[i]=spec[i]; + for (j=1;j<=order;j++) { + spec[i]+=temp[i+j]*a[j]; + } + } + + + } else { + + /* Startup, initial state is zero */ + temp[0]=spec[0]; + for (i=1;i= 0; i--) { + kArray[i] = (fabs(kArray[i])>threshold) ? kArray[i] : 0.0; + if (kArray[i]!=0.0) return i; + } + + return 0; +} + +/*****************************************************/ +/* QuantizeReflectionCoeffs: */ +/* Quantize the given array of reflection coeffs */ +/* to the specified resolution in bits. */ +/*****************************************************/ +static void QuantizeReflectionCoeffs(int fOrder, + int coeffRes, + double* kArray, + int* indexArray) +{ + double iqfac,iqfac_m; + int i; + + iqfac = ((1<<(coeffRes-1))-0.5)/(M_PI/2); + iqfac_m = ((1<<(coeffRes-1))+0.5)/(M_PI/2); + + /* Quantize and inverse quantize */ + for (i=1;i<=fOrder;i++) { + indexArray[i] = (int)(0.5+(asin(kArray[i])*((kArray[i]>=0)?iqfac:iqfac_m))); + kArray[i] = sin((double)indexArray[i]/((indexArray[i]>=0)?iqfac:iqfac_m)); + } +} + +/*****************************************************/ +/* Autocorrelation, */ +/* Compute the autocorrelation function */ +/* estimate for the given data. */ +/*****************************************************/ +static void Autocorrelation(int maxOrder, /* Maximum autocorr order */ + int dataSize, /* Size of the data array */ + double* data, /* Data array */ + double* rArray) /* Autocorrelation array */ +{ + int order,index; + + for (order=0;order<=maxOrder;order++) { + rArray[order]=0.0; + for (index=0;index + +#include "util.h" +#include "coder.h" // FRAME_LEN + +/* Returns the sample rate index */ +int GetSRIndex(unsigned int sampleRate) +{ + if (92017 <= sampleRate) return 0; + if (75132 <= sampleRate) return 1; + if (55426 <= sampleRate) return 2; + if (46009 <= sampleRate) return 3; + if (37566 <= sampleRate) return 4; + if (27713 <= sampleRate) return 5; + if (23004 <= sampleRate) return 6; + if (18783 <= sampleRate) return 7; + if (13856 <= sampleRate) return 8; + if (11502 <= sampleRate) return 9; + if (9391 <= sampleRate) return 10; + + return 11; +} + +/* Returns the maximum bitrate per channel for that sampling frequency */ +unsigned int MaxBitrate(unsigned long sampleRate) +{ + /* + * Maximum of 6144 bit for a channel + */ + return (unsigned int)(6144.0 * (double)sampleRate/(double)FRAME_LEN + .5); +} + +/* Returns the minimum bitrate per channel for that sampling frequency */ +unsigned int MinBitrate() +{ + return 8000; +} + + +/* Max prediction band for backward predictionas function of fs index */ +const int MaxPredSfb[] = { 33, 33, 38, 40, 40, 40, 41, 41, 37, 37, 37, 34, 0 }; + +int GetMaxPredSfb(int samplingRateIdx) +{ + return MaxPredSfb[samplingRateIdx]; +} + + + +/* Calculate bit_allocation based on PE */ +unsigned int BitAllocation(double pe, int short_block) +{ + double pew1; + double pew2; + double bit_allocation; + + if (short_block) { + pew1 = 0.6; + pew2 = 24.0; + } else { + pew1 = 0.3; + pew2 = 6.0; + } + bit_allocation = pew1 * pe + pew2 * sqrt(pe); + bit_allocation = min(max(0.0, bit_allocation), 6144.0); + + return (unsigned int)(bit_allocation+0.5); +} + +/* Returns the maximum bit reservoir size */ +unsigned int MaxBitresSize(unsigned long bitRate, unsigned long sampleRate) +{ + return 6144 - (unsigned int)((double)bitRate/(double)sampleRate*(double)FRAME_LEN); +} diff --git a/external/libfaac/libfaac/util.h b/external/libfaac/libfaac/util.h new file mode 100644 index 0000000..2233e78 --- /dev/null +++ b/external/libfaac/libfaac/util.h @@ -0,0 +1,59 @@ +/* + * FAAC - Freeware Advanced Audio Coder + * Copyright (C) 2001 Menno Bakker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: util.h,v 1.8 2003/12/20 04:32:48 stux Exp $ + */ + +#ifndef UTIL_H +#define UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include + +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* Memory functions */ +#define AllocMemory(size) malloc(size) +#define FreeMemory(block) free(block) +#define SetMemory(block, value, size) memset(block, value, size) + +int GetSRIndex(unsigned int sampleRate); +int GetMaxPredSfb(int samplingRateIdx); +unsigned int MaxBitrate(unsigned long sampleRate); +unsigned int MinBitrate(); +unsigned int MaxBitresSize(unsigned long bitRate, unsigned long sampleRate); +unsigned int BitAllocation(double pe, int short_block); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* UTIL_H */ diff --git a/external/libfaac/libfaac/version.h b/external/libfaac/libfaac/version.h new file mode 100644 index 0000000..94763e2 --- /dev/null +++ b/external/libfaac/libfaac/version.h @@ -0,0 +1,8 @@ +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define FAAC_RELEASE 1 + +#define FAAC_VERSION "1.28" + +#endif diff --git a/external/libfaac/outDebug.h b/external/libfaac/outDebug.h new file mode 100644 index 0000000..8457512 --- /dev/null +++ b/external/libfaac/outDebug.h @@ -0,0 +1,46 @@ + +#ifndef _OUTDEBUG_H +#define _OUTDEBUG_H + + + +#define DBG_MSG_BUFF_SIZE 3000 +#define FILE_NAME "-EasyAACEncoder-" + +#ifndef __linux__ +#include +#include +#include + +#define INFO_USE(fmt, ...) \ +{ \ + TCHAR traceBuffer[DBG_MSG_BUFF_SIZE]; \ + _stprintf_s(traceBuffer, _T(FILE_NAME) _T(" :INFO: ") _T(__FUNCTION__) _T("<%d>: ") _T(fmt) _T("\n"), __LINE__, ##__VA_ARGS__); \ + OutputDebugString(traceBuffer); \ +} + + +#define INFO_CHOOSEUSE(fmt, ...) {INFO_USE(fmt, ##__VA_ARGS__);} + +#define INFO_D(OUT,fmt, ...)\ +{ \ + if(OUT)\ + {\ + INFO_CHOOSEUSE(fmt, ##__VA_ARGS__);\ + }\ +} + +#else +#define INFO_D(OUT,fmt, ...)\ +{ \ + if(OUT)\ + {\ + printf(fmt, ##__VA_ARGS__);\ + }\ +} +#endif + + +#define AAC_DEBUG 0 + +#endif diff --git a/hal/abstract/IHalCpp.cpp b/hal/abstract/IHalCpp.cpp index 7395da0..23ae1ef 100644 --- a/hal/abstract/IHalCpp.cpp +++ b/hal/abstract/IHalCpp.cpp @@ -18,8 +18,9 @@ #include #include #include -camera_report_event::camera_report_event(const std::string &fileName, const CameraType &cameraType) - : mFileName(fileName), mCameraType(cameraType) +camera_report_event::camera_report_event(const std::string &fileName, const CameraType &cameraType, + const unsigned int &duration_ms) + : mFileName(fileName), mCameraType(cameraType), mDuration_ms(duration_ms) { } void VKeyHalMonitor::KeyEventHappened(const std::string &keyName, const VirtualKeyEvent &event, @@ -72,7 +73,7 @@ StatusCode VCameraTaskContext::TaskFinished(void) LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } -camera_task_param::camera_task_param(const CameraTaskType &cameraTask) : mCameraTask(cameraTask) +camera_task_param::camera_task_param(const CameraTaskType &cameraTask) : mTaskType(cameraTask) { mVideoRecordingTimeMs = DEFAULT_VIDEO_RECORDING_TIME_MS; } @@ -100,6 +101,11 @@ StatusCode VCameraHal::SetVideoStreamCallback(VideoStreamCallback callback) LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } +StatusCode VCameraHal::SetJpegEncodeCallback(JpegEncodeCallback callback) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} void VSdCardHalMonitor::ReportEvent(const SdCardHalStatus &status) { LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); @@ -133,6 +139,11 @@ StatusCode VSdCardHal::GetCapacity(unsigned long long &totalSizeMB, unsigned lon LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } +StatusCode VSdCardHal::FormatSDCard(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} StatusCode IHalCpp::Init(void) { LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); diff --git a/hal/include/IHalCpp.h b/hal/include/IHalCpp.h index 344e294..fe46376 100644 --- a/hal/include/IHalCpp.h +++ b/hal/include/IHalCpp.h @@ -50,9 +50,10 @@ enum class CameraTaskType }; typedef struct camera_report_event { - camera_report_event(const std::string &fileName, const CameraType &cameraType); + camera_report_event(const std::string &fileName, const CameraType &cameraType, const unsigned int &duration_ms); const std::string mFileName; const CameraType mCameraType; + const unsigned int mDuration_ms; } CameraReportEvent; void CreateHalCppModule(void); void DestroyHalCppModule(void); @@ -118,14 +119,13 @@ constexpr int DEFAULT_VIDEO_RECORDING_TIME_MS = 10 * 1000; typedef struct camera_task_param { camera_task_param(const CameraTaskType &cameraTask); - const CameraTaskType mCameraTask; - unsigned int mVideoRecordingTimeMs; + const CameraTaskType mTaskType; + unsigned int mVideoRecordingTimeMs; // TODO: delete? std::shared_ptr mCtx; } CameraTaskParam; -// using AudioStreamCallback = void (*)(const void *, const int, const unsigned long long); -// using VideoStreamCallback = void (*)(const void *, const int, const unsigned long long); using AudioStreamCallback = std::function; using VideoStreamCallback = std::function; +using JpegEncodeCallback = std::function; class VCameraHal { public: @@ -136,6 +136,7 @@ public: virtual StatusCode StopTask(void); virtual StatusCode SetAudioStreamCallback(AudioStreamCallback callback); virtual StatusCode SetVideoStreamCallback(VideoStreamCallback callback); + virtual StatusCode SetJpegEncodeCallback(JpegEncodeCallback callback); }; class VSdCardHalMonitor { @@ -153,6 +154,7 @@ public: virtual SdCardHalStatus GetSdCardStatus(void); virtual StatusCode GetCapacity(unsigned long long &totalSizeMB, unsigned long long &freeSizeMB, unsigned long long &usedSizeMB); + virtual StatusCode FormatSDCard(void); }; class IHalCpp { diff --git a/hal/src/CameraHal.cpp b/hal/src/CameraHal.cpp index c5ae1c7..68113a0 100644 --- a/hal/src/CameraHal.cpp +++ b/hal/src/CameraHal.cpp @@ -15,8 +15,12 @@ #include "CameraHal.h" #include "IHalCpp.h" #include "ILog.h" +#include "LinuxApi.h" #include "StatusCode.h" -CameraHal::CameraHal() : mTaskRuning(false), mAudioStreamCallback(nullptr), mVideoStreamCallback(nullptr) +#include +CameraHal::CameraHal() + : mTaskRuning(false), mAudioStreamCallback(nullptr), mVideoStreamCallback(nullptr), mVideoFile(nullptr), + mAudioFile(nullptr), mTaskType(CameraTaskType::END), mFastBootSaveFile(false) { } void CameraHal::Init(void) @@ -25,15 +29,39 @@ void CameraHal::Init(void) void CameraHal::UnInit(void) { } +void CameraHal::SetCameraMonitor(std::shared_ptr &monitor) +{ + std::lock_guard locker(mMutex); + LogInfo("SetCameraMonitor.\n"); + mCameraMonitor = monitor; + if (nullptr != mFastBootEvent) { + monitor->ReportEvent(*(mFastBootEvent.get())); + } +} StatusCode CameraHal::StartSingleTask(const CameraTaskParam ¶m) { LogInfo("StartSingleTask.\n"); + // mTaskType = param.mTaskType; mTaskRuning = true; + fx_system_v2("rm -f " SD_CARD_MOUNT_PATH "/chip.g711a"); + fx_system_v2("rm -f " SD_CARD_MOUNT_PATH "/chip.h264"); + mAudioFile = fopen(SD_CARD_MOUNT_PATH "/chip.g711a", "a+"); + mVideoFile = fopen(SD_CARD_MOUNT_PATH "/chip.h264", "a+"); return CreateStatusCode(STATUS_CODE_OK); } StatusCode CameraHal::StopTask(void) { mTaskRuning = false; + if (mAudioFile) { + fclose(mAudioFile); + mAudioFile = nullptr; + } + if (mVideoFile) { + fclose(mVideoFile); + mVideoFile = nullptr; + } + // mTaskType = CameraTaskType::END; + fx_system_v2("sync"); return CreateStatusCode(STATUS_CODE_OK); } StatusCode CameraHal::SetAudioStreamCallback(AudioStreamCallback callback) @@ -46,15 +74,80 @@ StatusCode CameraHal::SetVideoStreamCallback(VideoStreamCallback callback) mVideoStreamCallback = callback; return CreateStatusCode(STATUS_CODE_OK); } +StatusCode CameraHal::SetJpegEncodeCallback(JpegEncodeCallback callback) +{ + mJpegEncodeCallback = callback; + return CreateStatusCode(STATUS_CODE_OK); +} void CameraHal::GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { if (mTaskRuning && nullptr != mAudioStreamCallback) { + SaveChipStream(ChipStreamType::AUDIO, stream, length); mAudioStreamCallback(stream, length, timeStamp); } } void CameraHal::GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { if (mTaskRuning && nullptr != mVideoStreamCallback) { + SaveChipStream(ChipStreamType::VIDEO, stream, length); mVideoStreamCallback(stream, length, timeStamp); } +} +void CameraHal::GetJpegData(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) +{ + if (mTaskRuning && nullptr != mJpegEncodeCallback) { + mJpegEncodeCallback(stream, length, timeStamp); + } + else { + FastStartGetJpegData(stream, length, timeStamp); + } +} +void CameraHal::SaveChipStream(const ChipStreamType &streamType, const void *stream, const unsigned int &length) +{ + FILE *file = nullptr; + file = streamType == ChipStreamType::AUDIO ? mAudioFile : mVideoFile; + if (file) { + fwrite(stream, 1, length, file); + fflush(file); + } +} +void CameraHal::FastStartGetJpegData(const void *stream, const unsigned int &length, + const unsigned long long &timeStamp) +{ + std::lock_guard locker(mMutex); + if (mFastBootSaveFile) { + return; + } + std::string savedFile = SaveJpeg(stream, length); + if (savedFile.empty()) { + LogError("save jpeg failed.\n"); + return; + } + mFastBootSaveFile = true; + auto monitor = mCameraMonitor.lock(); + if (mCameraMonitor.expired()) { + LogWarning("SdCardHal: monitor is expired.\n"); + mFastBootEvent = std::make_shared(savedFile, CameraType::MAIN_CAMERA, 0); + return; + } + CameraReportEvent report(savedFile, CameraType::MAIN_CAMERA, 0); + monitor->ReportEvent(report); +} +std::string CameraHal::SaveJpeg(const void *data, unsigned int dataLength) +{ + const std::string SAVE_JPEG_FAILED = ""; + const std::string SaveJpegPath = "/tmp/fastboot.jpeg"; + FILE *fp = nullptr; + fp = fopen(SaveJpegPath.c_str(), "wb"); + if (fp) { + fwrite(data, 1, dataLength, fp); + fflush(fp); + fclose(fp); + LogInfo("save fastboot jpeg success.\n"); + return SaveJpegPath; + } + else { + LogInfo("open file error\n"); + return SAVE_JPEG_FAILED; + } } \ No newline at end of file diff --git a/hal/src/CameraHal.h b/hal/src/CameraHal.h index 1ab18c0..29386af 100644 --- a/hal/src/CameraHal.h +++ b/hal/src/CameraHal.h @@ -18,6 +18,12 @@ #include #include constexpr unsigned int NO_MEDIA_RECORDING = -1; +enum class ChipStreamType +{ + VIDEO = 0, + AUDIO, + END +}; class CameraHal : public VCameraHal, public std::enable_shared_from_this { public: @@ -25,21 +31,40 @@ public: virtual ~CameraHal() = default; virtual void Init(void); virtual void UnInit(void); + void SetCameraMonitor(std::shared_ptr &monitor) override; protected: StatusCode StartSingleTask(const CameraTaskParam ¶m) override; StatusCode StopTask(void) override; StatusCode SetAudioStreamCallback(AudioStreamCallback callback) override; StatusCode SetVideoStreamCallback(VideoStreamCallback callback) override; + StatusCode SetJpegEncodeCallback(JpegEncodeCallback callback) override; protected: void GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp); void GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp); + void GetJpegData(const void *stream, const unsigned int &length, const unsigned long long &timeStamp); + void SaveChipStream(const ChipStreamType &streamType, const void *stream, const unsigned int &length); + +private: + void FastStartGetJpegData(const void *stream, const unsigned int &length, const unsigned long long &timeStamp); + std::string SaveJpeg(const void *data, unsigned int dataLength); private: // std::mutex mMutex; + std::mutex mMutex; bool mTaskRuning; AudioStreamCallback mAudioStreamCallback; VideoStreamCallback mVideoStreamCallback; + JpegEncodeCallback mJpegEncodeCallback; + /** + * @brief Each time a media task is executed, the original data stream of the chip is saved to the SD card. + */ + FILE *mVideoFile; ///< The original video stream data is saved. + FILE *mAudioFile; ///< The original audio stream data is saved. + CameraTaskType mTaskType; + std::weak_ptr mCameraMonitor; + std::shared_ptr mFastBootEvent; + bool mFastBootSaveFile; }; #endif \ No newline at end of file diff --git a/hal/src/SdCardHal.cpp b/hal/src/SdCardHal.cpp index 5b1237f..000edd4 100644 --- a/hal/src/SdCardHal.cpp +++ b/hal/src/SdCardHal.cpp @@ -65,6 +65,26 @@ StatusCode SdCardHal::GetCapacity(unsigned long long &totalSizeMB, unsigned long LogInfo("Used size: %llu MB\n", usedSizeMB); return CreateStatusCode(STATUS_CODE_OK); } +/** + * @brief To format an SD card, you normally need to unmount the SD card before formatting it. However, when the SD card + * is abnormal (usually it cannot be mounted or unmounted), you still need to execute the format command operation, + * because some cards can be restored to normal by formatting when they are abnormal. + * @return StatusCode + */ +StatusCode SdCardHal::FormatSDCard(void) +{ + UnInit(); + char command[128] = {0}; + sprintf(command, "mkfs.vfat -F 32 %s", SD_CARD_DEVICE); + int ret = fx_system(command); + if (ret != 0) { + LogError("Format sd card failed [%s].\n", SD_CARD_DEVICE); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + LogInfo("Format sd card success.\n"); + Init(); + return CreateStatusCode(STATUS_CODE_OK); +} void SdCardHal::Init(void) { auto detectThread = [](std::shared_ptr sdCardHal) { @@ -79,6 +99,7 @@ void SdCardHal::UnInit(void) if (mDevDetectingThread.joinable()) { mDevDetectingThread.join(); } + UnmountSdCard(); } void SdCardHal::DevDetectingThread(void) { @@ -132,8 +153,8 @@ void SdCardHal::DevDetectingThread(void) } else { // LogInfo("sdCardHal: %s is inserted.\n", SD_CARD_DEVICE); - if (SdCardHalStatus::INSERTED != mStatus && SdCardHalStatus::MOUNTED != mStatus && - SdCardHalStatus::UNMOUNTED != mStatus) { + if (SdCardHalStatus::INSERTED != mStatus && SdCardHalStatus::MOUNTED != mStatus /* && + SdCardHalStatus::UNMOUNTED != mStatus*/) { mStatus = SdCardHalStatus::INSERTED; ReportDetecedChangedResult(mStatus); } @@ -151,13 +172,20 @@ void SdCardHal::ReportDetecedChangedResult(const SdCardHalStatus &status) LogInfo("SdCardHalStatus changed: %s.\n", PrintfStatusString(status)); SdCardHalStatus mountedStatus = SdCardHalStatus::END; if (SdCardHalStatus::INSERTED == status) { - LogInfo("mount sd to %s.\n", SD_CARD_MOUNT_PATH); constexpr int BUF_LENGTH = 128; char cmd[BUF_LENGTH] = {0}; snprintf(cmd, BUF_LENGTH, "mount %s %s", SD_CARD_DEV, SD_CARD_MOUNT_PATH); - fx_system(cmd); - mountedStatus = SdCardHalStatus::MOUNTED; - mStatus = SdCardHalStatus::MOUNTED; + LogInfo("system cmd: %s.\n", cmd); + int ret = fx_system(cmd); + constexpr int SYSTEM_SUCCESS = 0; + if (SYSTEM_SUCCESS == ret) { + LogInfo("mount sd card SUCCESS.\n"); + mountedStatus = SdCardHalStatus::MOUNTED; + mStatus = SdCardHalStatus::MOUNTED; + } + else { + LogError("mount sd card failed.\n"); + } } auto monitor = mMonitor.lock(); if (mMonitor.expired()) { @@ -190,4 +218,24 @@ const char *SdCardHal::PrintfStatusString(const SdCardHalStatus &status) default: return "UNDEFINE."; } +} +void SdCardHal::UnmountSdCard(void) +{ + char command[128] = {0}; + /** TODO: + * @brief Here, the SD card is forcibly loaded, and the DevDetectingThread function of this article causes the SD + * card node to be occupied and cannot be loaded. To find problems and optimize them, it is especially important to + * check whether there are any omissions. + */ + sprintf(command, "umount -fl %s", SD_CARD_DEVICE); + int ret = fx_system(command); + if (ret != 0) { + LogError("Format sd card failed [%s].\n", SD_CARD_DEVICE); + return; + } + mStatus = SdCardHalStatus::UNMOUNTED; + ReportDetecedChangedResult(mStatus); +} +void SdCardHal::MountSdCard(void) +{ } \ No newline at end of file diff --git a/hal/src/SdCardHal.h b/hal/src/SdCardHal.h index 6050c67..3adbaad 100644 --- a/hal/src/SdCardHal.h +++ b/hal/src/SdCardHal.h @@ -25,6 +25,7 @@ public: SdCardHalStatus GetSdCardStatus(void) override; StatusCode GetCapacity(unsigned long long &totalSizeMB, unsigned long long &freeSizeMB, unsigned long long &usedSizeMB) override; + StatusCode FormatSDCard(void) override; void Init(void); void UnInit(void); void DevDetectingThread(void); @@ -32,6 +33,8 @@ public: private: void ReportDetecedChangedResult(const SdCardHalStatus &status); const char *PrintfStatusString(const SdCardHalStatus &status); + void UnmountSdCard(void); + void MountSdCard(void); private: SdCardHalStatus mStatus; diff --git a/hal/src/WifiHal.cpp b/hal/src/WifiHal.cpp index e7e7cd0..7a15930 100644 --- a/hal/src/WifiHal.cpp +++ b/hal/src/WifiHal.cpp @@ -55,10 +55,10 @@ StatusCode WifiHal::OpenApMode(void) } } LogInfo("wlan0 ok. \n"); - fx_system("ifconfig wlan0 192.168.169.1 netmask 255.255.255.0"); - fx_system("hostapd -d /etc/hostapd.conf &"); - fx_system("touch /var/lib/misc/udhcpd.leases"); - fx_system("udhcpd -f /etc/udhcpd.conf &"); + fx_system_v2("ifconfig wlan0 192.168.169.1 netmask 255.255.255.0"); + fx_system_v2("hostapd -d /etc/hostapd.conf &"); + fx_system_v2("touch /var/lib/misc/udhcpd.leases"); + fx_system_v2("udhcpd -f /etc/udhcpd.conf &"); return CreateStatusCode(STATUS_CODE_OK); } StatusCode WifiHal::CloseApMode(void) diff --git a/middleware/AppManager/include/IAppManager.h b/middleware/AppManager/include/IAppManager.h index c418a55..f6ca8f4 100644 --- a/middleware/AppManager/include/IAppManager.h +++ b/middleware/AppManager/include/IAppManager.h @@ -74,15 +74,20 @@ enum class StorageFileType VIDEO, END }; +/** + * @brief A file classification list mapped to hunting camera scenes based on the dash cam protocol. + * NOTE: ALL is the process of getting all files in the protocol, while LOOP is the "all" files mapped to the hunting + * camera scene according to the protocol. ALL includes LOOP and other types outside LOOP, and LOOP returns the "all" + * file list in the hunting camera scene. + */ enum class StorageFileEvent { - LOOP = 0, - PIR, - CRASH, - EMR, - EVENT, - PARK, - END + LOOP = 0, ///< Loop event.In the hunting camera scene, all files are represented. + EMR, ///< EMR event.In the hunting camera scenario, it indicates the PIR snapshot file. + EVENT, ///< General event.In the hunting camera scenario, it indicates a manually captured file. + PARK, ///< Park event.In the hunting camera scenario, it indicates a timed snapshot file. + ALL, ///< No distinction between categories, all files. + END ///< End of events. }; enum class SwitchStatus { @@ -338,5 +343,12 @@ public: virtual const StatusCode UnInit(void); virtual const StatusCode SetAppMonitor(std::shared_ptr &monitor); virtual StatusCode SetSdCardStatus(const SdCardStatus &status); + virtual const char *StorageFileEventToString(const StorageFileEvent &event); + /** + * @brief Get the document root directory configured by the web server of the APP management module. The mobile APP + * can access the files in the directory through the network. + * @return std::string Path of the document root directory. + */ + virtual std::string GetFilesSavingRootPath(void); }; #endif \ No newline at end of file diff --git a/middleware/AppManager/src/AppManager.cpp b/middleware/AppManager/src/AppManager.cpp index 2cd7163..cce28f4 100644 --- a/middleware/AppManager/src/AppManager.cpp +++ b/middleware/AppManager/src/AppManager.cpp @@ -23,6 +23,7 @@ #include "TcpModule.h" #include "WebServer.h" #include +#include #include AppManager::AppManager() : mTcpServer(nullptr), mInitRuning(false) { @@ -60,6 +61,7 @@ GOAHEAD: std::shared_ptr protocolHandle = std::make_shared(); IAppProtocolHandle::GetInstance(&protocolHandle); mAppMonitor.reset(); + LogInfo("AppManager uninit success.\n"); return CreateStatusCode(STATUS_CODE_OK); } const StatusCode AppManager::SetAppMonitor(std::shared_ptr &monitor) @@ -75,6 +77,32 @@ StatusCode AppManager::SetSdCardStatus(const SdCardStatus &status) } return CreateStatusCode(STATUS_CODE_OK); } +const char *AppManager::StorageFileEventToString(const StorageFileEvent &event) +{ + switch (event) { + case StorageFileEvent::LOOP: + return "LOOP"; + case StorageFileEvent::EMR: + return "EMR"; + case StorageFileEvent::EVENT: + return "EVENT"; + case StorageFileEvent::PARK: + return "PARK"; + case StorageFileEvent::ALL: + return "ALL"; + case StorageFileEvent::END: + return "END"; + + default: + return "UNDEFINE"; + break; + } +} +std::string AppManager::GetFilesSavingRootPath(void) +{ + std::string rootPath = GetWebServerDocumentRoot(); + return rootPath; +} void AppManager::AppRequestHandle(const char *url, const unsigned int urlLength, ResponseHandle responseHandle, void *context) { @@ -177,6 +205,7 @@ void AppManager::TcpServerStop(void) { if (nullptr != mTcpServer) { FreeTcpServer(mTcpServer); + mTcpServer = nullptr; } } void AppManager::WifiApModeInit(const AppParam ¶m) diff --git a/middleware/AppManager/src/AppManager.h b/middleware/AppManager/src/AppManager.h index 0034e8d..82226d3 100644 --- a/middleware/AppManager/src/AppManager.h +++ b/middleware/AppManager/src/AppManager.h @@ -27,6 +27,8 @@ public: const StatusCode UnInit(void) override; const StatusCode SetAppMonitor(std::shared_ptr &monitor) override; StatusCode SetSdCardStatus(const SdCardStatus &status) override; + const char *StorageFileEventToString(const StorageFileEvent &event) override; + std::string GetFilesSavingRootPath(void) override; void AppRequestHandle(const char *url, const unsigned int urlLength, ResponseHandle responseHandle, void *context); void AppRequestHandleTcp(const char *data, const unsigned int dataLength, ResponseHandle responseHandle, void *context); diff --git a/middleware/AppManager/src/IAppManager.cpp b/middleware/AppManager/src/IAppManager.cpp index aba2ba6..7b8d8b4 100644 --- a/middleware/AppManager/src/IAppManager.cpp +++ b/middleware/AppManager/src/IAppManager.cpp @@ -100,92 +100,116 @@ app_param::app_param(const char *ip, const int &httpPort, const int &tcpPort) } void VAppClient::SetRecordingStatus(const RecordingStatus &status) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); } void VAppClient::SetMicrophoneStatus(const MicrophoneStatus &status) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); } void VAppClient::SetBatteryStatus(const BatteryStatus &status, const int &batteryCapacity) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); } void VAppClient::SetSdCardStatus(const SdCardStatus &status) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); } void VAppClient::DeletedFileMessage(const std::string &file, const StorageFileType &type) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); } void VAppClient::CreatedFileMessage(const std::string &file, const StorageFileType &type) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); } StatusCode VAppMonitor::GetProductInfo(AppGetProductInfo ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetDeviceAttr(AppGetDeviceAttr ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetMediaInfo(AppGetMediaInfo ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetSdCardInfo(AppGetSdCardInfo ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetBatteryInfo(AppGetBatteryInfo ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetParamValue(AppParamValue ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetCapability(AppGetCapability ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetLockVideoStatus(LockVideoStatus ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetStorageInfo(std::vector ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetStorageFileList(const AppGetFileInfo &fileInfo, std::vector ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::SetDateTime(const AppSetDateTime ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::SetTimeZone(const unsigned int &zone) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::SetParamValue(const AppSetParamValue ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::EnterRecorder(void) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::AppPlayback(const PlayBackEvent &event) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::UploadFile(AppUploadFile ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::GetThumbnail(AppGetThumbnail ¶m) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VAppMonitor::AppClientConnected(std::shared_ptr &client) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } std::shared_ptr &IAppManager::GetInstance(std::shared_ptr *impl) @@ -204,17 +228,31 @@ std::shared_ptr &IAppManager::GetInstance(std::shared_ptr &monitor) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode IAppManager::SetSdCardStatus(const SdCardStatus &status) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +const char *IAppManager::StorageFileEventToString(const StorageFileEvent &event) +{ + return "STATUS_CODE_VIRTUAL_FUNCTION"; +} +std::string IAppManager::GetFilesSavingRootPath(void) +{ + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + const std::string path = "unknwon"; + return path; } \ No newline at end of file diff --git a/middleware/AppManager/src/Protocol/SixFrame/SixFrameHandle.cpp b/middleware/AppManager/src/Protocol/SixFrame/SixFrameHandle.cpp index 7f35fd4..2993de8 100644 --- a/middleware/AppManager/src/Protocol/SixFrame/SixFrameHandle.cpp +++ b/middleware/AppManager/src/Protocol/SixFrame/SixFrameHandle.cpp @@ -597,40 +597,68 @@ AppGetFileInfo inline SixFrameHandle::RequestGetFileListParse(const std::string }; std::shared_ptr parse = std::make_shared>(); std::shared_ptr> parseImpl = std::dynamic_pointer_cast>(parse); + /** + * @brief If there is no "folder" field, it means to obtain all types of files (including: LOOP, EMR, EVENT, PARK). + * Example: http://192.168.1.100/app/getfilelist ==> no "folder" field + */ + parseImpl->mData.mEvent = StorageFileEvent::ALL; ExtractParamsFromUrl(url, parseFunc, parse); return parseImpl->mData; } +/** + * @brief The code discussion at this stage is a bit confusing, because it is based on the projection of the file types + * that mobile apps use to hunt for opponents. If you still have doubts, please refer to the README document. + */ void SixFrameHandle::RequestGetFileList(const std::string &url, ResponseHandle responseHandle, void *context) { LogInfo("RequestGetFileList.\n"); std::vector files; AppGetFileInfo info = RequestGetFileListParse(url); - mAppMonitor->GetStorageFileList(info, files); cJSON *result = MakeResponseResult(ResposeResult::SUCCESSFUL); - if (nullptr == result) { + cJSON *infoJson = cJSON_CreateArray(); + if (nullptr == result || nullptr == infoJson) { LogError("MakeResponseResult failed.\n"); responseHandle("Device run out of memory.", context); return; } - ResponseGetFileList(result, files, info); + LogInfo("info.mEvent: %s\n", IAppManager::GetInstance()->StorageFileEventToString(info.mEvent)); + cJSON_AddItemToObject(result, CJSON_INFO_STRING, infoJson); + if (StorageFileEvent::ALL == info.mEvent) { // "ALL" means that all types of files are retrieved once. + for (int i = 0; i < static_cast(StorageFileEvent::ALL); i++) { + files.clear(); + info.mEvent = static_cast(i); + LogInfo("GetStorageFileList: %s\n", IAppManager::GetInstance()->StorageFileEventToString(info.mEvent)); + mAppMonitor->GetStorageFileList(info, files); + ResponseGetFileList(infoJson, files, info); + } + } + else if (StorageFileEvent::LOOP == info.mEvent) { // "LOOP" means to get all files at once. + info.mEvent = StorageFileEvent::ALL; + mAppMonitor->GetStorageFileList(info, files); + ResponseGetFileList(infoJson, files, info); + } + else { + mAppMonitor->GetStorageFileList(info, files); + ResponseGetFileList(infoJson, files, info); + } ResponseJsonString(result, responseHandle, context); cJSON_Delete(result); } -void inline SixFrameHandle::ResponseGetFileList(cJSON *result, const std::vector ¶m, +void inline SixFrameHandle::ResponseGetFileList(cJSON *resultInfo, const std::vector ¶m, const AppGetFileInfo &fileInfo) { - cJSON *info = cJSON_CreateArray(); + // cJSON *info = cJSON_CreateArray(); cJSON *folder = cJSON_CreateObject(); cJSON *files = cJSON_CreateArray(); - if (nullptr == info || nullptr == folder || nullptr == files) { + if (nullptr == resultInfo || nullptr == folder || nullptr == files) { LogError("cJSON_CreateArray failed.\n"); return; } - cJSON_AddItemToObject(result, CJSON_INFO_STRING, info); - cJSON_AddItemToArray(info, folder); + // cJSON_AddItemToObject(result, CJSON_INFO_STRING, info); + cJSON_AddItemToArray(resultInfo, folder); + cJSON_AddStringToObject(folder, "folder", PrintfFileEvent(fileInfo)); + cJSON_AddNumberToObject(folder, "count", param.size()); for (const auto &fileList : param) { - cJSON_AddStringToObject(folder, "folder", PrintfFileEvent(fileInfo)); - cJSON_AddNumberToObject(folder, "count", param.size()); cJSON *file = nullptr; file = cJSON_CreateObject(); if (nullptr != file) { @@ -982,9 +1010,18 @@ void SixFrameHandle::ResponseJsonString(cJSON *json, ResponseHandle responseHand const char *SixFrameHandle::PrintfFileEvent(const AppGetFileInfo &fileInfo) { switch (fileInfo.mEvent) { + case StorageFileEvent::ALL: case StorageFileEvent::LOOP: { return "loop"; - break; + } + case StorageFileEvent::EMR: { + return "emr"; + } + case StorageFileEvent::EVENT: { + return "event"; + } + case StorageFileEvent::PARK: { + return "park"; } default: { diff --git a/middleware/FilesManager/include/IFilesManager.h b/middleware/FilesManager/include/IFilesManager.h index 8bca437..576a08f 100644 --- a/middleware/FilesManager/include/IFilesManager.h +++ b/middleware/FilesManager/include/IFilesManager.h @@ -19,13 +19,16 @@ #include #include #include +/** + * @brief Enum defining different types of file creation. + */ enum class FileCreateType { - PIR = 0, - MANUAL_TEST, - MANUAL_PHONE, - TIMED, - END + PIR = 0, ///< Automatically created file (PIR triggered). + MANUAL_TEST, ///< File created for manual testing purposes. + MANUAL_PHONE, ///< File created manually by phone. + TIMED, ///< File created on a timed schedule. + END ///< End. }; enum class FileStatus { @@ -36,16 +39,20 @@ enum class FileStatus UPLOADED, END }; -constexpr unsigned long UNDEFINE_SERIAL_NUMBER = -1; +constexpr unsigned long UNDEFINE_SERIAL_NUMBER = 0; constexpr unsigned long UNDEFINE_FILE_SIZE = 0; +constexpr unsigned long UNDEFINE_FILE_DURATION = 0; constexpr time_t UNDEFINE_CREATE_TIME = -1; typedef struct sync_file_info { - sync_file_info(const unsigned long &serialNumber, const std::string &fileName, const unsigned long &fileSize, - const time_t &createTime_s, const FileCreateType &type, const FileStatus &status); + sync_file_info(const unsigned long &serialNumber, const std::string &fileName, const unsigned int &fileSize, + const unsigned int &fileDuration, const time_t &createTime_s, const FileCreateType &type, + const FileStatus &status); const unsigned long mSerialNumber; const std::string mFileName; - const unsigned long mFileSize; + std::string mSourceFile; ///< If the source file exists, it will be copied to overwrite the mFileName. + const unsigned int mFileSize; + const unsigned int mFileDuration; const time_t mCreateTime_s; const FileCreateType mType; const FileStatus mStatus; @@ -58,8 +65,8 @@ typedef struct save_file_info typedef struct info_to_be_saved { info_to_be_saved(const unsigned long &serialNumber, const std::string mSavingPath); - const unsigned long mSerialNumber; - const std::string mSavingPath; + const unsigned long mSerialNumber; ///< The unique key value of the file in the database. + const std::string mSavingPath; ///< The path where the file will be saved. } InfoToBeSaved; bool CreateFilesManagerModule(void); bool DestroyFilesManagerModule(void); @@ -71,8 +78,16 @@ public: static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); virtual StatusCode Init(void); virtual StatusCode UnInit(void); - virtual StatusCode SaveFile(const SaveFileInfo &fileInfo); + virtual StatusCode SaveFile(const SaveFileInfo &fileInfo); // TODO: delete virtual InfoToBeSaved GetInfoForSavingFiles(const unsigned int &count); + /** + * @brief Synchronize file information. + * + * @param info + * @return StatusCode + */ virtual StatusCode SaveFiles(const std::vector &info); + virtual StatusCode GetAllFiles(std::vector &info); + virtual StatusCode GetFiles(const std::vector &types, std::vector &info); }; #endif \ No newline at end of file diff --git a/middleware/FilesManager/src/FilesDatabase.h b/middleware/FilesManager/src/FilesDatabase.h index c70a03f..da82781 100644 --- a/middleware/FilesManager/src/FilesDatabase.h +++ b/middleware/FilesManager/src/FilesDatabase.h @@ -17,6 +17,7 @@ #include "FilesHandle.h" #include "IFilesManager.h" #include "StatusCode.h" +constexpr FileCreateType END_MEANS_SEARCHING_ALL_FILES = FileCreateType::END; class FilesDatabase : public FilesHandle { public: @@ -24,8 +25,9 @@ public: virtual ~FilesDatabase() = default; void Init(void); void UnInit(void); - StatusCode DbSaveFile(const SaveFileInfo &fileInfo); + StatusCode DbSaveFile(const SaveFileInfo &fileInfo); // TODO: delete InfoToBeSaved CreateInfoForSavingFiles(const unsigned int &count); StatusCode DbSaveFiles(const std::vector &info); + StatusCode DbGetFiles(const std::vector &types, std::vector &info); }; #endif \ No newline at end of file diff --git a/middleware/FilesManager/src/FilesManagerImpl.cpp b/middleware/FilesManager/src/FilesManagerImpl.cpp index e3b10f2..d6c3815 100644 --- a/middleware/FilesManager/src/FilesManagerImpl.cpp +++ b/middleware/FilesManager/src/FilesManagerImpl.cpp @@ -39,4 +39,14 @@ InfoToBeSaved FilesManagerImpl::GetInfoForSavingFiles(const unsigned int &count) StatusCode FilesManagerImpl::SaveFiles(const std::vector &info) { return FilesDatabase::DbSaveFiles(info); +} +StatusCode FilesManagerImpl::GetAllFiles(std::vector &info) +{ + std::vector types; + types.push_back(END_MEANS_SEARCHING_ALL_FILES); + return FilesDatabase::DbGetFiles(types, info); +} +StatusCode FilesManagerImpl::GetFiles(const std::vector &types, std::vector &info) +{ + return FilesDatabase::DbGetFiles(types, info); } \ No newline at end of file diff --git a/middleware/FilesManager/src/FilesManagerImpl.h b/middleware/FilesManager/src/FilesManagerImpl.h index d73989f..3e73771 100644 --- a/middleware/FilesManager/src/FilesManagerImpl.h +++ b/middleware/FilesManager/src/FilesManagerImpl.h @@ -29,5 +29,7 @@ protected: StatusCode SaveFile(const SaveFileInfo &fileInfo) override; InfoToBeSaved GetInfoForSavingFiles(const unsigned int &count) override; StatusCode SaveFiles(const std::vector &info) override; + StatusCode GetAllFiles(std::vector &info) override; + StatusCode GetFiles(const std::vector &types, std::vector &info) override; }; #endif \ No newline at end of file diff --git a/middleware/FilesManager/src/IFilesManager.cpp b/middleware/FilesManager/src/IFilesManager.cpp index 1381134..faa8ca1 100644 --- a/middleware/FilesManager/src/IFilesManager.cpp +++ b/middleware/FilesManager/src/IFilesManager.cpp @@ -23,10 +23,10 @@ save_file_info::save_file_info(const std::string &fileName) : mFileName(fileName { } sync_file_info::sync_file_info(const unsigned long &serialNumber, const std::string &fileName, - const unsigned long &fileSize, const time_t &createTime_s, const FileCreateType &type, - const FileStatus &status) - : mSerialNumber(serialNumber), mFileName(fileName), mFileSize(fileSize), mCreateTime_s(createTime_s), mType(type), - mStatus(status) + const unsigned int &fileSize, const unsigned int &fileDuration, + const time_t &createTime_s, const FileCreateType &type, const FileStatus &status) + : mSerialNumber(serialNumber), mFileName(fileName), mSourceFile(""), mFileSize(fileSize), + mFileDuration(fileDuration), mCreateTime_s(createTime_s), mType(type), mStatus(status) { } info_to_be_saved::info_to_be_saved(const unsigned long &serialNumber, const std::string mSavingPath) @@ -69,6 +69,16 @@ InfoToBeSaved IFilesManager::GetInfoForSavingFiles(const unsigned int &count) return info; } StatusCode IFilesManager::SaveFiles(const std::vector &info) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IFilesManager::GetAllFiles(std::vector &info) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IFilesManager::GetFiles(const std::vector &types, std::vector &info) { LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); diff --git a/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp b/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp index a6bf21b..33c495d 100644 --- a/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp +++ b/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp @@ -57,11 +57,29 @@ InfoToBeSaved FilesDatabase::CreateInfoForSavingFiles(const unsigned int &count) StatusCode FilesDatabase::DbSaveFiles(const std::vector &info) { for (auto &each : info) { - bool result = SqliteHandle::GetInstance()->SyncFile(each); + unsigned long key = UNDEFINE_SERIAL_NUMBER; + bool result = SqliteHandle::GetInstance()->SyncFile(each, key); if (!result) { LogError("Save file failed.\n"); return CreateStatusCode(STATUS_CODE_NOT_OK); } + if (!each.mSourceFile.empty()) { + /** + * @brief If the source file parameter is passed in, the source file needs to be copied to overwrite the + * target file. + */ + std::string saveFile = SqliteHandle::RenameFileToSync(each.mFileName, key); + IStorageManager::GetInstance()->SaveFile(each.mSourceFile, saveFile); + } + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode FilesDatabase::DbGetFiles(const std::vector &types, std::vector &info) +{ + bool result = SqliteHandle::GetInstance()->SearchFiles(types, info); + if (!result) { + LogError("Search files failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); } return CreateStatusCode(STATUS_CODE_OK); } \ No newline at end of file diff --git a/middleware/FilesManager/src/sqlite3/SqliteHandle.cpp b/middleware/FilesManager/src/sqlite3/SqliteHandle.cpp index be814ec..463abe8 100644 --- a/middleware/FilesManager/src/sqlite3/SqliteHandle.cpp +++ b/middleware/FilesManager/src/sqlite3/SqliteHandle.cpp @@ -18,17 +18,21 @@ #include "sqlite3.h" #include #include +#include #include #include +#include #include #include +#include #define FILES_TABLE "files" #define TABLE_KEY "key" #define FILE_PATH "path" -#define CREATE_TIME "time" +#define CREATE_TIME "create_time" #define FILE_TYPE "type" #define FILE_SIZE "size" #define FILE_STATUS "status" +#define FILE_DURATION "duration" #define FILE_STATUS_BE_RECORDING "recording" #define FILE_STATUS_COMPLETE_RECORD "complete_record" #define FILE_STATUS_SHOULD_BE_UPLOADED "should_be_uploaded" @@ -84,21 +88,21 @@ unsigned long int SqliteHandle::CreateFiles(const unsigned int &count) int rc = SQLITE_UNDEFINE; const char *sql = "INSERT INTO " FILES_TABLE " (" FILE_PATH ", " CREATE_TIME ", " FILE_TYPE ", " FILE_SIZE ", " FILE_STATUS - ") VALUES (\"\", 0, \"" FILE_TYPE_UNDEFINE "\", 0, \"" FILE_STATUS_BE_RECORDING "\");"; + ", " FILE_DURATION ") VALUES (\"\", 0, \"" FILE_TYPE_UNDEFINE "\", 0, \"" FILE_STATUS_BE_RECORDING "\", 0);"; for (unsigned int i = 0; i < count; ++i) { rc = sqlite3_exec(mDb, sql, nullptr, nullptr, &err_msg); if (SQLITE_OK != rc) { LogError("SQL error: %s\n", err_msg); sqlite3_free(err_msg); - return 0; + return UNDEFINE_SERIAL_NUMBER; } } auto last_rowid_lambda = [](void *last_rowid, int argc, char **argv, char **azColName) -> int { if (argc > 0) { - LogInfo("last_rowid = %s\n", argv[0]); + LogInfo("last_rowid key = %s\n", argv[0]); *(unsigned long int *)last_rowid = strtoul(argv[0], nullptr, 10); } - return 0; + return UNDEFINE_SERIAL_NUMBER; }; unsigned long int last_rowid = 0; const char *sql2 = "SELECT last_insert_rowid();"; @@ -106,33 +110,83 @@ unsigned long int SqliteHandle::CreateFiles(const unsigned int &count) if (SQLITE_OK != rc) { LogError("SQL error: %s\n", err_msg); sqlite3_free(err_msg); - return 0; + return UNDEFINE_SERIAL_NUMBER; } - return last_rowid - count; + constexpr int MINIMUM_AUTO_INCREMENT_PRIMARY_KEY = 1; + return last_rowid - count + MINIMUM_AUTO_INCREMENT_PRIMARY_KEY; } -bool SqliteHandle::SyncFile(const SyncFileInfo &info) +bool SqliteHandle::SyncFile(const SyncFileInfo &info, unsigned long &key) { - if (UNDEFINE_SERIAL_NUMBER == info.mSerialNumber) { - LogError("Serial number is undefine.\n"); - return false; - } - if (UpdateCreateTime(mDb, info.mSerialNumber, info.mCreateTime_s) == false) { - return false; - } - if (UpdateFileName(mDb, info.mSerialNumber, info.mFileName) == false) { - return false; - } - if (UpdateFileSize(mDb, info.mSerialNumber, info.mFileSize) == false) { - return false; - } - if (UpdateFileStatus(mDb, info.mSerialNumber, info.mStatus) == false) { - return false; - } - if (UpdateFileType(mDb, info.mSerialNumber, info.mType) == false) { + unsigned long serialNumber = info.mSerialNumber; + std::string fileName = info.mFileName; + if (UNDEFINE_SERIAL_NUMBER == serialNumber) { + LogWarning("Serial number is undefine, create new one.\n"); + serialNumber = CreateFiles(1); + key = serialNumber; + fileName = SqliteHandle::RenameFileToSync(fileName, key); + } + std::stringstream sqlStream; + std::string comma = " "; + sqlStream << "UPDATE " FILES_TABLE " SET "; + if (UNDEFINE_CREATE_TIME != info.mCreateTime_s) { + sqlStream << comma << CREATE_TIME " = '" << info.mCreateTime_s << "'"; + comma = ", "; + } + if (info.mFileName.empty() == false) { + sqlStream << comma << FILE_PATH " = '" << fileName << "'"; + comma = ", "; + } + if (UNDEFINE_FILE_SIZE != info.mFileSize) { + sqlStream << comma << FILE_SIZE " = '" << info.mFileSize << "'"; + comma = ", "; + } + if (FileStatus::END != info.mStatus) { + sqlStream << comma << FILE_STATUS " = '" << ConvertFileStatusToString(info.mStatus) << "'"; + comma = ", "; + } + if (FileCreateType::END != info.mType) { + sqlStream << comma << FILE_TYPE " = '" << ConvertFileTypeToString(info.mType) << "'"; + comma = ", "; + } + if (UNDEFINE_FILE_DURATION != info.mFileDuration) { + sqlStream << comma << FILE_DURATION " = '" << info.mFileDuration << "'"; + comma = ", "; + } + sqlStream << " WHERE " TABLE_KEY " = " << serialNumber << ";"; + LogInfo("Sql: %s\n", sqlStream.str().c_str()); + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + rc = sqlite3_exec(mDb, sqlStream.str().c_str(), nullptr, nullptr, &errMsg); + if (SQLITE_OK != rc) { + LogError("SQL error: %s\n", errMsg); + sqlite3_free(errMsg); return false; } + LogInfo("Sync file success.\n"); return true; } +bool SqliteHandle::SearchFiles(const std::vector &types, std::vector &info) +{ + std::string sqlAnd = " "; + // if (types.size() > 1) { + // sqlAnd = " AND "; + // } + constexpr FileCreateType END_MEANS_SEARCHING_ALL_FILES = FileCreateType::END; + std::stringstream sqlStream; + if (types.size() == 1 && types[0] == END_MEANS_SEARCHING_ALL_FILES) { + sqlStream << "SELECT * from " FILES_TABLE << ";"; + } + else { + sqlStream << "SELECT * from " FILES_TABLE " WHERE "; + for (auto &type : types) { + sqlStream << sqlAnd << FILE_TYPE " = '" << ConvertFileTypeToString(type) << "'"; + sqlAnd = " OR "; + } + sqlStream << ";"; + } + LogInfo("Sql: %s\n", sqlStream.str().c_str()); + return SearchFiles(mDb, sqlStream.str(), info); +} void SqliteHandle::DbInit(sqlite3 *db) { if (nullptr == db) { @@ -141,9 +195,9 @@ void SqliteHandle::DbInit(sqlite3 *db) } char *errMsg = nullptr; int rc = SQLITE_UNDEFINE; - const char *sql = - "CREATE TABLE IF NOT EXISTS " FILES_TABLE " (" TABLE_KEY " INTEGER PRIMARY KEY AUTOINCREMENT, " FILE_PATH - " TEXT, " CREATE_TIME " INTEGER," FILE_TYPE " TEXT, " FILE_SIZE " INTEGER, " FILE_STATUS " TEXT);"; + const char *sql = "CREATE TABLE IF NOT EXISTS " FILES_TABLE " (" TABLE_KEY + " INTEGER PRIMARY KEY AUTOINCREMENT, " FILE_PATH " TEXT, " CREATE_TIME " INTEGER," FILE_TYPE + " TEXT, " FILE_SIZE " INTEGER, " FILE_STATUS " TEXT," FILE_DURATION " INTEGER);"; rc = sqlite3_exec(db, sql, nullptr, nullptr, &errMsg); if (SQLITE_OK != rc) { LogError("Sql: %s, errMsg: %s\n", sql, errMsg); @@ -229,7 +283,7 @@ bool SqliteHandle::UpdateFileType(sqlite3 *db, const unsigned long &key, const F int rc = SQLITE_UNDEFINE; if (FileCreateType::END != type) { std::stringstream sqlStream; - sqlStream << "UPDATE " FILES_TABLE " SET " FILE_TYPE " = '" << ConvertFileType(type) + sqlStream << "UPDATE " FILES_TABLE " SET " FILE_TYPE " = '" << ConvertFileTypeToString(type) << "' WHERE " TABLE_KEY " = " << key << ";"; LogInfo("Sql: %s\n", sqlStream.str().c_str()); rc = sqlite3_exec(db, sqlStream.str().c_str(), nullptr, nullptr, &errMsg); @@ -241,22 +295,108 @@ bool SqliteHandle::UpdateFileType(sqlite3 *db, const unsigned long &key, const F } return true; } -std::string SqliteHandle::ConvertFileType(const FileCreateType &type) +bool SqliteHandle::UpdateFileDuration(sqlite3 *db, const unsigned long &key, const unsigned int &duration) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return false; + } + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + std::stringstream sqlStream; + sqlStream << "UPDATE " FILES_TABLE " SET " FILE_DURATION " = '" << duration << "' WHERE " TABLE_KEY " = " << key + << ";"; + LogInfo("Sql: %s\n", sqlStream.str().c_str()); + rc = sqlite3_exec(db, sqlStream.str().c_str(), nullptr, nullptr, &errMsg); + if (SQLITE_OK != rc) { + LogError("SQL error: %s\n", errMsg); + sqlite3_free(errMsg); + return false; + } + return true; +} +bool SqliteHandle::SearchFiles(sqlite3 *db, const std::string &sql, std::vector &info) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return false; + } + auto dbCallback = [](void *data, int argc, char **argv, char **azColName) -> int { + /********************** Do not delete this code to avoid debugging. *********************/ + int i = 0; + fprintf(stderr, "%s: ", "searchFiles"); + for (i = 0; i < argc; i++) { + printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); + } + printf("\n"); + /********************** Do not delete this code to avoid debugging. *********************/ + /** + * @brief The magic number in the argv variable refers to the SQL statement that creates the form, which is as + * follows: + * CREATE TABLE IF NOT EXISTS files (key INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT, create_time INTEGER,type + * 0 1 2 3 + * TEXT, size INTEGER, status TEXT, duration INTEGER); + * 4 5 6 + */ + constexpr int TABLE_MEMBER_NUM = 7; + if (TABLE_MEMBER_NUM != argc) { + LogError("Something wrong happened, argc = %d.\n", argc); + return 0; + } + std::vector *info = (std::vector *)data; + const unsigned long serialNumber = std::stoul(argv[0]); + const std::string fileName = argv[1]; + const unsigned int fileSize = std::stoi(argv[4]); + const unsigned int fileDuration = std::stoi(argv[6]); + const time_t createTime_s = std::stol(argv[2]); + const std::string typeString(argv[3]); + const FileCreateType type = SqliteHandle::ConvertStringToFileType(typeString); + const std::string statusString(argv[5]); + const FileStatus status = SqliteHandle::ConvertStringToFileStatus(statusString); + SyncFileInfo fileInfo(serialNumber, fileName, fileSize, fileDuration, createTime_s, type, status); + (*info).push_back(fileInfo); + return 0; + }; + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + rc = sqlite3_exec(db, sql.c_str(), dbCallback, &info, &errMsg); + if (SQLITE_OK != rc) { + LogError("SQL error: %s\n", errMsg); + sqlite3_free(errMsg); + return false; + } + return true; +} +std::string SqliteHandle::ConvertFileTypeToString(const FileCreateType &type) { switch (type) { case FileCreateType::PIR: - return "PIR"; + return FIEL_TYPE_PIR; case FileCreateType::MANUAL_TEST: - return "MANUAL_TEST"; + return FIEL_TYPE_MANUAL_TEST; case FileCreateType::MANUAL_PHONE: - return "MANUAL_PHONE"; + return FIEL_TYPE_MANUAL_PHONE; case FileCreateType::TIMED: - return "TIMED"; + return FIEL_TYPE_TIMED; case FileCreateType::END: default: - return "undefine"; + return FILE_TYPE_UNDEFINE; } } +FileCreateType SqliteHandle::ConvertStringToFileType(const std::string &type) +{ + std::map fileTypeMap; + fileTypeMap[FIEL_TYPE_PIR] = FileCreateType::PIR; + fileTypeMap[FIEL_TYPE_MANUAL_TEST] = FileCreateType::MANUAL_TEST; + fileTypeMap[FIEL_TYPE_MANUAL_PHONE] = FileCreateType::MANUAL_PHONE; + fileTypeMap[FIEL_TYPE_TIMED] = FileCreateType::TIMED; + fileTypeMap[FILE_TYPE_UNDEFINE] = FileCreateType::END; + auto it = fileTypeMap.find(type); + if (it != fileTypeMap.end()) { + return it->second; + } + return FileCreateType::END; +} bool SqliteHandle::UpdateFileStatus(sqlite3 *db, const unsigned long &key, const FileStatus &status) { if (nullptr == db) { @@ -267,7 +407,7 @@ bool SqliteHandle::UpdateFileStatus(sqlite3 *db, const unsigned long &key, const int rc = SQLITE_UNDEFINE; if (FileStatus::END != status) { std::stringstream sqlStream; - sqlStream << "UPDATE " FILES_TABLE " SET " FILE_STATUS " = '" << ConvertFileStatus(status) + sqlStream << "UPDATE " FILES_TABLE " SET " FILE_STATUS " = '" << ConvertFileStatusToString(status) << "' WHERE " TABLE_KEY " = " << key << ";"; LogInfo("Sql: %s\n", sqlStream.str().c_str()); rc = sqlite3_exec(db, sqlStream.str().c_str(), nullptr, nullptr, &errMsg); @@ -279,7 +419,7 @@ bool SqliteHandle::UpdateFileStatus(sqlite3 *db, const unsigned long &key, const } return true; } -std::string SqliteHandle::ConvertFileStatus(const FileStatus &status) +std::string SqliteHandle::ConvertFileStatusToString(const FileStatus &status) { switch (status) { case FileStatus::RECORDING: @@ -296,4 +436,30 @@ std::string SqliteHandle::ConvertFileStatus(const FileStatus &status) default: return "undefine"; } +} +FileStatus SqliteHandle::ConvertStringToFileStatus(const std::string &status) +{ + std::map fileStatusMap; + fileStatusMap[FILE_STATUS_BE_RECORDING] = FileStatus::RECORDING; + fileStatusMap[FILE_STATUS_COMPLETE_RECORD] = FileStatus::FINISHED_RECORD; + fileStatusMap[FILE_STATUS_SHOULD_BE_UPLOADED] = FileStatus::SHOULD_BE_UPLOAD; + fileStatusMap[FILE_STATUS_UPLOADING] = FileStatus::UPLOADING; + fileStatusMap[FILE_STATUS_UPLOADED] = FileStatus::UPLOADED; + auto it = fileStatusMap.find(status); + if (it != fileStatusMap.end()) { + return it->second; + } + return FileStatus::END; +} +std::string SqliteHandle::RenameFileToSync(const std::string &sourceFile, unsigned long &key) +{ + std::string original = sourceFile; + const std::string pattern = "???"; + std::string replacement = std::to_string(key); + size_t pos = 0; + while ((pos = original.find(pattern, pos)) != std::string::npos) { + original.replace(pos, pattern.length(), replacement); + pos += replacement.length(); + } + return original; } \ No newline at end of file diff --git a/middleware/FilesManager/src/sqlite3/SqliteHandle.h b/middleware/FilesManager/src/sqlite3/SqliteHandle.h index b3df206..6650a88 100644 --- a/middleware/FilesManager/src/sqlite3/SqliteHandle.h +++ b/middleware/FilesManager/src/sqlite3/SqliteHandle.h @@ -16,9 +16,11 @@ #define SQLITE_HANDLE_H #include "IFilesManager.h" #include "sqlite3.h" +#include #include #include #include +#include constexpr int SQLITE_UNDEFINE = -1; class SqliteHandle { @@ -28,8 +30,23 @@ public: static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); void Init(const std::string &dbFileName); void UnInit(void); + /** + * @brief Request to create a file and return the minimum value of the file's unique serial number. + * + * @param count + * @return unsigned long int If invalid, returns UNDEFINE_SERIAL_NUMBER. + */ unsigned long int CreateFiles(const unsigned int &count); - bool SyncFile(const SyncFileInfo &info); + /** + * @brief Synchronize file information. When the synchronized file number is invalid, a new number will be created + * and assigned to the given parameter "key". + * @param info + * @param key [out] If the key value does not exist, it is created and output as a parameter. + * @return true + * @return false + */ + bool SyncFile(const SyncFileInfo &info, unsigned long &key); + bool SearchFiles(const std::vector &types, std::vector &info); bool Remove(); bool Modified(); bool Serach(); @@ -40,9 +57,16 @@ private: static bool UpdateFileName(sqlite3 *db, const unsigned long &key, const std::string &fileName); static bool UpdateFileSize(sqlite3 *db, const unsigned long &key, const unsigned long &fileSize); static bool UpdateFileType(sqlite3 *db, const unsigned long &key, const FileCreateType &type); - static std::string ConvertFileType(const FileCreateType &type); + static bool UpdateFileDuration(sqlite3 *db, const unsigned long &key, const unsigned int &duration); + static bool SearchFiles(sqlite3 *db, const std::string &sql, std::vector &info); + static std::string ConvertFileTypeToString(const FileCreateType &type); + static FileCreateType ConvertStringToFileType(const std::string &type); static bool UpdateFileStatus(sqlite3 *db, const unsigned long &key, const FileStatus &status); - static std::string ConvertFileStatus(const FileStatus &status); + static std::string ConvertFileStatusToString(const FileStatus &status); + static FileStatus ConvertStringToFileStatus(const std::string &status); + +public: + static std::string RenameFileToSync(const std::string &sourceFile, unsigned long &key); private: sqlite3 *mDb; diff --git a/middleware/IpcConfig/include/IIpcConfig.h b/middleware/IpcConfig/include/IIpcConfig.h index 57b545e..5525719 100644 --- a/middleware/IpcConfig/include/IIpcConfig.h +++ b/middleware/IpcConfig/include/IIpcConfig.h @@ -46,6 +46,7 @@ enum class IpcConfigKey enum class WorkMode { MODE_PIC = 0, + MODE_VIDEO, MODE_PIC_VIDEO, END, }; diff --git a/middleware/McuManager/include/IMcuManager.h b/middleware/McuManager/include/IMcuManager.h index 6fc4ae4..5659902 100644 --- a/middleware/McuManager/include/IMcuManager.h +++ b/middleware/McuManager/include/IMcuManager.h @@ -16,6 +16,7 @@ #define I_MCU_MANAGER_H #include "StatusCode.h" #include +constexpr unsigned short CLOSE_WATCH_DOG = 0; bool CreateMcuManager(void); bool DestroyMcuManager(void); enum class IpcMission @@ -153,6 +154,15 @@ public: virtual const StatusCode FeedWatchDog(std::shared_ptr &ask); virtual const StatusCode SetFeedingCycleForWatchDog(std::shared_ptr &ask, const unsigned char &hour, const unsigned char &min, const unsigned char &second); + /** + * @brief Set the dog feeding cycle. When the dog feeding cycle is set to 0, it means that the dog feeding is turned + * off. + * @param ask + * @param feedCycle_s The dog feeding cycle is in seconds. Using CLOSE_WATCH_DOG(0) means close watch dog. + * @return const StatusCode + */ + virtual const StatusCode SetFeedingCycleForWatchDog(std::shared_ptr &ask, + const unsigned short &feedCycle_s); virtual const StatusCode SetDateTime(std::shared_ptr &ask, const McuAskDateTime &value); virtual const StatusCode SetPirSensitivity(std::shared_ptr &ask, const unsigned char &sensitivity); virtual const StatusCode ContorlInfraredLight(std::shared_ptr &ask, const ControlLight &control); diff --git a/middleware/McuManager/src/IMcuManager.cpp b/middleware/McuManager/src/IMcuManager.cpp index 1b77778..112c5e9 100644 --- a/middleware/McuManager/src/IMcuManager.cpp +++ b/middleware/McuManager/src/IMcuManager.cpp @@ -83,50 +83,68 @@ std::shared_ptr &IMcuManager::GetInstance(std::shared_ptr &monitor) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::GetIpcMission(std::shared_ptr &ask) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::CutOffPowerSupply(std::shared_ptr &ask) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::FeedWatchDog(std::shared_ptr &ask) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::SetFeedingCycleForWatchDog(std::shared_ptr &ask, const unsigned char &hour, const unsigned char &min, const unsigned char &second) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +const StatusCode IMcuManager::SetFeedingCycleForWatchDog(std::shared_ptr &ask, + const unsigned short &feedCycle_s) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::SetDateTime(std::shared_ptr &ask, const McuAskDateTime &value) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::SetPirSensitivity(std::shared_ptr &ask, const unsigned char &sensitivity) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::ContorlInfraredLight(std::shared_ptr &ask, const ControlLight &control) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const StatusCode IMcuManager::GetPhotosensitivityValue(std::shared_ptr &ask) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const char *IMcuManager::PrintIpcMissionString(const IpcMission &mission) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); return "VIRTUAL FUNCTION"; } \ No newline at end of file diff --git a/middleware/McuManager/src/McuManagerImpl.cpp b/middleware/McuManager/src/McuManagerImpl.cpp index 71ea14d..1daeb96 100644 --- a/middleware/McuManager/src/McuManagerImpl.cpp +++ b/middleware/McuManager/src/McuManagerImpl.cpp @@ -121,6 +121,20 @@ const StatusCode McuManagerImpl::SetFeedingCycleForWatchDog(std::shared_ptr context = std::make_shared>>(ask); return McuProtocol::SetFeedingCycleForWatchDog(hour, min, second, context); } +const StatusCode McuManagerImpl::SetFeedingCycleForWatchDog(std::shared_ptr &ask, + const unsigned short &feedCycle_s) +{ + if (CLOSE_WATCH_DOG == feedCycle_s) { + /** + * @brief When the dog feeding is turned off, you need to stop feeding the dog. Continuing to feed the dog will + * restore the watchdog. + */ + mWatchDogRuning = false; + mCv.notify_one(); + } + std::shared_ptr context = std::make_shared>>(ask); + return McuProtocol::SetFeedingCycleForWatchDog(feedCycle_s, context); +} const StatusCode McuManagerImpl::SetDateTime(std::shared_ptr &ask, const McuAskDateTime &value) { std::shared_ptr context = std::make_shared>>(ask); diff --git a/middleware/McuManager/src/McuManagerImpl.h b/middleware/McuManager/src/McuManagerImpl.h index 0e012f7..10c891f 100644 --- a/middleware/McuManager/src/McuManagerImpl.h +++ b/middleware/McuManager/src/McuManagerImpl.h @@ -48,6 +48,8 @@ public: const StatusCode FeedWatchDog(std::shared_ptr &ask) override; const StatusCode SetFeedingCycleForWatchDog(std::shared_ptr &ask, const unsigned char &hour, const unsigned char &min, const unsigned char &second) override; + const StatusCode SetFeedingCycleForWatchDog(std::shared_ptr &ask, + const unsigned short &feedCycle_s) override; const StatusCode SetDateTime(std::shared_ptr &ask, const McuAskDateTime &value) override; const StatusCode SetPirSensitivity(std::shared_ptr &ask, const unsigned char &sensitivity) override; const StatusCode ContorlInfraredLight(std::shared_ptr &ask, const ControlLight &control) override; diff --git a/middleware/MediaManager/include/IMediaManager.h b/middleware/MediaManager/include/IMediaManager.h index fb39bcb..966d1b7 100644 --- a/middleware/MediaManager/include/IMediaManager.h +++ b/middleware/MediaManager/include/IMediaManager.h @@ -41,14 +41,16 @@ enum class MediaTaskType }; typedef struct media_report_event { - media_report_event(const std::string &fileName, const MediaChannel &mediaChannedl); + media_report_event(const std::string &fileName, const MediaChannel &mediaChannedl, const unsigned int &duration_ms); const std::string mFileName; const MediaChannel mMediaChannedl; + const unsigned int mDuration_ms; } MediaReportEvent; typedef struct media_task_response { - media_task_response(const std::string &fileName); + media_task_response(const std::string &fileName, const unsigned int &duration_ms); const std::string mFileName; + const unsigned int mDuration_ms; } MediaTaskResponse; class VMediaTask { @@ -57,6 +59,8 @@ public: virtual ~VMediaTask() = default; virtual const MediaTaskType GetTaskType(void); virtual std::string GetTargetNameForSaving(void); + virtual std::string GetThumbnailNameForSaving(const std::string &targetName); + virtual int GetVideoDuration_ms(void); virtual void Response(const std::vector &response); virtual bool IsTaskFinished(void); virtual const signed int GetIsNight(void); @@ -89,5 +93,8 @@ public: virtual StatusCode UnInit(void); virtual StatusCode SetMediaMonitor(std::shared_ptr &monitor); virtual StatusCode GetMediaChannel(const MediaChannel &channel, std::shared_ptr &handle); + +public: + static const char *PrintfTaskType(const MediaTaskType &type); }; #endif \ No newline at end of file diff --git a/middleware/MediaManager/src/IMediaManager.cpp b/middleware/MediaManager/src/IMediaManager.cpp index 62fcd00..4f17c93 100644 --- a/middleware/MediaManager/src/IMediaManager.cpp +++ b/middleware/MediaManager/src/IMediaManager.cpp @@ -18,11 +18,13 @@ #include #include #include -media_report_event::media_report_event(const std::string &fileName, const MediaChannel &mediaChannedl) - : mFileName(fileName), mMediaChannedl(mediaChannedl) +media_report_event::media_report_event(const std::string &fileName, const MediaChannel &mediaChannedl, + const unsigned int &duration_ms) + : mFileName(fileName), mMediaChannedl(mediaChannedl), mDuration_ms(duration_ms) { } -media_task_response::media_task_response(const std::string &fileName) : mFileName(fileName) +media_task_response::media_task_response(const std::string &fileName, const unsigned int &duration_ms) + : mFileName(fileName), mDuration_ms(duration_ms) { } const MediaTaskType VMediaTask::GetTaskType(void) @@ -34,6 +36,16 @@ std::string VMediaTask::GetTargetNameForSaving(void) const std::string fileName = "Undefined"; return fileName; } +std::string VMediaTask::GetThumbnailNameForSaving(const std::string &targetName) +{ + const std::string fileName = "Undefined"; + return fileName; +} +int VMediaTask::GetVideoDuration_ms(void) +{ + constexpr int DURATION_MS = 0; + return DURATION_MS; +} void VMediaTask::Response(const std::vector &response) { } @@ -127,4 +139,19 @@ StatusCode IMediaManager::GetMediaChannel(const MediaChannel &channel, std::shar { LogWarning("STATUS_CODE_VIRTUAL_FUNCTION"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +const char *IMediaManager::PrintfTaskType(const MediaTaskType &type) +{ + switch (type) { + case MediaTaskType::TAKE_PICTURE: + return "TAKE_PICTURE"; + case MediaTaskType::TAKE_VIDEO: + return "TAKE_VIDEO"; + case MediaTaskType::TAKE_PICTURE_AND_VIDEO: + return "TAKE_PICTURE_AND_VIDEO"; + + default: + LogWarning("unknow MediaTaskType.\n"); + return "unknow MediaTaskType"; + } } \ No newline at end of file diff --git a/middleware/MediaManager/src/MediaHandle.cpp b/middleware/MediaManager/src/MediaHandle.cpp index 8da4a6a..4793914 100644 --- a/middleware/MediaManager/src/MediaHandle.cpp +++ b/middleware/MediaManager/src/MediaHandle.cpp @@ -16,7 +16,7 @@ #include "IHalCpp.h" #include "ILog.h" #include "IMediaManager.h" -#include "RecordMp4.h" +#include "MediaManagerMakePtr.h" #include "StatusCode.h" #include #include @@ -26,26 +26,28 @@ #include #include #include -one_frame_stream::one_frame_stream() : mType(FrameType::END), mData(nullptr), mLength(0) +constexpr int MEDIA_TASK_NOT_START = 0; +one_frame_stream::one_frame_stream() : mType(FrameType::END), mData(nullptr), mLength(0), mTimeStamp_us(0) { } one_frame_stream::~one_frame_stream() { } MediaHandle::MediaHandle(const MediaChannel &mediaChannel, const std::shared_ptr &cameraHal) - : mMediaChannel(mediaChannel), mCameraHal(cameraHal), mTaskRuning(false) + : mMediaChannel(mediaChannel), mCameraHal(cameraHal), mTaskRuning(false), + mFirstFrameTimeStamp_us(MEDIA_TASK_NOT_START) { } void MediaHandle::Init(void) { - if (mCameraHal == nullptr) { - LogError("CameraHal is null.\n"); - return; - } - auto audioFunc = std::bind(&MediaHandle::GetAudioStreamCallback, this, _1, _2, _3); - mCameraHal->SetAudioStreamCallback(audioFunc); - auto videoFunc = std::bind(&MediaHandle::GetVideoStreamCallback, this, _1, _2, _3); - mCameraHal->SetVideoStreamCallback(videoFunc); + // if (mCameraHal == nullptr) { + // LogError("CameraHal is null.\n"); + // return; + // } + // auto audioFunc = std::bind(&MediaHandle::GetAudioStreamCallback, this, _1, _2, _3); + // mCameraHal->SetAudioStreamCallback(audioFunc); + // auto videoFunc = std::bind(&MediaHandle::GetVideoStreamCallback, this, _1, _2, _3); + // mCameraHal->SetVideoStreamCallback(videoFunc); } void MediaHandle::UnInit(void) { @@ -65,6 +67,7 @@ void MediaHandle::UnInit(void) */ mCameraHal->SetAudioStreamCallback(nullptr); mCameraHal->SetVideoStreamCallback(nullptr); + mCameraHal->SetJpegEncodeCallback(nullptr); } } StatusCode MediaHandle::ExecuteTask(std::shared_ptr &task) @@ -78,7 +81,8 @@ StatusCode MediaHandle::ExecuteTask(std::shared_ptr &task) return CreateStatusCode(STATUS_CODE_NOT_OK); } } - mStreamHandle = std::make_shared(task); + // mStreamHandle = std::make_shared(task); + mStreamHandle = MediaManagerMakePtr::GetInstance()->CreateMediaHandle(task); if (nullptr == mStreamHandle) { LogError("Create stream handle failed.\n"); mStreamHandle.reset(); @@ -91,7 +95,7 @@ StatusCode MediaHandle::ExecuteTask(std::shared_ptr &task) } CameraTaskType taskType = TaskTypeConvert(task->GetTaskType()); CameraTaskParam data(taskType); - auto code = mCameraHal->StartSingleTask(data); + auto code = StartMediaTask(data); if (IsCodeOK(code)) { mCurrentTask = task; StartTaskTimer(); @@ -116,41 +120,46 @@ void MediaHandle::StartTaskTimer(void) LogInfo("StartTaskTimer start.\n"); media->TaskTimer(); }; + if (mTaskTimerThread.joinable()) { + mTaskTimerThread.join(); + } std::shared_ptr media = shared_from_this(); mTaskTimerThread = std::thread(taskTimerThread, media); } void MediaHandle::TaskTimer(void) { - constexpr int TASK_TIMER = 1000 * 10; + constexpr int TASK_TIME_OUT = 1000 * 60; mTaskRuning = true; while (mTaskRuning) { std::unique_lock lock(mMutex); - mCvTaskHandle.wait_for(lock, std::chrono::milliseconds(TASK_TIMER), [&] { + mCvTaskHandle.wait_for(lock, std::chrono::milliseconds(TASK_TIME_OUT), [&] { return !mTaskRuning; }); /** * @brief If the recording time is over, you need to stop the recording timer here. */ mTaskRuning = false; + mStreamHandle->StopHandleStream(); + std::vector files; + mStreamHandle->GetAllFiles(files); + mStreamHandle->UnInit(); + if (mCameraHal) { + mCameraHal->StopTask(); + ClearFrameList(); + } + // mMutex.lock(); + mStreamHandle.reset(); + mFirstFrameTimeStamp_us = MEDIA_TASK_NOT_START; + auto runingTask = mCurrentTask.lock(); + if (mCurrentTask.expired()) { + LogWarning("mCurrentTask is expired.\n"); + return; + } + LogInfo("Task finished response to application.\n"); + runingTask->Response(files); + mCurrentTask.reset(); + // mMutex.unlock(); } - mStreamHandle->StopHandleStream(); - std::vector files; - mStreamHandle->GetAllFiles(files); - mStreamHandle->UnInit(); - if (mCameraHal) { - mCameraHal->StopTask(); - } - mStreamHandle.reset(); - mMutex.lock(); - auto runingTask = mCurrentTask.lock(); - if (mCurrentTask.expired()) { - LogWarning("mCurrentTask is expired.\n"); - return; - } - LogInfo("Task finished response to application.\n"); - runingTask->Response(files); - mCurrentTask.reset(); - mMutex.unlock(); } void MediaHandle::StartFrameHandle(void) { @@ -158,6 +167,9 @@ void MediaHandle::StartFrameHandle(void) LogInfo("StartFrameHandle start.\n"); media->FrameHandle(); }; + if (mFrameHandleThread.joinable()) { + mFrameHandleThread.join(); + } std::shared_ptr media = shared_from_this(); mFrameHandleThread = std::thread(taskTimerThread, media); } @@ -170,57 +182,158 @@ void MediaHandle::FrameHandle(void) mCvFrameHandle.wait_for(lock, std::chrono::milliseconds(TASK_TIMER), [&] { return !mTaskRuning || !mFrameList.empty(); }); + /** + * @brief + * NOTE: From here on the code will be re-locked. + */ + if (!mStreamHandle) { + LogWarning("mStreamHandle is nullptr, will not handle frame.\n"); + break; + } if (mFrameList.size() > 0) { HandleListFrame(); } } + LogInfo("FrameHandle exit.\n"); } -void MediaHandle::HandleListFrame(void) +/** + * @brief Processing audio and video streams may take a long time. This is not allowed to block the MediaHandle thread, + * which will cause it to be unable to add audio and video stream data. + */ +void inline MediaHandle::HandleListFrame(void) { int leftFrameCount = -1; do { + mFrameMutex.lock(); OneFrameStream &frontFrame = mFrameList.front(); - OneFrameStream handleIt; - handleIt.mData = frontFrame.mData; - handleIt.mLength = frontFrame.mLength; - handleIt.mType = frontFrame.mType; - mFrameList.pop_front(); - if (FrameType::VIDEO == handleIt.mType) { - mStreamHandle->GetVideoStream(handleIt.mData, handleIt.mLength, 0); + mFrameMutex.unlock(); + if (FrameType::VIDEO == frontFrame.mType) { + mStreamHandle->GetVideoStream(frontFrame.mData, frontFrame.mLength, frontFrame.mTimeStamp_us); } - if (FrameType::AUDIO == handleIt.mType) { - mStreamHandle->GetAudioStream(handleIt.mData, handleIt.mLength, 0); + else if (FrameType::AUDIO == frontFrame.mType) { + mStreamHandle->GetAudioStream(frontFrame.mData, frontFrame.mLength, frontFrame.mTimeStamp_us); } - free(handleIt.mData); - handleIt.mData = nullptr; + else if (FrameType::JPEG == frontFrame.mType) { + mStreamHandle->GetJpegData(frontFrame.mData, frontFrame.mLength, frontFrame.mTimeStamp_us); + } + free(frontFrame.mData); + frontFrame.mData = nullptr; + // mFrameList.pop_front(); + DeleteFrame(frontFrame); leftFrameCount = mFrameList.size(); } while (leftFrameCount > 0); + if (mStreamHandle->HandleFinished()) { + /** + * @brief A single media stream processing task is completed. The media stream processing thread and the media + * task waiting thread are released. + */ + mTaskRuning = false; + mCvTaskHandle.notify_one(); + mCvFrameHandle.notify_one(); + } } CameraTaskType MediaHandle::TaskTypeConvert(const MediaTaskType &type) { - return CameraTaskType::END; + switch (type) { + case MediaTaskType::TAKE_PICTURE: + return CameraTaskType::PICTURE; + case MediaTaskType::TAKE_VIDEO: + return CameraTaskType::VIDEO; + case MediaTaskType::TAKE_PICTURE_AND_VIDEO: + return CameraTaskType::PICTURE_AND_VIDEO; + + default: + LogError("TaskTypeConvert error.\n"); + return CameraTaskType::END; + } } void MediaHandle::GetVideoStreamCallback(const void *stream, const int &length, const unsigned long long &timeStamp) { - std::unique_lock lock(mMutex); - // mStreamHandle->GetVideoStream(stream, length, timeStamp); - OneFrameStream addFrame; - addFrame.mData = malloc(length); - addFrame.mLength = length; - memcpy(addFrame.mData, stream, length); - addFrame.mType = FrameType::VIDEO; - mFrameList.push_back(addFrame); - mCvFrameHandle.notify_one(); + GetAVStream(FrameType::VIDEO, stream, length, timeStamp); } void MediaHandle::GetAudioStreamCallback(const void *stream, const int &length, const unsigned long long &timeStamp) { - std::unique_lock lock(mMutex); - // mStreamHandle->GetAudioStream(stream, length, timeStamp); + GetAVStream(FrameType::AUDIO, stream, length, timeStamp); +} +void MediaHandle::GetJpegDataCallback(const void *stream, const int &length, const unsigned long long &timeStamp) +{ + /** + * @brief If it is a screenshot, each task only needs to process one piece of image data. + * + */ + if (MEDIA_TASK_NOT_START == mFirstFrameTimeStamp_us) { + GetAVStream(FrameType::JPEG, stream, length, timeStamp); + } +} +void MediaHandle::GetAVStream(const FrameType &type, const void *stream, const int &length, + const unsigned long long &timeStamp_us) +{ + std::unique_lock lock(mFrameMutex); + if (MEDIA_TASK_NOT_START == mFirstFrameTimeStamp_us) { + mFirstFrameTimeStamp_us = timeStamp_us; + } OneFrameStream addFrame; addFrame.mData = malloc(length); addFrame.mLength = length; memcpy(addFrame.mData, stream, length); - addFrame.mType = FrameType::AUDIO; + addFrame.mType = type; + addFrame.mTimeStamp_us = timeStamp_us - mFirstFrameTimeStamp_us; mFrameList.push_back(addFrame); mCvFrameHandle.notify_one(); +} +void MediaHandle::ClearFrameList(void) +{ + std::unique_lock lock(mFrameMutex); + for (auto &frame : mFrameList) { + if (frame.mData) { + free(frame.mData); + frame.mData = nullptr; + } + } + mFrameList.clear(); +} +void MediaHandle::DeleteFrame(const OneFrameStream &frame) +{ + constexpr bool REMOVE_THE_FRAME = true; + constexpr bool KEEP_THE_FRAME = false; + std::lock_guard locker(mFrameMutex); + auto searchMcuAsk = [frame](OneFrameStream &frameList) -> bool { + if (frame.mTimeStamp_us == frameList.mTimeStamp_us) { + if (frameList.mData) { + free(frameList.mData); + frameList.mData = nullptr; + } + return REMOVE_THE_FRAME; + } + return KEEP_THE_FRAME; + }; + mFrameList.remove_if(searchMcuAsk); +} +StatusCode MediaHandle::StartMediaTask(const CameraTaskParam ¶m) +{ + if (mCameraHal == nullptr) { + LogError("CameraHal is null.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + mCameraHal->SetAudioStreamCallback(nullptr); + mCameraHal->SetVideoStreamCallback(nullptr); + mCameraHal->SetJpegEncodeCallback(nullptr); + auto audioFunc = std::bind(&MediaHandle::GetAudioStreamCallback, this, _1, _2, _3); + auto videoFunc = std::bind(&MediaHandle::GetVideoStreamCallback, this, _1, _2, _3); + auto jpegFunc = std::bind(&MediaHandle::GetJpegDataCallback, this, _1, _2, _3); + switch (param.mTaskType) { + case CameraTaskType::PICTURE: + mCameraHal->SetJpegEncodeCallback(jpegFunc); + break; + case CameraTaskType::VIDEO: + mCameraHal->SetAudioStreamCallback(audioFunc); + mCameraHal->SetVideoStreamCallback(videoFunc); + break; + case CameraTaskType::PICTURE_AND_VIDEO: + break; + + default: + break; + } + return mCameraHal->StartSingleTask(param); } \ No newline at end of file diff --git a/middleware/MediaManager/src/MediaHandle.h b/middleware/MediaManager/src/MediaHandle.h index 04ec1fb..40be51b 100644 --- a/middleware/MediaManager/src/MediaHandle.h +++ b/middleware/MediaManager/src/MediaHandle.h @@ -28,6 +28,7 @@ enum class FrameType { VIDEO, AUDIO, + JPEG, END }; typedef struct one_frame_stream @@ -37,6 +38,7 @@ typedef struct one_frame_stream FrameType mType; void *mData; int mLength; + unsigned long long mTimeStamp_us; } OneFrameStream; class MediaHandle : public VMediaHandle, public std::enable_shared_from_this { @@ -60,9 +62,24 @@ private: CameraTaskType TaskTypeConvert(const MediaTaskType &type); void GetVideoStreamCallback(const void *stream, const int &length, const unsigned long long &timeStamp); void GetAudioStreamCallback(const void *stream, const int &length, const unsigned long long &timeStamp); + void GetJpegDataCallback(const void *stream, const int &length, const unsigned long long &timeStamp); + /** + * @brief + * + * @param type The type of media stream (video/audio). + * @param stream Data pointer of the media stream. + * @param length The length of the media stream data. + * @param timeStamp_us The unit of timestamp must be us. + */ + void GetAVStream(const FrameType &type, const void *stream, const int &length, + const unsigned long long &timeStamp_us); + void ClearFrameList(void); + void DeleteFrame(const OneFrameStream &frame); + StatusCode StartMediaTask(const CameraTaskParam ¶m); private: std::mutex mMutex; + std::mutex mFrameMutex; std::condition_variable mCvTaskHandle; std::condition_variable mCvFrameHandle; const MediaChannel &mMediaChannel; @@ -73,5 +90,6 @@ private: std::thread mTaskTimerThread; std::thread mFrameHandleThread; std::list mFrameList; + unsigned long long mFirstFrameTimeStamp_us; }; #endif \ No newline at end of file diff --git a/middleware/MediaManager/src/MediaManagerImpl.cpp b/middleware/MediaManager/src/MediaManagerImpl.cpp index d58751a..5203f6c 100644 --- a/middleware/MediaManager/src/MediaManagerImpl.cpp +++ b/middleware/MediaManager/src/MediaManagerImpl.cpp @@ -51,7 +51,7 @@ StatusCode MediaManagerImpl::SetMediaMonitor(std::shared_ptr &mon void MediaManagerImpl::ReportEvent(const CameraReportEvent &event) { LogInfo("ReportEvent. file: %s.\n", event.mFileName.c_str()); - MediaReportEvent reprot(event.mFileName, static_cast(event.mCameraType)); + MediaReportEvent reprot(event.mFileName, static_cast(event.mCameraType), event.mDuration_ms); auto monitor = mMediaMonitor.lock(); if (mMediaMonitor.expired()) { LogWarning("MediaMonitor is expired.\n"); diff --git a/middleware/MediaManager/src/MediaManagerMakePtr.cpp b/middleware/MediaManager/src/MediaManagerMakePtr.cpp index 30df0cb..5f2ebf5 100644 --- a/middleware/MediaManager/src/MediaManagerMakePtr.cpp +++ b/middleware/MediaManager/src/MediaManagerMakePtr.cpp @@ -16,7 +16,10 @@ #include "ILog.h" #include "IMediaManager.h" #include "MediaManagerImpl.h" +#include "RecordMp4.h" #include "StatusCode.h" +#include "TakePicture.h" +#include "VStreamHandle.h" #include bool CreateMediaManagerModule(void) { @@ -54,4 +57,25 @@ const StatusCode MediaManagerMakePtr::CreateMediaManagerModule(std::shared_ptr(); impl = tmp; return CreateStatusCode(STATUS_CODE_OK); +} +std::shared_ptr MediaManagerMakePtr::CreateMediaHandle(std::shared_ptr &task) +{ + const MediaTaskType type = task->GetTaskType(); + std::shared_ptr stream; + switch (type) { + case MediaTaskType::TAKE_PICTURE: + stream = std::make_shared(task); + break; + case MediaTaskType::TAKE_VIDEO: + stream = std::make_shared(task); + break; + case MediaTaskType::TAKE_PICTURE_AND_VIDEO: + /* code */ + break; + + default: + LogWarning("unsupport type.\n"); + break; + } + return stream; } \ No newline at end of file diff --git a/middleware/MediaManager/src/MediaManagerMakePtr.h b/middleware/MediaManager/src/MediaManagerMakePtr.h index 4adc017..7c7ca71 100644 --- a/middleware/MediaManager/src/MediaManagerMakePtr.h +++ b/middleware/MediaManager/src/MediaManagerMakePtr.h @@ -16,6 +16,7 @@ #define MEDIA_MANAGER_MAKE_PTR_H #include "IMediaManager.h" #include "StatusCode.h" +#include "VStreamHandle.h" #include class MediaManagerMakePtr { @@ -24,5 +25,6 @@ public: virtual ~MediaManagerMakePtr() = default; static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); virtual const StatusCode CreateMediaManagerModule(std::shared_ptr &impl); + virtual std::shared_ptr CreateMediaHandle(std::shared_ptr &task); }; #endif // !MEDIA_MANAGER_MAKE_PTR_H \ No newline at end of file diff --git a/middleware/MediaManager/src/RecordMp4.cpp b/middleware/MediaManager/src/RecordMp4.cpp index 625ac04..ccc31b2 100644 --- a/middleware/MediaManager/src/RecordMp4.cpp +++ b/middleware/MediaManager/src/RecordMp4.cpp @@ -22,10 +22,12 @@ #include #include #include +#include #include #include #include -RecordMp4::RecordMp4(std::shared_ptr &recordTask) : mRecordMp4Object(nullptr), mRecordTask(recordTask) +RecordMp4::RecordMp4(std::shared_ptr &recordTask) + : mRecordMp4Object(nullptr), mRecordTask(recordTask), mIsRecordingFinished(OUTPUT_FILE_STATUS_END) { } StatusCode RecordMp4::Init(void) @@ -36,7 +38,24 @@ StatusCode RecordMp4::Init(void) return CreateStatusCode(STATUS_CODE_NOT_OK); } std::string videoPath = mRecordTask->GetTargetNameForSaving(); - StatusCode code = IOpenOutputFile(mRecordMp4Object, videoPath.c_str()); + std::string thumbnailPath = mRecordTask->GetThumbnailNameForSaving(videoPath); + const unsigned int duration_ms = mRecordTask->GetVideoDuration_ms(); + OutputFileInfo fileInfo = {.mDuration_ms = duration_ms, .mFinished = &mIsRecordingFinished}; + if (OUTPUT_FILE_NAME_MAX >= videoPath.size()) { + memcpy(fileInfo.mFileName, videoPath.c_str(), videoPath.size()); + } + else { + LogError("VideoPath is too long.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (OUTPUT_FILE_NAME_MAX >= thumbnailPath.size()) { + memcpy(fileInfo.mThumbnailFileName, thumbnailPath.c_str(), thumbnailPath.size()); + } + else { + LogError("ThumbnailPath is too long.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + StatusCode code = IOpenOutputFile(mRecordMp4Object, &fileInfo); if (!IsCodeOK(code)) { LogError("OpenOutputFile failed.\n"); ICloseOutputFile(mRecordMp4Object); @@ -54,19 +73,20 @@ void RecordMp4::StopHandleStream(void) { std::lock_guard locker(mMutex); if (mRecordMp4Object) { + OutputFileInfo finalFile = IGetOutputFileInfo(mRecordMp4Object); + MediaTaskResponse response(finalFile.mFileName, finalFile.mDuration_ms); + mTaskResponse.push_back(response); ICloseOutputFile(mRecordMp4Object); IMediaBaseFree(mRecordMp4Object); mRecordMp4Object = nullptr; } - std::string videoPath = mRecordTask->GetTargetNameForSaving(); - MediaTaskResponse response(videoPath.c_str()); - mTaskResponse.push_back(response); + // std::string videoPath = mRecordTask->GetTargetNameForSaving(); } void RecordMp4::GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { std::lock_guard locker(mMutex); if (mRecordMp4Object) { - StreamInfo info = {.mType = STREAM_TYPE_VIDEO_H264}; + StreamInfo info = {.mType = STREAM_TYPE_VIDEO_H264, .mTimeStamp_us = timeStamp}; IGetStreamData(mRecordMp4Object, stream, length, info); } } @@ -74,7 +94,7 @@ void RecordMp4::GetAudioStream(const void *stream, const unsigned int &length, c { std::lock_guard locker(mMutex); if (mRecordMp4Object) { - StreamInfo info = {.mType = STREAM_TYPE_AUDIO_G711A}; + StreamInfo info = {.mType = STREAM_TYPE_AUDIO_G711A, .mTimeStamp_us = timeStamp}; IGetStreamData(mRecordMp4Object, stream, length, info); } } @@ -83,4 +103,8 @@ StatusCode RecordMp4::GetAllFiles(std::vector &files) files = std::move(mTaskResponse); mTaskResponse.clear(); return CreateStatusCode(STATUS_CODE_OK); +} +bool RecordMp4::HandleFinished(void) +{ + return mIsRecordingFinished == OUTPUT_FILE_STATUS_FINISHED ? true : false; } \ No newline at end of file diff --git a/middleware/MediaManager/src/RecordMp4.h b/middleware/MediaManager/src/RecordMp4.h index 87e6968..369d812 100644 --- a/middleware/MediaManager/src/RecordMp4.h +++ b/middleware/MediaManager/src/RecordMp4.h @@ -30,11 +30,13 @@ public: void GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) override; void GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) override; StatusCode GetAllFiles(std::vector &files) override; + bool HandleFinished(void) override; private: - std::mutex mMutex; + std::mutex mMutex; // TODO: To improve and delete this mutex. void *mRecordMp4Object; std::shared_ptr mRecordTask; std::vector mTaskResponse; + int mIsRecordingFinished; }; #endif \ No newline at end of file diff --git a/middleware/MediaManager/src/TakePicture.cpp b/middleware/MediaManager/src/TakePicture.cpp new file mode 100644 index 0000000..014061c --- /dev/null +++ b/middleware/MediaManager/src/TakePicture.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TakePicture.h" +#include "ILog.h" +#include "IMediaManager.h" +#include "MediaBase.h" +#include "StatusCode.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +TakePicture::TakePicture(std::shared_ptr &recordTask) + : mTakePictureObject(nullptr), mPictureTask(recordTask), mIsTakePictureFinished(OUTPUT_FILE_STATUS_END) +{ +} +StatusCode TakePicture::Init(void) +{ + mTakePictureObject = ICreateMediaBase(MEDIA_HANDLE_TYPE_TAKE_PICTURE); + if (nullptr == mTakePictureObject) { + LogError("mTakePictureObject is null.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + std::string picturePath = mPictureTask->GetTargetNameForSaving(); + std::string thumbnailPath = mPictureTask->GetThumbnailNameForSaving(picturePath); + OutputFileInfo fileInfo = {.mTakePicture = true, .mDuration_ms = 0, .mFinished = &mIsTakePictureFinished}; + if (OUTPUT_FILE_NAME_MAX >= picturePath.size()) { + memcpy(fileInfo.mFileName, picturePath.c_str(), picturePath.size()); + } + else { + LogError("VideoPath is too long.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (OUTPUT_FILE_NAME_MAX >= thumbnailPath.size()) { + memcpy(fileInfo.mThumbnailFileName, thumbnailPath.c_str(), thumbnailPath.size()); + } + else { + LogError("ThumbnailPath is too long.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + StatusCode code = IOpenJpegFile(mTakePictureObject, &fileInfo); + if (!IsCodeOK(code)) { + LogError("OpenOutputFile failed.\n"); + ICloseJpegFile(mTakePictureObject); + IMediaBaseFree(mTakePictureObject); + mTakePictureObject = nullptr; + } + return code; +} +StatusCode TakePicture::UnInit(void) +{ + StopHandleStream(); + return CreateStatusCode(STATUS_CODE_OK); +} +void TakePicture::StopHandleStream(void) +{ + std::lock_guard locker(mMutex); + if (mTakePictureObject) { + // OutputFileInfo finalFile = IGetOutputFileInfo(mTakePictureObject); + std::string picturePath = mPictureTask->GetTargetNameForSaving(); + MediaTaskResponse response(picturePath, 0); + mTaskResponse.push_back(response); + ICloseJpegFile(mTakePictureObject); + IMediaBaseFree(mTakePictureObject); + mTakePictureObject = nullptr; + } + // std::string picturePath = mPictureTask->GetTargetNameForSaving(); +} +void TakePicture::GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) +{ + std::lock_guard locker(mMutex); + if (mTakePictureObject) { + StreamInfo info = {.mType = STREAM_TYPE_VIDEO_H264, .mTimeStamp_us = timeStamp}; + IWriteJpegData(mTakePictureObject, stream, length, info); + } +} +void TakePicture::GetJpegData(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) +{ + std::lock_guard locker(mMutex); + if (mTakePictureObject) { + StreamInfo info = {.mType = STREAM_TYPE_JPEG, .mTimeStamp_us = timeStamp}; + IWriteJpegData(mTakePictureObject, stream, length, info); + } +} +StatusCode TakePicture::GetAllFiles(std::vector &files) +{ + files = std::move(mTaskResponse); + mTaskResponse.clear(); + return CreateStatusCode(STATUS_CODE_OK); +} +bool TakePicture::HandleFinished(void) +{ + return mIsTakePictureFinished == OUTPUT_FILE_STATUS_FINISHED ? true : false; +} \ No newline at end of file diff --git a/middleware/MediaManager/src/TakePicture.h b/middleware/MediaManager/src/TakePicture.h new file mode 100644 index 0000000..27fcf2e --- /dev/null +++ b/middleware/MediaManager/src/TakePicture.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TAKE_PICTURE_H +#define TAKE_PICTURE_H +#include "IMediaManager.h" +#include "VStreamHandle.h" +#include +#include +#include +class TakePicture : public VStreamHandle +{ +public: + TakePicture(std::shared_ptr &pictureTask); + virtual ~TakePicture() = default; + StatusCode Init(void) override; + StatusCode UnInit(void) override; + void StopHandleStream(void) override; + void GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) override; + void GetJpegData(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) override; + StatusCode GetAllFiles(std::vector &files) override; + bool HandleFinished(void) override; + +private: + std::mutex mMutex; // TODO: To improve and delete this mutex. + void *mTakePictureObject; + std::shared_ptr mPictureTask; + std::vector mTaskResponse; + int mIsTakePictureFinished; +}; +#endif \ No newline at end of file diff --git a/middleware/MediaManager/src/VStreamHandle.cpp b/middleware/MediaManager/src/VStreamHandle.cpp index 5cfe5e3..c44c566 100644 --- a/middleware/MediaManager/src/VStreamHandle.cpp +++ b/middleware/MediaManager/src/VStreamHandle.cpp @@ -19,28 +19,37 @@ #include StatusCode VStreamHandle::Init(void) { - LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode VStreamHandle::UnInit(void) { - LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } void VStreamHandle::StopHandleStream(void) { - LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); } void VStreamHandle::GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { - LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + // LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); } void VStreamHandle::GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { - LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + // LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); +} +void VStreamHandle::GetJpegData(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) +{ + // LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); } StatusCode VStreamHandle::GetAllFiles(std::vector &files) { - LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +bool VStreamHandle::HandleFinished(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return false; } \ No newline at end of file diff --git a/middleware/MediaManager/src/VStreamHandle.h b/middleware/MediaManager/src/VStreamHandle.h index 8bee00c..3308260 100644 --- a/middleware/MediaManager/src/VStreamHandle.h +++ b/middleware/MediaManager/src/VStreamHandle.h @@ -27,6 +27,8 @@ public: virtual void StopHandleStream(void); virtual void GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp); virtual void GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp); + virtual void GetJpegData(const void *stream, const unsigned int &length, const unsigned long long &timeStamp); virtual StatusCode GetAllFiles(std::vector &files); + virtual bool HandleFinished(void); }; #endif \ No newline at end of file diff --git a/middleware/StorageManager/include/IStorageManager.h b/middleware/StorageManager/include/IStorageManager.h index 69a3918..e2c8297 100644 --- a/middleware/StorageManager/include/IStorageManager.h +++ b/middleware/StorageManager/include/IStorageManager.h @@ -23,7 +23,7 @@ enum class StorageEvent { SD_CARD_INSERT = 0, SD_CARD_REMOVE, - SD_ABNORMAL, + SD_ABNORMAL, ///< Abnormal status includes: The card cannot be mounted. EMMC_NORMAL, END }; @@ -56,5 +56,6 @@ public: virtual StatusCode GetSdCardInfo(SdCardInfo &info); virtual std::string GetFilesDatabasePath(void); virtual std::string GetFilesSavingPath(void); + virtual StatusCode FormatSDCardNow(void); }; #endif \ No newline at end of file diff --git a/middleware/StorageManager/src/IStorageManager.cpp b/middleware/StorageManager/src/IStorageManager.cpp index 8673d31..9a85349 100644 --- a/middleware/StorageManager/src/IStorageManager.cpp +++ b/middleware/StorageManager/src/IStorageManager.cpp @@ -77,4 +77,9 @@ std::string IStorageManager::GetFilesSavingPath(void) LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); std::string path = "undefined"; return path; +} +StatusCode IStorageManager::FormatSDCardNow(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } \ No newline at end of file diff --git a/middleware/StorageManager/src/SdCardHandle.cpp b/middleware/StorageManager/src/SdCardHandle.cpp index 0eb9d43..ebb3581 100644 --- a/middleware/StorageManager/src/SdCardHandle.cpp +++ b/middleware/StorageManager/src/SdCardHandle.cpp @@ -37,6 +37,11 @@ StatusCode SdCardHandle::GetSdCardInfo(SdCardInfo &info) info.mEvent = StorageEventConvert(mSdCardHal->GetSdCardStatus()); return mSdCardHal->GetCapacity(info.mTotalSizeMB, info.mFreeSizeMB, info.mUsedSizeMB); } +StatusCode SdCardHandle::FormatSDCardNow(void) +{ + mSdCardHal->FormatSDCard(); + return CreateStatusCode(STATUS_CODE_OK); +} void SdCardHandle::SdCardInit(void) { IHalCpp::GetInstance()->GetSdCardHal(mSdCardHal); @@ -61,15 +66,19 @@ StatusCode SdCardHandle::SdSaveFile(const std::string &sourceFile, const std::st LogInfo("SaveFile: %s -> %s", sourceFile.c_str(), savePaht.c_str()); constexpr int CMD_BUF_SIZE = 128; char cmd[CMD_BUF_SIZE] = {0}; - const std::string realSavePah = SD_CARD_MOUNT_PATH + savePaht; + const std::string realSavePah = savePaht; bool directoryExist = StorageBase::CheckDirectory(realSavePah.c_str()); if (false == directoryExist) { LogError("Directory not exist.\n"); return CreateStatusCode(STATUS_CODE_NOT_OK); } snprintf(cmd, CMD_BUF_SIZE, "cp %s %s", sourceFile.c_str(), realSavePah.c_str()); - fx_system(cmd); - fx_system("sync"); + LogInfo("cmd: %s", cmd); + int ret = fx_system_v2(cmd); + if (ret != 0) { + LogError("Save file failed.\n"); + } + fx_system_v2("sync"); return CreateStatusCode(STATUS_CODE_OK); } StorageEvent SdCardHandle::StorageEventConvert(const SdCardHalStatus &status) diff --git a/middleware/StorageManager/src/SdCardHandle.h b/middleware/StorageManager/src/SdCardHandle.h index b542b9d..c14a16b 100644 --- a/middleware/StorageManager/src/SdCardHandle.h +++ b/middleware/StorageManager/src/SdCardHandle.h @@ -25,6 +25,7 @@ public: virtual ~SdCardHandle() = default; void ReportEvent(const SdCardHalStatus &status) override; StatusCode GetSdCardInfo(SdCardInfo &info) override; + StatusCode FormatSDCardNow(void) override; void SdCardInit(void); void SdCardUnInit(void); diff --git a/output_files/libs/test_tools/libAppManagerTestTool.a b/output_files/libs/test_tools/libAppManagerTestTool.a index 1ec0ecb..44f192b 100644 Binary files a/output_files/libs/test_tools/libAppManagerTestTool.a and b/output_files/libs/test_tools/libAppManagerTestTool.a differ diff --git a/output_files/libs/test_tools/libDeviceManagerTestTool.a b/output_files/libs/test_tools/libDeviceManagerTestTool.a index fdb0ff0..580d737 100644 Binary files a/output_files/libs/test_tools/libDeviceManagerTestTool.a and b/output_files/libs/test_tools/libDeviceManagerTestTool.a differ diff --git a/output_files/libs/test_tools/libHalTestTool.a b/output_files/libs/test_tools/libHalTestTool.a index 3145a30..892de82 100644 Binary files a/output_files/libs/test_tools/libHalTestTool.a and b/output_files/libs/test_tools/libHalTestTool.a differ diff --git a/output_files/libs/test_tools/libHuntingUpgradeTestTool.a b/output_files/libs/test_tools/libHuntingUpgradeTestTool.a index a574b3f..1194c40 100644 Binary files a/output_files/libs/test_tools/libHuntingUpgradeTestTool.a and b/output_files/libs/test_tools/libHuntingUpgradeTestTool.a differ diff --git a/output_files/libs/test_tools/libIpcConfigTestTool.a b/output_files/libs/test_tools/libIpcConfigTestTool.a index 6d8f5c0..5caf743 100644 Binary files a/output_files/libs/test_tools/libIpcConfigTestTool.a and b/output_files/libs/test_tools/libIpcConfigTestTool.a differ diff --git a/output_files/libs/test_tools/libMcuAskBaseTestTool.a b/output_files/libs/test_tools/libMcuAskBaseTestTool.a index f40a8a3..f7b1411 100644 Binary files a/output_files/libs/test_tools/libMcuAskBaseTestTool.a and b/output_files/libs/test_tools/libMcuAskBaseTestTool.a differ diff --git a/output_files/libs/test_tools/libMcuManagerTestTool.a b/output_files/libs/test_tools/libMcuManagerTestTool.a index 701d415..072a3a7 100644 Binary files a/output_files/libs/test_tools/libMcuManagerTestTool.a and b/output_files/libs/test_tools/libMcuManagerTestTool.a differ diff --git a/output_files/libs/test_tools/libMcuProtocolTestTool.a b/output_files/libs/test_tools/libMcuProtocolTestTool.a index b3424ad..f6bad9e 100644 Binary files a/output_files/libs/test_tools/libMcuProtocolTestTool.a and b/output_files/libs/test_tools/libMcuProtocolTestTool.a differ diff --git a/output_files/libs/test_tools/libMediaManagerTestTool.a b/output_files/libs/test_tools/libMediaManagerTestTool.a index 492dfbc..e935df5 100644 Binary files a/output_files/libs/test_tools/libMediaManagerTestTool.a and b/output_files/libs/test_tools/libMediaManagerTestTool.a differ diff --git a/output_files/libs/test_tools/libMissionManagerTestTool.a b/output_files/libs/test_tools/libMissionManagerTestTool.a index 90cf573..4af9061 100644 Binary files a/output_files/libs/test_tools/libMissionManagerTestTool.a and b/output_files/libs/test_tools/libMissionManagerTestTool.a differ diff --git a/output_files/libs/test_tools/libUartDeviceTestTool.a b/output_files/libs/test_tools/libUartDeviceTestTool.a index 166f000..b4fa5bc 100644 Binary files a/output_files/libs/test_tools/libUartDeviceTestTool.a and b/output_files/libs/test_tools/libUartDeviceTestTool.a differ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7bca24b..4752575 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -28,6 +28,7 @@ if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) set(TEST_LINUX_MOCK "${TEST_LINUX_MOCK},--wrap=fx_read" CACHE STRING INTERNAL FORCE) set(TEST_LINUX_MOCK "${TEST_LINUX_MOCK},--wrap=fx_access" CACHE STRING INTERNAL FORCE) set(TEST_LINUX_MOCK "${TEST_LINUX_MOCK},--wrap=fx_fopen" CACHE STRING INTERNAL FORCE) + set(TEST_LINUX_MOCK "${TEST_LINUX_MOCK},--wrap=fx_system" CACHE STRING INTERNAL FORCE) endif() # Mock Linux api. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TEST_LINUX_MOCK}") diff --git a/test/application/HuntingCamera/CMakeLists.txt b/test/application/HuntingCamera/CMakeLists.txt index 8dc8625..6ce0ff1 100644 --- a/test/application/HuntingCamera/CMakeLists.txt +++ b/test/application/HuntingCamera/CMakeLists.txt @@ -13,6 +13,7 @@ include_directories( ${MIDDLEWARE_SOURCE_PATH}/McuManager/src ${MIDDLEWARE_SOURCE_PATH}/DeviceManager/include ${MIDDLEWARE_SOURCE_PATH}/DeviceManager/src + ${MIDDLEWARE_SOURCE_PATH}/IpcConfig/include ${UTILS_SOURCE_PATH}/McuProtocol/include ${UTILS_SOURCE_PATH}/UartDevice/include ${UTILS_SOURCE_PATH}/LedControl/include @@ -21,6 +22,7 @@ include_directories( ${TEST_SOURCE_PATH}/middleware/McuManager/tool/include ${TEST_SOURCE_PATH}/middleware/AppManager/tool/include ${TEST_SOURCE_PATH}/middleware/DeviceManager/tool/include + ${TEST_SOURCE_PATH}/middleware/IpcConfig/tool/include ${TEST_SOURCE_PATH}/utils/LinuxApiMock/include ${TEST_SOURCE_PATH}/utils/TestManager/include ${TEST_SOURCE_PATH}/utils/McuProtocol/tool/include @@ -46,8 +48,9 @@ set(TARGET_NAME HuntingCameraTest) add_executable(${TARGET_NAME} ${SRC_FILES_MAIN} ${SRC_FILES}) target_link_libraries(${TARGET_NAME} McuProtocolTestTool UartDeviceTestTool) target_link_libraries(${TARGET_NAME}# -Wl,--start-group + IpcConfig HuntingMainLib MissionManagerTestTool McuManagerTestTool McuAskBaseTestTool - AppManagerTestTool HalTestTool DeviceManagerTestTool TestManager UartDevice Hal LinuxApiMock + AppManagerTestTool HalTestTool DeviceManagerTestTool IpcConfigTestTool TestManager UartDevice Hal LinuxApiMock # -Wl,--end-group gtest gmock pthread) target_link_libraries(${TARGET_NAME} UpgradeTool Servers MediaBase) @@ -92,4 +95,14 @@ define_file_name(${TARGET_NAME}) # if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tool/CMakeLists.txt") # add_subdirectory(tool) -# endif() \ No newline at end of file +# endif() + +add_custom_target( + HuntingCameraTest_Report + COMMAND ../output_files/test/bin/HuntingCameraTest --gtest_output=xml:./${TARGET_NAME}.xml || true + COMMAND xsltproc ${TEST_SOURCE_PATH}/support_test/gtest2html.xslt ${TARGET_NAME}.xml > ${TARGET_NAME}_Report.html + COMMAND lcov --gcov-tool /usr/bin/gcov-9 -c -d . -o ${TARGET_NAME}.info + COMMAND lcov --remove ${TARGET_NAME}.info \"/usr/include/*\" \"${EXTERNAL_SOURCE_PATH}/*\" \"${TEST_SOURCE_PATH}/*\" -o ${TARGET_NAME}_final.info + COMMAND genhtml -o ${TARGET_NAME}_Report ${TARGET_NAME}_final.info + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) \ No newline at end of file diff --git a/test/application/HuntingCamera/src_mock/AppMonitor_Mock_Test.cpp b/test/application/HuntingCamera/src_mock/AppMonitor_Mock_Test.cpp index 8ff68d9..cc3d894 100644 --- a/test/application/HuntingCamera/src_mock/AppMonitor_Mock_Test.cpp +++ b/test/application/HuntingCamera/src_mock/AppMonitor_Mock_Test.cpp @@ -160,14 +160,58 @@ TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_GetStorageInfo) MainThread::GetInstance()->Runing(); } // ../output_files/test/bin/HuntingCameraTest -// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_AUTO_GetStorageFileList -TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_GetStorageFileList) +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_ALL +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_ALL) { McuManagerTestTool::MockMcuDeviceOpenFailed(mLinuxTest); MainThread::GetInstance()->Init(); TestManager::ResetTimeOut(1000 * 3); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - MockGetStorageFileList(); + MockGetStorageFileList(StorageFileEvent::ALL); + MainThread::GetInstance()->Runing(); +} +// ../output_files/test/bin/HuntingCameraTest +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_LOOP +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_LOOP) +{ + McuManagerTestTool::MockMcuDeviceOpenFailed(mLinuxTest); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 3); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MockGetStorageFileList(StorageFileEvent::LOOP); + MainThread::GetInstance()->Runing(); +} +// ../output_files/test/bin/HuntingCameraTest +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_EMR +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_EMR) +{ + McuManagerTestTool::MockMcuDeviceOpenFailed(mLinuxTest); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 3); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MockGetStorageFileList(StorageFileEvent::EMR); + MainThread::GetInstance()->Runing(); +} +// ../output_files/test/bin/HuntingCameraTest +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_EVENT +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_EVENT) +{ + McuManagerTestTool::MockMcuDeviceOpenFailed(mLinuxTest); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 3); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MockGetStorageFileList(StorageFileEvent::EVENT); + MainThread::GetInstance()->Runing(); +} +// ../output_files/test/bin/HuntingCameraTest +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_PARK +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_GetStorageFileList_PARK) +{ + McuManagerTestTool::MockMcuDeviceOpenFailed(mLinuxTest); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 3); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MockGetStorageFileList(StorageFileEvent::PARK); MainThread::GetInstance()->Runing(); } // ../output_files/test/bin/HuntingCameraTest @@ -205,4 +249,20 @@ TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_AUTO_AppPlayback) MockAppPlayback(); MainThread::GetInstance()->Runing(); } +/** + * @brief Construct a new test f object + * ../output_files/test/bin/HuntingCameraTest + * --gtest_filter=HuntingCameraTest.HS_INTEGRATION_HunttingCamera_EXAMPLE_ReInitAppManager + */ +TEST_F(HuntingCameraTest, HS_INTEGRATION_HunttingCamera_EXAMPLE_ReInitAppManager) +{ + SetAllCamerasResult(mAllCamerasMock); + MockOtherSideIpcMissionReply(IpcMission::TEST); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 20); + HalTestTool::MockKeyClick("format", 1000 * 18); // Simulate pressing a button. + std::this_thread::sleep_for(std::chrono::milliseconds(1000 * 17)); + MockGetSdCardInfo(); + MainThread::GetInstance()->Runing(); +} } // namespace AppMonitor_Mock_Test \ No newline at end of file diff --git a/test/application/HuntingCamera/src_mock/DeviceManager_Mock_Test.cpp b/test/application/HuntingCamera/src_mock/DeviceManager_Mock_Test.cpp index 1a0dc5d..0512264 100644 --- a/test/application/HuntingCamera/src_mock/DeviceManager_Mock_Test.cpp +++ b/test/application/HuntingCamera/src_mock/DeviceManager_Mock_Test.cpp @@ -65,6 +65,56 @@ TEST_F(HuntingCameraTest, HS_INTEGRATION_HunttingCamera_EXAMPLE_KeyControlClick) std::this_thread::sleep_for(std::chrono::milliseconds(100)); MainThread::GetInstance()->Runing(); } +/** + * @brief Construct a new test f object + * ../output_files/test/bin/HuntingCameraTest + * --gtest_filter=HuntingCameraTest.HS_INTEGRATION_HunttingCamera_EXAMPLE_SD_Abnormal + */ +TEST_F(HuntingCameraTest, HS_INTEGRATION_HunttingCamera_EXAMPLE_SD_Abnormal) +{ + SetAllCamerasResult(mAllCamerasMock); + MockOtherSideIpcMissionReply(IpcMission::TEST); + MockSdCardAbnormal(mLinuxTest); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 25); + HalTestTool::MockKeyClick("format", 1000 * 18); // Simulate pressing a button. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MainThread::GetInstance()->Runing(); +} +/** + * @brief Construct a new test f object + * ../output_files/test/bin/HuntingCameraTest + * --gtest_filter=HuntingCameraTest.HS_INTEGRATION_HunttingCamera_EXAMPLE_FormatSDCard + */ +TEST_F(HuntingCameraTest, HS_INTEGRATION_HunttingCamera_EXAMPLE_FormatSDCard) +{ + SetAllCamerasResult(mAllCamerasMock); + MockOtherSideIpcMissionReply(IpcMission::TEST); + MockSdCardFormatReturn(mLinuxTest, FORMAT_SD_CARD_SUCESS); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 25); + HalTestTool::MockKeyClick("format", 1000 * 18); // Simulate pressing a button. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MainThread::GetInstance()->Runing(); +} +/** + * @brief Construct a new test f object + * ../output_files/test/bin/HuntingCameraTest + * --gtest_filter=HuntingCameraTest.HS_INTEGRATION_HunttingCamera_EXAMPLE_FormatSDCard_Twice + */ +TEST_F(HuntingCameraTest, HS_INTEGRATION_HunttingCamera_EXAMPLE_FormatSDCard_Twice) +{ + SetAllCamerasResult(mAllCamerasMock); + MockOtherSideIpcMissionReply(IpcMission::TEST); + MockSdCardFormatReturn(mLinuxTest, FORMAT_SD_CARD_SUCESS); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 40); + HalTestTool::MockKeyClick("format", 1000 * 16); // Simulate pressing a button. + std::this_thread::sleep_for(std::chrono::milliseconds(1000 * 17)); + HalTestTool::MockKeyClick("format", 1000 * 16); // Simulate pressing a button. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MainThread::GetInstance()->Runing(); +} /** * @brief Construct a new test f object * ../output_files/test/bin/HuntingCameraTest diff --git a/test/application/HuntingCamera/src_mock/HuntingCameraTest.cpp b/test/application/HuntingCamera/src_mock/HuntingCameraTest.cpp index ecd026b..ab9af31 100644 --- a/test/application/HuntingCamera/src_mock/HuntingCameraTest.cpp +++ b/test/application/HuntingCamera/src_mock/HuntingCameraTest.cpp @@ -15,9 +15,11 @@ #include "HuntingCameraTest.h" #include "GtestUsing.h" #include "ILog.h" +#include "IpcConfigTestTool.h" #include "MainThread.h" #include const char *KEY_RESET = "reset"; +const char *KEY_FORMAT_SD_CARD = "format"; const char *LED_DEVICE_STATUS = "device_status"; void MainThreadTest::CustomizationInit(void) { @@ -54,6 +56,7 @@ void HuntingCameraTest::SetUp() LinuxApiMock::GetInstance()->Init(); McuManagerTestTool::Init(mLinuxTest); HalTestTool::InitSdCardHal(mLinuxTest); + IpcConfigTestTool::Init(mLinuxTest); DeviceManagerTestTool::Init(); TestManager::Init(); } @@ -68,6 +71,7 @@ void HuntingCameraTest::TearDown() std::shared_ptr test = std::make_shared(); LinuxApiMock::GetInstance(&test); McuManagerTestTool::UnInit(); + IpcConfigTestTool::UnInit(); DeviceManagerTestTool::UnInit(); DestroyAllCamerasMock(); DestroyAllKeysMock(); @@ -90,6 +94,8 @@ void HuntingCameraTest::CreateAllKeysMcok(void) { std::shared_ptr key = HalTestTool::MakeKeyHalTest(KEY_RESET); mAllKeysMock[KEY_RESET] = key; + std::shared_ptr key2 = HalTestTool::MakeKeyHalTest(KEY_FORMAT_SD_CARD); + mAllKeysMock[KEY_FORMAT_SD_CARD] = key2; HalTestTool::SetAllKeysResult(mAllKeysMock); } void HuntingCameraTest::DestroyAllKeysMock(void) diff --git a/test/application/HuntingCamera/src_mock/HuntingCameraTest.h b/test/application/HuntingCamera/src_mock/HuntingCameraTest.h index 2921507..04d59c6 100644 --- a/test/application/HuntingCamera/src_mock/HuntingCameraTest.h +++ b/test/application/HuntingCamera/src_mock/HuntingCameraTest.h @@ -18,6 +18,7 @@ #include "DeviceManagerTestTool.h" #include "GtestUsing.h" #include "HalTestTool.h" +#include "IpcConfigTestTool.h" #include "LinuxApiMock.h" #include "MainThread.h" #include "McuManagerTestTool.h" @@ -37,6 +38,7 @@ class HuntingCameraTest : public testing::Test, public McuManagerTestTool, public AppManagerTestTool, public DeviceManagerTestTool, + public IpcConfigTestTool, virtual public HalTestTool { public: diff --git a/test/application/HuntingCamera/src_mock/McuManager_Mock_Test.cpp b/test/application/HuntingCamera/src_mock/McuManager_Mock_Test.cpp index 5c71b95..313204b 100644 --- a/test/application/HuntingCamera/src_mock/McuManager_Mock_Test.cpp +++ b/test/application/HuntingCamera/src_mock/McuManager_Mock_Test.cpp @@ -64,4 +64,20 @@ TEST_F(HuntingCameraTest, HS_INTEGRATION_HunttingCamera_EXAMPLE_PirTriggeredMiss std::this_thread::sleep_for(std::chrono::milliseconds(100)); MainThread::GetInstance()->Runing(); } +/** + * @brief Construct a new test f object + * ../output_files/test/bin/HuntingCameraTest + * --gtest_filter=HuntingCameraTest.HS_INTEGRATION_HunttingCamera_EXAMPLE_CloseWatchDog + */ +TEST_F(HuntingCameraTest, HS_INTEGRATION_HunttingCamera_EXAMPLE_CloseWatchDog) +{ + McuManagerTestTool::MockOtherSideIpcMissionReply(IpcMission::TEST); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 10); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + HalTestTool::MockKeyClick("format", 1000 * 5); // Simulate pressing a button. + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + HalTestTool::MockKeyClick("reset", 1000 * 5); // Simulate pressing a button. + MainThread::GetInstance()->Runing(); +} } // namespace McuManager_Mock_Test \ No newline at end of file diff --git a/test/application/HuntingCamera/src_mock/MediaManager_Mock_Test.cpp b/test/application/HuntingCamera/src_mock/MediaManager_Mock_Test.cpp index 4dee901..f4c8402 100644 --- a/test/application/HuntingCamera/src_mock/MediaManager_Mock_Test.cpp +++ b/test/application/HuntingCamera/src_mock/MediaManager_Mock_Test.cpp @@ -16,6 +16,7 @@ #include "GtestUsing.h" #include "HalTestTool.h" #include "HuntingCameraTest.h" +#include "IIpcConfig.h" #include "ILog.h" #include "MainThread.h" #include "McuManagerTestTool.h" @@ -40,6 +41,34 @@ TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_EXAMPLE_MediaReprot) RemoveUpgradeFile(); } // ../output_files/test/bin/HuntingCameraTest +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_EXAMPLE_TakePicture +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_EXAMPLE_TakePicture) +{ + McuManagerTestTool::MockOtherSideIpcMissionReply(IpcMission::TEST); + SetAllCamerasResult(mAllCamerasMock); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 5); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + HalTestTool::MockKeyClick("reset", 200); // Simulate pressing a button. + MainThread::GetInstance()->Runing(); +} +// ../output_files/test/bin/HuntingCameraTest +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_EXAMPLE_FastStartMediaHandle +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_EXAMPLE_FastStartMediaHandle) +{ + McuManagerTestTool::MockOtherSideIpcMissionReply(IpcMission::TEST); + SetAllCamerasResult(mAllCamerasMock); + HalTestTool::MockSdCardRemove(mLinuxTest); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 5); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + HalTestTool::MockSdCardInsert(mLinuxTest); + HalTestTool::MockKeyClick("reset", 200); // Simulate pressing a button. + MainThread::GetInstance()->Runing(); +} +// ../output_files/test/bin/HuntingCameraTest // --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_EXAMPLE_MediaTask TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_EXAMPLE_MediaTask) { @@ -48,11 +77,29 @@ TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_EXAMPLE_MediaTask) std::this_thread::sleep_for(std::chrono::milliseconds(100)); MainThread::GetInstance()->Init(); TestManager::ResetTimeOut(1000 * 15); + IIpcConfig::GetInstance()->SetWorkMode(WorkMode::MODE_VIDEO); std::this_thread::sleep_for(std::chrono::milliseconds(100)); HalTestTool::MockKeyClick("reset", 200); // Simulate pressing a button. MainThread::GetInstance()->Runing(); } // ../output_files/test/bin/HuntingCameraTest +// --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_EXAMPLE_MediaTask_Twice +TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_EXAMPLE_MediaTask_Twice) +{ + McuManagerTestTool::MockOtherSideIpcMissionReply(IpcMission::TEST); + SetAllCamerasResult(mAllCamerasMock); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + MainThread::GetInstance()->Init(); + TestManager::ResetTimeOut(1000 * 25); + IIpcConfig::GetInstance()->SetWorkMode(WorkMode::MODE_VIDEO); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + HalTestTool::MockKeyClick("reset", 200); // Simulate pressing a button. + std::this_thread::sleep_for(std::chrono::milliseconds(1000 * 15)); + LogInfo("Make a media task 2 times.\n"); + HalTestTool::MockKeyClick("reset", 200); // Simulate pressing a button. + MainThread::GetInstance()->Runing(); +} +// ../output_files/test/bin/HuntingCameraTest // --gtest_filter=HuntingCameraTest.INTEGRATION_HunttingCamera_EXAMPLE_MediaTask_No_sdcard TEST_F(HuntingCameraTest, INTEGRATION_HunttingCamera_EXAMPLE_MediaTask_No_sdcard) { diff --git a/test/hal/tool/include/HalTestTool.h b/test/hal/tool/include/HalTestTool.h index 52d619e..2c6d74e 100644 --- a/test/hal/tool/include/HalTestTool.h +++ b/test/hal/tool/include/HalTestTool.h @@ -18,6 +18,8 @@ #include "IHalCpp.h" #include "LedControl.h" #include "LinuxApiMock.h" +constexpr int FORMAT_SD_CARD_SUCESS = 0; +constexpr int FORMAT_SD_CARD_FAIL = -1; class HalTestTool { public: @@ -71,6 +73,8 @@ private: // About camera hal protected: // About sd card hal void MockSdCardRemove(std::shared_ptr &mock); void MockSdCardInsert(std::shared_ptr &mock); + void MockSdCardFormatReturn(std::shared_ptr &mock, const int &systemResult); // TODO: delete + void MockSdCardAbnormal(std::shared_ptr &mock); public: static std::shared_ptr MakeKeyHalTest(const std::string &keyName); diff --git a/test/middleware/AppManager/src/AppManager_Test.cpp b/test/middleware/AppManager/src_mock/AppManager_Test.cpp similarity index 99% rename from test/middleware/AppManager/src/AppManager_Test.cpp rename to test/middleware/AppManager/src_mock/AppManager_Test.cpp index 64cddc8..66e2920 100644 --- a/test/middleware/AppManager/src/AppManager_Test.cpp +++ b/test/middleware/AppManager/src_mock/AppManager_Test.cpp @@ -257,7 +257,7 @@ TEST_F(AppManagerTest, INTEGRATION_AppManager_EXAMPLE_AUTO_GetStorageFileList) IAppManager::GetInstance()->Init(mAppParam); IAppManager::GetInstance()->SetAppMonitor(monitor); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - AppManagerTestTool::MockGetStorageFileList(); + AppManagerTestTool::MockGetStorageFileList(StorageFileEvent::ALL); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); IAppManager::GetInstance()->UnInit(); } diff --git a/test/middleware/AppManager/tool/include/AppManagerTestTool.h b/test/middleware/AppManager/tool/include/AppManagerTestTool.h index 5101ff3..872f149 100644 --- a/test/middleware/AppManager/tool/include/AppManagerTestTool.h +++ b/test/middleware/AppManager/tool/include/AppManagerTestTool.h @@ -39,7 +39,7 @@ protected: // About http void MockGetCapability(void); void MockGetLockVideoStatus(void); void MockGetStorageInfo(void); - void MockGetStorageFileList(void); + void MockGetStorageFileList(const StorageFileEvent &event); void MockSetParamValue(const std::string &item, const std::string &value); void MockEnterRecorder(void); void MockAppPlayback(void); diff --git a/test/middleware/DeviceManager/src/DeviceManager_Test.cpp b/test/middleware/DeviceManager/src_mock/DeviceManager_Test.cpp similarity index 100% rename from test/middleware/DeviceManager/src/DeviceManager_Test.cpp rename to test/middleware/DeviceManager/src_mock/DeviceManager_Test.cpp diff --git a/test/middleware/IpcConfig/src_mock/IpcConfigTest.cpp b/test/middleware/IpcConfig/src_mock/IpcConfigTest.cpp index cf4b85e..a8662f5 100644 --- a/test/middleware/IpcConfig/src_mock/IpcConfigTest.cpp +++ b/test/middleware/IpcConfig/src_mock/IpcConfigTest.cpp @@ -60,6 +60,6 @@ void IpcConfigTest::RemoveConfigFile(void) constexpr int BUFF_SIZE = 128; char cmd[BUFF_SIZE] = {0}; snprintf(cmd, BUFF_SIZE, "rm -f %s", IPC_CONFIG_FILE_PATH); - fx_system(cmd); + fx_system_v2(cmd); } } \ No newline at end of file diff --git a/test/middleware/McuManager/src_mock/McuManager_Mock_Test.cpp b/test/middleware/McuManager/src_mock/McuManager_Mock_Test.cpp index 0dc3693..f58dbaf 100644 --- a/test/middleware/McuManager/src_mock/McuManager_Mock_Test.cpp +++ b/test/middleware/McuManager/src_mock/McuManager_Mock_Test.cpp @@ -593,6 +593,41 @@ TEST_F(McuManagerMockTest, HS_INTEGRATION_McuManager_AUTO_SetFeedingCycleForWatc IMcuManager::GetInstance()->UnInit(); } // ../output_files/test/bin/McuManagerTest +// --gtest_filter=McuManagerMockTest.HS_INTEGRATION_McuManager_AUTO_SetFeedingCycleToCloseWatchDog +TEST_F(McuManagerMockTest, HS_INTEGRATION_McuManager_AUTO_SetFeedingCycleToCloseWatchDog) +{ + class McuAskTest : public McuAsk, public McuAskBaseTestTool + { + public: + McuAskTest() + : McuAskBaseTestTool(McuAskBlock::NOT_BLOCK, + McuAskReply::NEED_REPLY) // using McuAskBlock::NOT_BLOCK + { + } + virtual ~McuAskTest() = default; + void ReplyFinished(const bool result) override + { + McuAskBaseTestTool::ReplyFinished(result); + if (result) { + LogInfo("Ask data succeed, mDataReply = %d.\n", static_cast(mDataReply)); + // Do something here. + } + else { + LogError("Ask data falied.\n"); + } + } + }; + IMcuManager::GetInstance()->Init(); + std::shared_ptr ask = std::make_shared(); + std::shared_ptr testTool = std::dynamic_pointer_cast(ask); + testTool->McuAskDefaultFeatures(testTool); + StatusCode code = IMcuManager::GetInstance()->SetFeedingCycleForWatchDog(ask, CLOSE_WATCH_DOG); + EXPECT_EQ(code.mStatusCode, STATUS_CODE_OK); // STATUS_CODE_OK means write data to mcu succeed. + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + EXPECT_EQ(CheckAskExist(ask), false); // Ensure that the request has been processed and deleted. + IMcuManager::GetInstance()->UnInit(); +} +// ../output_files/test/bin/McuManagerTest // --gtest_filter=McuManagerMockTest.HS_INTEGRATION_McuManager_AUTO_STRESS_MultiThreadWrite TEST_F(McuManagerMockTest, HS_INTEGRATION_McuManager_AUTO_STRESS_MultiThreadWrite) { diff --git a/test/middleware/MediaManager/src/MediaManagerTest.cpp b/test/middleware/MediaManager/src_mock/MediaManagerTest.cpp similarity index 100% rename from test/middleware/MediaManager/src/MediaManagerTest.cpp rename to test/middleware/MediaManager/src_mock/MediaManagerTest.cpp diff --git a/test/middleware/MediaManager/src/MediaManagerTest.h b/test/middleware/MediaManager/src_mock/MediaManagerTest.h similarity index 100% rename from test/middleware/MediaManager/src/MediaManagerTest.h rename to test/middleware/MediaManager/src_mock/MediaManagerTest.h diff --git a/test/middleware/MediaManager/src/MediaManager_Test.cpp b/test/middleware/MediaManager/src_mock/MediaManager_Test.cpp similarity index 100% rename from test/middleware/MediaManager/src/MediaManager_Test.cpp rename to test/middleware/MediaManager/src_mock/MediaManager_Test.cpp diff --git a/test/support_test/audio_sync.g711a b/test/support_test/audio_sync.g711a new file mode 100755 index 0000000..f16c7e0 --- /dev/null +++ b/test/support_test/audio_sync.g711a @@ -0,0 +1 @@ +UUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUTTTTTTUUUUUUUU^_CFTXwWD_ZWQ]_WVSQPW\QVVTPQPUUVVQVRW\\Y]WTQTS\\WY[PWXT^VT[^US\PYRSVUV]STS]_UT]W_U^WPASS\UPP]VVQW_UVWQTQTVVTWTWUUWVTTTUTTUUTTUUUTUTUTTUUUTTWVVWVVQVUUUUUTTTTTTTUUTTWTTUUUUUUUTUUUUUUUTTWWWTTUUUUUUUUTUUUUUUUUUUUUTTUTTTUUUTUTUTTTTTTUUUUUUUTTUUUTTTTTTTTTTTTTTTUTUUUTTTTUUUTTWTTTTUTTWWWWTTTUUUTTTTUUTWWWTTUUUTTTUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTTTWWWWTTUUUUUUUTTTUUUUUTTTUUTTUUUUUUUTTUUUUUUUTWTTTWTUUUUUUUUUTTTUUUUUTTTTTTTUUUUUUUUUUUUUTTUUUUUUUUTTTUUUTTTTUUUUUUTTUTTTTTUUUUUUUUUTTTWWWWTTUUUUTTUUUUTTWWWWTUUUUUUUUUTTTTWWTTUUUUUUUUUTTWWWTTUUTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUTTUUUUUUUUUUUTTTTTTUUUUUUUUUUUUTTTTUUUUUUUTTTTTTTUUUUUTTWWWWTUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTUTTTTUUUUUUUUUUTTTTTUUUUUUUUUUUTTTTTUUUUUUUTTTUUUUUUUTTTUUUUUTUUUUUTTTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTUUUUUUUUUUUUTUUTTUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTWWTTUUUUUUTUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUTUUUTTTTWWWWWWWWWWWWTTUUUUUUTTUUUWQS\YXZDDGGGDDEZZ[XY^^_\\]RRSSPPQVVWWTUUUUUUUUUUUUUUUTTTTTTWWWWWWWWWWWWWWWWWTTUUUUUUTTTTTTTTUUUUUTTTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUTTTTTTTTUUUUUUUUUUUTUUTTWVVVVVVVQQQQQQQVVVVVVQQVVQVWTUVQWUUTTUUUWUTUUUTTWTTTQQWUUUUTTWWWVVQQPPPPPQVWWWWWWWTTWTWWWWWWWVWVVVVQQVVWVVVWWWWWWWWWWWWWTWTTTTTTTTTTUUTTTTTUUTUUUUUUUUUUUUUUUUUUTTUUUTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUTTTTUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUTTTTTTTWWWWWWWWWWWWWTTTTTTTTTTTTTTTTUUUUUUUUUUUUUTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTTWTUUUUUUUUTTTTTWWVVVQVVVTTTWVQVWTUUUTWWVQVVVQVVVVVVQQPPPPS]]\\]\]RRSSSPQQQQVVVVVVVVWTTTUUUUUTTUW\_]]RRRRSSPPQQQVVWWWTTTTTUUUUUUUUUTTTTTTTTTTUUTUUUUUUUUUUUUTTTTTTTTUUUUUUUTTTTTTTUUUUUTQSSSSPPQQQQQPPQQVVWWTUUUUTTTTUUUUUUUUUUTTTTTTTTTTTTTUUUUUUTTTWWWWTTTUUUUTTTTTTTWWWWWWWWTTTUUUUUUUTTTTTTTTTTTUUTTTTTUUUTWWWWWWWWWTTTTTTTUUUUUUUTTWWTTTTTTTTTUUUUUUUUUTTWTTUUUUUUUUUUUUUUUTTWWTTTTTWWTTTTTUUUUUUUUUUUUUUTTTTWWWWWTTTTTTUUUTUUUUTTWWWTUUUUUUUUUUUUTTTTTTWWWTTTUTTTTTTTTUUUUUUTWWWWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUTTTTWWWWWWWWTUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTTTTUTUUTTTTTTTTTTTTTTUUUUUUUUUUTTUUUTTTUUTTTTTTTTWWWWTTTTWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTWWWWWTUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUTUUTTUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUTUUUUTTTTUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUTTTTUUUUUUUUUUUUUUUTUUUUUUUTUUUUUUUUUUUUUUUUUTUTUUTWUUTUUTTWWVVTTUTVWUTUTTTTUUTTUUTTTUUTUUUTWWUTUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUTUUUUUUTTUUUUUUUUUUTTTTTTTUUUTTUUUUUUUUUUTTTTWTUTTTUTUUTTTTUUUUUTUTWTWWTWTWTTTUTTTUTWWTTUUTTTTTTUUUUUUTUUUUUUUTTTTTUUTTWVTTTTTTTTWTUUUUTTTWWWWTTTUWVQQQQPSPSSPQSQVWWUUTTUUTTUUUUTTTUTUTTWTWTTTTUUUUUUTUUUUUTTTTTUUTUUUUUUUUUUUUUUUUUTTTTTWTTTTWWTTTTTTUUTTUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUTTTTUUUTTUUUUUTTTTTTTWWUUTUUTUUUTUUTTTUUUUUUUUUUUTWWTWTUUTTUUUTUUUTTWTUUTWWTWWTWTWWWTUUUUUUTTUUUUUUTTTWTTTUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUTTTTTTTUUUUUUUTTWWWWWWTTTTUTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTWWWTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUTTUUUUTTTTTTTTTTTTTTTTUUUUUUUUTTTWTTTUUUTTTTUUUUUTTUUUTTTTTTTUUUTTTTTTTTTTUUTWTTTTUUUUUTWWTUUUUUUUUTWWWWWTTTWWWWWTTUUTVVVVVVVVVVVQQVVWTTTTTTWWWTTUUUUTTTUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUTTTTTUTTWVVVVWWTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUTTTTWWWTTTTTUUUUUUUTTTTTTTTWWWWTTTTTUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTUUUUUUUUUTTTTTTTTTUTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUTTTTTUUUUUUUUTTTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWTTTTUUTTUUUUUTTTTWWWWTTTTUUUUUTTTTWTTTWWWWWWWWWWWWWWWWWWWWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUTTUUUUUUTTTTTTTTTTTTTTTTTTTWWWWTTTUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTWWWWWWWTTTTUUUTTTTTUUUTTUUUUUUUUUUUTUUUUTTWWWTTTTTTTUTTTTUUTTUTTUUUUUUUTTUUUUUTUUUUUUTTTWTTTTTTTTTTTUUUTTTTUUUUUUUTWTUWTUTUUTUUTTUTUUUUUUUUTTTTTTTUUUUUTTTTWWTTTUUUUUUTTTTTWVQWWTUTUUUUTTUTTTTTTTTTUUUUTTTUUTTTTTTWVVVWTUUUUUUUUUUUUUUTTTTTUUUUUUTTTTTWWVWWWTTUUTTTTUUUUUUUUUUTTTTTTWWTTUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUTTTTTTTTTTUUUUUUUUUUTUUUUUUUUUTTTUUUUUTUUUUUTTTTUUTUUUUUUUUTUUUUUUUUTTTTTWWWWWWWVVVWWWWWWWTTWWWVVVWTTTUUUUUUUUUUUUUUUUUTTTUUUUUUTTUTUUUUUUUUUUUUTTTTTTTWWWWWWWWWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWWWWWTTTTUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUTUTUUTTTUTTUTTTUUUUUUUUTUUUUUUUUUUTUUUUTTUTTUUUTTUUUUUUUTUTUUUUUUUUUUUUUUUUUUTTTWTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUTTTTTTUUUUUUUUTUUTTTTUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUTTTTTUTTTTTTTTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUTTTUUUUUUUUUUUUUUUUUTTUUUUUUTWWWTTTWWWTTTWWWWWTTTTTUUUUUUUUTUUUUTTTTTTUUUUUTTWWWWTTTTTUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWTTTTUUUUUUUUTTTTWWWTTTTUUUUUUUTTTTTTUUUUUUTTWWWWWWTTUUUUUTTTTTUUUUUUTTWWWWWWWWWTTTTTTTTTTWWTTUUUUTWWVVVWWWTTTTWUUTTUUTUUUUTTWTUUUTUUUTTTWWTTTUUUTTUUTTWWWWTWTTUUUUUUUTUUUUUUUUUUUTTWVVVWWWWWTUUUUUUUUUUUUUUUUUTTTUUUUUUUTTWWWWWWTTTTUUUUUUUUUUTTTTTWWWWWWWWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTWTTTUUUUUUUUUUUUUUUUUUUUUUTTTTUTTTUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUTTTTTTTTTTUUUUUUTUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUTTTWWWWWWWTTTTTTTTTTTTTTTTTTTTTUUUTTTWWTTTTUUUUUUUUUUUUUUTUUUUUUUUUUTWWWWTTTTUUUUUUTTTTUUUUUTTTTTWWTWWTTWWTTTTUUUUUTTUUUUUUUUUUTWWWWWTTTTTWWWWTTUUUTTTTTUUTTTUUUUTUUUTTTTUUUUUUUUTUTUUUUTTTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUTTTWWWWTTTTTTTTTTUUUUUUUUUTTWWTTTTTTTUUUUUTTTWWWWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUTTTWTTTTTTTTUUUUUUUUUUUUTTUUUTTTTTTTTTUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUTUTTTTUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUTUUUUUUTUUTTTTUUTTTUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUTTWWWWWWWWTTTTUUTWWVQQQPPQQVVWTUUUUUTTTTTTTTTWTTTTTUUUTVQPPSSSSSSPVWUUUUUUUUUUTTTWWWWWWWWWVVVVVVVVVVVWWWTUTQSR]\\\]]RSQWUWVVQQQQVWUWS\^YXYYY^^_RQTUVPRR]]]RPVUWY@NKHOCFEX_STV^DACC@@AGZ_VV\[DFFDZ^STQYG@@FDE[[_QP\^YYYY_SUXt}}vOGYSUQAKtuI@D[\VRXDFGEX]WZt}~}vLYPTEBIO@EYRVUVQS\\__]QTWLsxz|tDRGOBZRT^DCLGRUW]YEXRQFszgzv[UYMHIGSXEGDAOC[T]MHLDRU\t|~rCQR_Y[AF\R@OBFD[^QTZAFXWQNqspAUZG@@DAEWEKtNF[]WT[B@XVFqrrLSCAEZ[GE_NuJCX_Q_[D[QTDs|rBVFDECBF\XOOLB[SP[X_]VTL||qFTD@OuHX[NHHOYV_DGXRUQSWYq~ruRP@LJtCU_LwtHGQ\XGE]WSSVWF}~vGPCuwtZYNvt@QSYGCDWWPS\]URqysK_EJvvNWPBvvNXTXACF_W_[[P[p~q@TFuvt@^NqwCRWXAGYWUQSSZsxpCWFwptC^JspH[WU^@F^QTR]QU[s{rN]EtqtLW]NqqKDS]DFFXUTPR]SSw~qB^^KuNB]W[ItKC\T\^DXURTZ}}wJ[[@ItJG\NtwO_\DD^RTTH}vwtGUUP]CutMQCuuBZ^WP]_[[PXwwuJCQV[NKAQ^CLBCFSREE_WTuJHqvEQMN@XV]_YANOZWU]_SR\T@KuqwZRMN@GYP^AHOE_PVVP^]TZIKqvFW[AA@GRWYBBAD^TQSRST]OMusK_RXZACDSQ[G@MB[VWTWPWHGIsGPWVGNLGSWWSYDMF^\WUJQL{sFDP[BFAZEXPV\_[EEZQUGRVrLCB^V[]_EAA]V_R\SV_ZD\USSYQC}wCOB_WQUREGY][YWUQ\RWR]WGD}t@L@^QYD^_CET]WURRUU\SWQQC}wKtHGTP[DZ@NZP^RUWTVTS_PSYRWVU_pptqqK_\[XBuCXDYUQ_RPSRsvNprt[V]WQ]NwM@H@SQPQWW]R_qJu}rtFZXSIJOtwOGE^VCBCq}swOI]_@FupuuK@YRRSCJuHMJGUPRAttuwwC^_DQQMOCBMN_T]VDBMuvKOB[WW[RMBFFO@WR]]FFBuuLBC_ZWS@@EDBAVS]VY^AIHHLOGPVUGT]OBGCN@SY_P\]@INNHHX__TCPPOLD@LCP\YTRPGHMHuI@EYTA^WOHEBL@\S^QPDOONJKFDXCUXu@AHMG]^PT]@OIttKMA^UMRYwNFuLD^_S\DONuvHLOYWT@RSwJFwKAE^P_ANKwqtOOXTLS_pHAqHGG^VXMIupsuuuYPQVJVCMOrBEGS@OIwsvJtLRQBMRp|CprA@ET]KIurpwwNZPTIQM~KJIC@SCJKr}qptCZTN[Z|sHqLN\WTEJt}}s}w@FPOFF|}wqHIRTDJJ|p}sBF\ACCrrqvKJRUGutrpsvMEQ@Bt|rr~vJOPV\Lvq}sptC^VFNp}p|}wJDVP[Hws}sqIFRY@t}}s}qtH_SAJwsrpquC[TTXAtsr|stJC]T^BKwsspvIG]]EMwqs}pwHE]T\GOtqqquLDS\DOJtqpqwNZSW_[ALHuuI@_T_GMItvvvuB[QT]ZACLOMAXVTYAMOHJttuLG\TTP^EF@AD^PPYEGABLNNLA[SWS_XEE[\VT]XZZEEFCC@D^QUWQ]^XX_PT_ZDEDDFCCAE\TWVP\XDAFZ\T^FMBBBBLLBG_WR^ECNHHMG]WDItwuKNMBFYVV_DMJwvvuOZPWDItwuMFX_]VW_D@OJtwtNGPSAHuJMEY]]RW]XG@BNJuJM[V_NttKFY_\_\UYGGFAMJtuL^YJwuNDXE[_WT^GFEAOJtHEQ\JppquGXVXCNJNOO@E^UF}x|wZUFwvH@RV]RSWURXXURtytEV@utGQQ_Q_BAXSJ~qL^SLuJAWQW_GINYYvzzpAWFwvH_TWQFOuLQVPURqedrEUMsp@TTP^BKuL_P]QI~ezJQQBvsIVZBKtLDXTUR^YWtzGZJvvZTBJuuGVUSY^]uypYAttHSWLwuOYUYD^PRp}}N]HJOEGtwJ@^WQZA[PWHs|w^QMKI@Q[JJOMZRU_AG_PPv|rNVYKI@XUTAJOCFYQWYE\VVRqx}MPDKKBXSQCOBACFR]X[EXTMtqtDQPD@ZAXVSFA@NC\T\^_^PBrrtA^RGCOMASQ_GOKB[W^CAYP]twwuF[SREYAIZTW[AKJESV]Y^_PTOvwID^VS[XDLAVRFOICD_TR\XZXRENIJNF[TS\CB]UYEFLFYSQ_Z^PU\IJJJMXQXMLYTRXZCNNX_EX_^RFNFLHA_SXFA]TQP]_MO[RWVPP]\PUROHwpuE^ACGZ[QTELNBZ_UUQ^[^PT_JFHqtZ_Z[FAERQGFG@@EQUSPUWDAt}rJ_TSEOB[\PUUYFAF[RWWU^VMsvHDPPPFAXZDRT\[DEY\PWS\HwJME^RQ^^XFD_SWSQQY[^^]SP]PJJCOG[_PR^DZ[YPTQUTVQVS\VWVQQGwuNN@[WW\[EDFERVTUQS]^^_]PT\WQJJAMCE\Q^Y_GA_\YSUTUVTVQUYtuuvwKXQTS\AIMA@E]UU]\]^_QAJNuwuM]QPYOHBOOFXST]^FNJtMBBPVYEMJJIBAXTWTVTWTSGOILMHZWQQSZLNNOLF]VYZ^E@AD^Z_VQ\GFAB@GXSWRUSEDFFFB^S_UUYDECMCA[\V_CGBJNL@DZVS^ABOKOMA^QQUXTQZ^_[_Z\V]WQXXD@FFZ_RUEE[OIBOADXVP\[BINON@X]TVZC\DOZEG]_QPR_F@CB@FXSTVA_[I@FLZ[YTPSEBCNNBF[PUFB[MuFBC\^P]XEBNNNLA[SFB[OuANM\X]R[DOHHJNAD]WDNGMJFCBQSSVXECNLNOD^RUXMFMIGMBPPSVRG@NuINNG\QTTEMLHH@OCRPVYCBHtJKLE_VTV]FCOKLGCD_QV^FMKKNNBE]TQXGFOJO@F_]QU_EAMHHKLD_VTRF@CIKNOA_]TW_ECIHHHBG^WW[BMLNNOLE]TW\EABHJKIAYPQEBBBMMMMZRWYFCMONIOA^TQGLNOBA@@E^U^@NKHOLBFXQW[BONL@AFZ^Q]DMKJHNCDYPRFMOL@GGEX]W^FNJJNBDYSPDBMBAEEEX_TPXCHtuIBE_QSANHOCGFFE^USEOuwuLGYRV^@LOCDDE[YVVS^GMHJNF_TSMutKBAAGXVVP]ZOtwHEVQ]SWJ}}vIEZD^TSFAD[EBKNDVUSPV\RNydyvFSSPYKtL[\^GF_PAMDTS\WGtrstODSW]ZFZ]PSPRQU^BLG]UTuxe~wFPYHwKZTVVTZIIGPWTWWBdztRDqqN]U[OKNZTVWAdztPRHsqFTXIwtCVWS]SWLpy}CRGNtMQW^GMHNEWUWSRRSVwyxq^ZHwJXREMHKC]V_Y^\WNqruSXCB@QW]AIN@_UQPR\RTwx{vSVBtwMWT[NuIA\SFCZQBwpt^[OM@SU]ANBD]P_XXVO~yv^]LtuFTEKuLXWPABZQUVVOwtO\RCHG_PGHK@YSPYX^WBs}t\\NJL[UGJJ@^VQ[Z_VPPTCuuN^T\CO[]YAOLERW^E[^WGp}wDRMJBXVTFHK@X\W\[Y_RQQQUBtJBS]CM^RU]E@A^SQU\RVPVMpqH[VYMMG[\SGBCAGXVV\[EXQIIL@\TVW_AES_P^XFFY\QQRVWPWRuquB[QQ[GFFERS[FMMDSVD@E\VTTLtKODQVRZFXEZS\XC@Z_UVS_\VTT^tvuLG_QY[EBB_RDNNF\WQ\Y^]SWENMMC^SWPXYDGQU]ABZ^\WWTWQVVUEuwwJB_T_AKO\VZFAMLEV]XY]QVU_NDE@ERUWUQY[ZSRZ[\YFZVPVCIKwwBW_BO@[_VV_FOMD^]WR\PUPKBBuI^[GEZZ\WUQ^EBCZ^SUUE@usqORP_DBF]UUPRYFAYQU@[LqtC_USYGG\SRWTQ\YED^SV__AvqKG^^S[Z^YZ^QTTUV__S\^ST[SEtJMF^]WUP]\^[X]SVTVTQQTTWVP_uptNCD]TS^[DGZY]QVTWSUWSQVVWWTP\SHwILLD_RY^XED\R^]TVPUYttIJJC]R_YFBFYY_UTWTU]KILKHF^WVS\FMDDFYSVUAM@HwKAZYXA@BLOG\^VTQWUSEEMuJN@G]V[[CHBC@[R]Z]XLBEZF_SSY@BC@@F\VWSVRG@FFCGRSWTT_BACIMF[_QXC[DNLZ[F]UQPXF@CCB@X\RVVXXXEFE]^R]__GADGZYSUZ[^ANAZGDWTVY[@MCNBZ[RWW\R]^X_S\SSPRZZXE[^SWT[\YCMAGAEVQVW]^FA@OAEZRUTV]QP_\R]]RQQPTTTUV\S\[_^Y\RQWSSPDAGAA@X]_TTP_GDFMDE[SWWUWSPSRRRPPQUPPSY\\_RPVTPW^ZXEEZX]]QWQ\[YZG[X_STTQTPRPR]R]RRSQVWUVSP\_\_\SSVWTV___XY^\RSWTQR\\^Y]]SWWVVSRR]]]]SPQWWTUTTTWWWVWTUTSS]^_^_]RQWUUTQPSRR]RPQVTTWVQVVWTUUTTWWWWVWWWWVVVVVVVWWTUQPS]]]]SPVTWQPSSSSPVWUTWWVVWWTUUUUTTTTTTTTTTUUUUUTUUUUUUUUUTTTTTTTWWWTTTTTUUUTTWWWWWWTUUTTTTTTTTTTTTTTTTTUUUTWWVVVVWWWUUUUUTUUUUUUUUTTTTUUUTTTTTTUUUUUUUUUTTTUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTUUUUUUUUTTTUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUTTTUUUUUUTWWWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUTTUUUUUTTTTTTTUUUUUUUUUUTTTTTUUUUUUUUTTTUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUTTTTTUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWWWTTTUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTTTTTWTTTTTUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUTTTUUUUUUUUUUUUTWVVQQPPPPQQVVWTUTWVQSRR\\]]]SPQWTUTVPR]]_^__\SPQWTTTUTTUUUVQR^XX[E[YX_R]SVQVTWTTUTTTWTQQR^[XZGE[Z^RRQUUUUVVWPQWWUVQ]XDDFAAEDX]RQTTWWQPVVVQR_ZFFFCAEDZ]]PUUQVS]RSSWS\YFB@BL@DF[]SVWVQ\\]\]QWU_^[CLMNIMA@E]RWUQP]_]\]QWSXX@INIJKBBA_PVQP]XY^Y\VVTXZFHJKttNMCXPVVP\[E[[XRQU]GFNtttvuIMF\VS\XGGDDYSWU^@LJwwwwJNFXQTRYDACAG[RWQWZIHtpqvvuNDXTPZ@BIKLLA_PS]YuttrrvqwHGXQV\NONquLKFRPY]Hwus}qpvKA_PZBNvqvvwOFYRD^wsw}~qpqHE^QGtLp~vqrBDFQC]t}wr{pvpK[_P^wKt{stuYCQL_Iqp{}vstE]R]wttx|t|wYFRO_Kqs{}vpu[SQU_wquxyu}p\D_H^Jyprz}qpuZPVUXupv~yvsp[X]VLYwq}zsqqNYPUEtqpy~vsvYXSSL^vv|zpppOYRSDvrq{xt}q]DRXBXsvy{qsqC^S_Mwrryvpu^_@E@sqz|wsuZ\WZNtr||}qwL^PC]uw}eqqrL[^BKw~||pJBXUEZY|rwzxw|pGE_RJIpx}|wHM\A\Dvve|wtZDP^KLsxp|JNLVAVK~u}evpMZE@JIxp~IHOSXQprKz{t|sEFXOBuysrytIK_ZV[}uverv~uD@Q]BCvvs|NMNW_upL~xw}sG@[ABI|rvvCJZW^UXsOw{vqOGCT\CFv|ts}COOUSutM~}u|vEM\UDAOsqtruGO^S[wCv~tqrCFFRZFuqtqpMMBWTUSTMu@srJsvGBX_DBtwtvtCCZTTWWLLCsvupuFC\\ECuuJtHFF^UUQCCBquJvNFASU_ZBJKuuL@G]VS@FBwKJwLFGPT_[BKIJuM@GRQTVGEFtNNtLGA]P^DBLOOBDE_WTUQCDSwL_vFWMVVTVXDDCBDDE]SQXXPLCYNB\FYVVQ[EZCA[GXP]WUYYP@G^CG\Z\UWVV]XY[DY^^QWW]\QE[\D[R^SUWPR^XXZY\]VVTQ[]_G^^D_]_VTTS]]XX^Y\PPTTR\SYY]YYR]STTTWPS]\]]RQWSQQ_]R^]S\QWVTVVSSS]SPQTUTSPS^\_X^__]SPVWTUUWWVPPPRSPSQTTTQVPRSR]SSSQPQVWWVWVPQPSQQPWWTVQQRSS]RSSQVQTWVTVQWQPWQQTWTUVPPR]]\\]\]SRPQPVWQVWVWTWUTWVQSSR]]]\]]]SSPQVVWWWTUTTWVQPPSRSRRSSPQQQWWTUUTWWVQQPSSSRSSSSSPQQVWWTTUUTTWWWVVVQQQQQQQQVVWWTUUUUUTTTTUUUUUUUUUUUUUUTUUUUUTTTTTTTUUUUUUUUUUTTTTWTTTTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUTTTTTTTTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWTTTUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUTUUUTTTTWTTTUUTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTTWWTTTTWWWWTTTTTWTTTWWWWWTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWWWWWTWTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTWWWWWTTTTUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUTTTTUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUTTTTWWWWWWWWWWWWTTTTTTUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUTTTTTWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUTTTTTUUUUTTWWVWWWWTTTUUUTTWWWWWWWWWTTUUUUUUTTUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUTTTTTTTTTTTWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTTWWWVVVVVVVVVVVWWWTTTUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUTTTTTTTUUUUUUUUUUUUTTTUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTWWWWWTTTTTTTTTTTTTTTTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUTUUUUTTTTTTTTUUUUUUUUTTUUUUUUUUUUUUUUUUUUUTUUUUTTTUUUUUTUUUTTTTTUUUUUUUUUUUUUTTWWWWWTTUUUUUUUTUTUUUUUUUUUUUTTTUUUUUTTUUUUTTUUUUUUUTUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUTTTWWTTTTTUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWTTTTUUUUUUUTTTTTTWTTTTUUUUUUUUUTUTTTUUUUUUTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUTTTTTTTUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUTWWWVVVVVWWWWTTTUUUUUUUUUTUTTTTTTTTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUTTTTTUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTWWWTTTUUUUUUUUUUUUUUTTTTTUUUUUUUUUUTTTUTUUUQPVUTVVTUUUWVUQQVTTUWWTTUTTUUTUUTS^^\QTUWPSSQTDIH@XDXX^SV[OMZPSYXY]PQTEvst\]BGGGXS\JtM_^BOB^T^p{|FVMCBCG^\wsuE^CNKCSWWJzdrTGuOLBDRL||HRTGIuu@WPRQNef{E[quKNFRE{vYTEIJuL]PWvgayQFqtJM[VO{zvRTFJwqJSQX_Vy`c~I}pvLYqdewT\OwppB^DRwfbgLRs}stDAzgEDv}}tSPAEZdb`|uy|sNRV|fduWO}{xJMuGp`oaurx~rLTMd`{@Puyd{MWuvZbofBR~z{r@uf`y[\wxdyZRuw^PdlleWExz{pErafrQRpda{YEsqS@anlzO{eepYRxcfvYsg`z\@rp\DfioeBzdes[QycfqSwdcdDG||ZRgingQ[zddF|c`rVI{cavP}{u|ljmsgfewUIfbdCXrfbd_Kx|\[fhig@dfg|ZT~c`sWB``rpxvIcklxvggevVYecfJWL~ffqq~t^akidugges[U~c`pTAveay\M}}DMbjo~gztPRzbfuUMqegrUNrsZWfhidpdx}t^}c`rQFv~{rX_vrJXakixre~suX~b`sTWMp}JWYtvH]xmlzpzrtMXWp``}VQFupvZPEHNXvaczUuvOMG]HgfsS_BwvEEOB_QPQTIg`z]GtIHtHZI{{w^SMwt^CHOZQQ]PUsfgsBwuuKZuxyuRXLJLSTu}sKQW\S\zc`sJvuJL[Qse~IT[OtLQ^Jspu\P@MEvgfxVBvKBC^WC~xvZ]@JH_T_AItuZ_GF^Zs|vR[KMEE_V[qrHP\EAFSW]XE[_QW]ZZ^SFsp\^OCGG^QYuwM]SYDESTQ]^Y^PS\]SR_\WS\_RWDIKC]\PRRVT^AG_VWVSSTUTQQQQTWPPPSRPVTUWQQVTYJuOR\E^]^RW]AB[STVQQUWVSRPVUUUUTTWTTPPVTTWWT^ZZ\W]RQPPTUS_RQTTVWTUWPSSSQWTUUTTWTUTWR]SWTVPSPPQUUTWWTTWQSSQWUWVVWTVSSQTUWVVVVVWWWTUTTWQQVVWTWWVVWTUUTWWWTWPPQVUWQSPQWTTVQPSPQVTTWTTTUUTTUTUTTWTUUTTWWVVWTTWWWWWWWTUUUTWTUWVWTTWVVVWTTUUTWWTUUUUUTTTTUUTTTUTTTTUUUUUUUUUUTTTUUTWTUUUUTUUUUTTUTTTTTTUUTTUUUTTTUUUUUUTTTUUUUUUUUUUTTTUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUTTUTTTWTTUUUTTTWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUTUUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUTUTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUTUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWVVVVVVVWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUTTTTTTTUUUUUUUUUUTTTTUUUUUUUUUUUUUTTUUUUUTUUUUUUUTUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUTTTTWTTWWWTTTUUUUUUUUUUUUUUUTTTTTTTTUUUTTTTUUUUUUUUUUUUUUUUTUTTTTTTTUUUTWWWVWWWTTTTTUUTTTTTWWWVVWWTUUUUUUUUTTTTWTTTUUTTWWWWWWWTTTUUUUUUUUUUUUTTTWWWWWTTUUTWWWWTTUUTWWVVVWTUUTWWVWWTUUTTTTUUTTTTTUUUTWVVVVWWTUUTTTTTTUUTWWVVVVWWTUUUTTTTTUUUTWWWWWTTUUUTUUUTTWWWTTUUTTTTTUTWVVQQVWWTTUUUTWVVWWTUUTWWVWWTTUUTTWWWTUUTWWVVWTTUUUUTWWWTTUUUTWWWWWTTUUTWVVVVWTTUTWWWWWWWTTUUTWWVVVWWTUUTTWWWWTTTUUTTWTTTUUTTTTTTUUUUTTTTUUUTTTTTTTTTTUUUUUTTTWWWWWWTTUUUTTWWTTTTTUUUTTTTWWWWWWWWWWTTTUUUUTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTTUUUUTTWWWWTTUUUTTTTTTWWWVWWWTUUUTTTTTUUTTTUUTTTTWWWTUUTTTUUUUUUUTTTWWTTTTTTTTTTTUUTTUUUUUTTUUUTUUUUTTTTUUUUTTUUUUTUUUUUUUUUUUUTTTTUUUTTUTWWWWWWWTWWTTWWVVVTUUTTTTTWTWTTUTWQQQQWUUUUUUTTUTUUUTUTTTTUTUUUUUUUTTUTTUUTTTTUUTWTTTWTTTTTWWWWWVWWVVWWWTTTTTUUUTUTTUUUTUUUUUUTUTWTWTTTTTTTTTUTUTUTUUUUUUTUUUTUTTTWTWVVVVVVQQQSRR]_]YXXZZEEDGGAGGZ[X_\SSVWTUUTWWWWUTTUTTUWUWTTUUUTUUUTUTUUTTTTWWTWWTTVTWWWVVWWTTTTTTUUUUTTTTTTUTUUTUUTUUUUUUUTUTUUTTWWTWWWWWTTTTTTTTTUUTVQR]\__\\]SSPPQVVWTRDLMBKIG_WRR^XDMAZYR\]VVTCuwICB[TTSZZ\QVSRS]XZX]UDKJHGEEURS\[DD\T]X[DC@E\QvppJY_Q]AGFGXSSDBONLA]UWVT[}}p@CtL@ZQYOI@@MF]T]^YXRH~qX_JwB^_LtHZRRQUUPZEX_PWT]~z}O^IpJ_UMwvCVT\BKA\QWtdzwSRAvp@AqpuS_s|uRRFF^IffqUSLp|KUA}~v\QG}xIWXHO]wcaq[HsNO~wVVRFvuRYCMRH`avXus|NB|KWPZJwMTQX@BYCffq[t}KB}|KTVRZLJO\W^FLG}fxGOq|q\Rw}vZS@H@]UYFDSres^WNvqN_tqH_R@N@YTQZAFSqzsEVOtwOW]JvH[S@BGZRQZAFR\}|OVXLJJ[DJHAVXFEZ^QRXZ]Spp@T\AIHX[OODVT[D[[X]TRY[\WtrH^PGOKGQW\MOA]UXGXX^SQTRY]WUCpK[_GLDWTEAG]TTRY]R\RQQRRQVUMwOXU^FB[QQUV[GD_PPUR\]_^\PUQSSSST_uOEQ\Z@ZQVW^Z[]VQVS]PS\\]WUWPQWWNIG_UUWW]XXWWP_Y_PPPUSPP\\RVTWTWTFME_UWTSY[SWPWR^XYSPPWUSY_PTUTTWWQ[\PTQVUP_YRTVVTPPSVWPPSSVTTRX]QWP^YSVSQTTQVVTVSQWQPSPWTWWWTVSPQWVQTUWQSSQQPQWUUS]\^]SPVVQQVQWTU^]SQWVUQ]]PVSRVTVQSSPQVVSSQWUUUWWWWTTVQPRSSRSQPSR]RSSSSPQPSQWUTQQPVVPWTPR]\R\_]RSP\^]_Y^]PVVQWTTUUTUTUWPRRSR]RRSSSRRR]RPQWUTVQQQPRRSR]\\SSRRRPQQQVVWWWUUUUWVVPSSSSRRSPQQQVWWWVWWTWTTTWWWWVVTTTTWTUUUUUTUUUTWWWTWWWTWWWWWWWWWWVVVVWWWTUTWWTWTTTUTWWWTUUUUUTWWWWTTTTTUTUUUUUUUUTTWWWTTTUUUUUUTUUUUUUUUUTTWWTTTTWWWWTTUUUTTWTTUUUUTUUUTTTTTTTUUUUUUUUUTTTTUUUTUUUUTTTTTUTTWWTUUUUUUUUTTWWWTTTUUUUTWVWWTUUUUUUUUUUUTTWTTTTUUUUUUUTUUUUUTTTTTUUUUUUUUUUUUUUUTUUUUUUTTWWWWWVWWWWTTTTUUUUUUTTTTTTTTTTUUUUUUTTWWWWWWWWTTTTTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUTTTTTTTTTTUTTTTTUTTUUUUUUUUUUUUTWWWVWWWTTTTTTTTTUUUUUUTTTUUTUUUUUUUUUUTTTTTTTTTTTTTTTTTUUTTTTTTTUUUUUUUUUTTTTTUUUUUUUUUUUUUUUTTWWWTTUUUUUTTTTUUUTTTTTTUUUUUUUUUUUUUUTTWWWVVVVVVVWWWTTTUUUUUUUUUUTUUUUTTTWVVVVVVWWWWTTTUUUUUUUUUUUUUUUUUTTTTTTTTUUTTWWWWWWTTTTTUUUUUTTWWWWWWTTUUUUUTTTTTTTTTTTTUUUUTTTTWTTTTTUUUUUUUUUUUTTWWWWWWWWWWWWTTTTUUUUUUUTTTTTWWWWTTTUUUUUUUUUUUUUUUUUTUTTUUTTWWWTTTTUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUTTUUUUUUTTTTWWWWWTTTUUUUUTTTTTTUUUUUUUUUUUUUTTTTUUUTTTTTUUUUUUUUUUUUTTTWWWWWTTTTUUUUUTUUUUUUUTTTTTTTUUUUUUUTTTUUUUUUUUTUTTTTTTTUUTUUUUUUUUUUTTTWWWVVVVWVWTTTTTTTUUTTTTTUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUTTTTUUUUUUUTTTTTWTTTTTTTUUUUUUUUUUUUUUUUUUUTUUTTTTTTUUUUUUUUUUUUUUUTTTTTTTUUUUTTWWWWTTTTTTTTTWWVVVQQQVVVVVVVWWWTTUUUUUUUUUUUUUUUUUUUUTUTTTTTTUUUUTTTTTTUUUUUUUTUTUUUUUUUUTTTTWWWWWWTTTTTTTTTTTTTTTTTTTTTUUUUTTWWWWWWVWWWWWWTTTTTTTTTTTUUUUUUUTUUUUUUUTTTTTTTTTTUUUUUTUUUUTTTWWTTTWWWVVVWWTTTTTTTTTTTTTTTTUUUUUUUUUUUUTTTUUUTTUUUTTTTTWWTTTTTTUUTUUUUUUUUUTTTTTUUUUUTTTTUUUUTTWTTUUUUUUTTTTUUUUTWVWWTUUTTTTUUTTTTUUUUUUUUUUUUUUUUUTTUTTTTTTTTTUUTWWVVVVVVWWTUUUUUUUUTWWTUUUUUUTTTUUUUUUUTTTUUUTTUUUTTTTTTTTWTUUTTWWWVVVWTTUTTUUUTTUUUTWWTTUUUUUUUUUTTUUUUUUUUUTTTUUUUUUUUUUTTTUUUUTTTTTTTTTTTUUTTTTUUUUTTTUUUUTUUUUTTTUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUTWWWWWTWWWVWWTTTUUUUUUUUUUUUUUUUTUUUUUTTTTTTTTTTWWWWTTUTTWWVVWTUUUUTTWWWWWWTTUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUTTTWTWTTTTTTTTTUUUUTTTTTTTTTTTTTTWWWVVVWWTUUUUTUTTTTTUUUTWWWWTTUTTTTWWVVVVVVWWWTTWWWVWWWWVVVVWWTTTTUUTTTTTUUUUUTTWWWWTTUUTTTTTTWWWWWWWTTTUUUUUTTTTTTTTUUUUUUTTTWWTTTUUUUUUTTTTWTTTTTUUUUUTTTTUUUUTTTTTTTTUUUUTTTTTTTTUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUTTWWVVVQVVWWTTUUUUUUUUUUUUUUUUTTTTTUUUUUUUUTTTTTTTTTTUUUUUUUTTTTTWWWWWWWWWTTTUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUTTTTTTTTTTTTTTTTTTTTTTTUUUUUUTTTTUUUUUUUUTTUUUUUUUUUUUUUUUUUUTUTTTTTUUUUUUUUUUTTTUTUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUTTTTTTWWTTTTTTTUUUUUUUUUUUTTTUUUUUUUUUUTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTUTUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTWWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUTTWWWTTTUUUUUUUUUUTTTWWWWTTTTTTTTTTTUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUUUUUTUUTTUTWTTUTTTTUUWTTWWTWQWTUWWWTWWWTWWWWWWWWWVVVWPQQQPPQQQQQQQQPVVWWWTTTTUUUUUUUTUTTUUUUUUUWTTTTUTUTTTTTUUTUUTTUTTTTUUTTTVWTTUUUTTWUUTWWUTUTTUUWTTWTTTUUUUUUUUUUTWWUWWTTTTUTTWUVTUTTWWWVQTTWUWWTUTTVWUUTWVUVUWTWWVUQUVUUTTUUTUUTTWTUUUUUTTTTUUTTTTUWVWUTWUWTWTVPWWWWVTUTTUUTTWTUTUUUUTUUUTWWTUVUWWVVSQPWQVQTPWVWVQVVVQTVVQPQQPWWQWTWUWUUTTUUTTWVWWTTTVWVVWWWVWUUTUTVPSRRSSSQQWTTUTWTVTQWWTP]Y[EE[[X_RQUWPR\^XZ[X_RPWTTV]^[EZZD[Y\VWUWSS\X[[^]PWV]_X[X[X^\QUVRXEFFE[^STWR^X[XXZ[Y]WREACCAGE^STTSYZDGDFAGE_PVUSXGBMBCFZ^STQ^EFAFAAGZ_QUTSXG@C@FE^SWQYEACA@@GZ\VW]YFBMOBAD_PTTQYGAC@A@GZ^QS_[@MOO@EYPTUV]ZGA@FFFEXRUWR^EACLMFZ\QUV_ZG@AFAGZ^PUP_[G@BLCF[\PTTV\[G@CACADZ\QUP\XDGABCAD^RVUQ_[GAAFFGZ^PTQ\XEDF@FDX]PTUP^EGGDDDZ^STV]Y[ZZDGEX]VUW]XEDEEGEX]VW]YZZZZEZY]VUTR^[[XZEZX]WWR_XYYXX^]VUUR_ZEEFFDZ_PWV]^[XYXY_]VTUUWWWWUP^D@@@@AG[RWUP\Y[Y^^_]PUWTWWUQ_ZGFDGZY]TWS_YXY_]PWWQQQQVTTQ\[DFFDEX\QUVR^[EE[Y_RQTTTTTUP\XZZ[[Y_STWP\YXXY_]QTTWVQVWUTQR\_^\]SQTTQS\^X[X^\SVTUUTVPSRRRRSPQWUUWVPSSSPQVWUUWVQQQVWWTUUTWWVWWTTUUUUUTVQQPSSSPPQVVWWTTUUUUUTTTTTTUUUUTTWWWTTWWWWWWWWWWTTTTUUUUUUTTTTTTTUUUUUUUUUUTTTTWWWWWTTTTTUUUUUTTTTWWWTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTUTTTUUUUUTTTTTUUUUUUTTTTTTWWTTTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUTTTTTTTTTTTTTUUTUUTTTTTTTTTTTTTTTTTUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUTTTWWWTTTTTTTTTTUUUTUUUUUUUUUUUUUUTTTTTTTTWWWTTTTUUUUUUUUUTUUTTTTTTTTUUUUUUUUUUTTTTTUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWTWTTTTTTUTUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUTTUTUTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUTTTTWWWWVVVVVVVVVVVVWWWWWWWWWWVVVVVVWWTTTUUUUTTTTUUUTTWWWWWTTWWWWWWWWWWWWWTTUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUTUUUUTUUUUUTTWWWWTTUUUTTWWWTTTUUUUUTWVVQQQPPPPPQVWTUUUUTTTTTUUTTWWWWTTUUTTTTTUTTTTUTTWVVVVVWWTUUUUTTUUUUUTWVVVVWTUUTTTTTUUUUTTWWWUUUUUUTTWWVVQPQQQPQVVWWTTUTTTTTWVVVVWTUUTTWWTTTTUUUUTVQPPPQVTUUTVVVVVWWUUWVPSRSPQWUTVQQQWWUUWVQQQVVTUTWWTUUTWWVWWTUUTTTTTUUUTTTTTTTUUTUTTTWTTUUUUUUTUTTUUTUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUTWVVQVQVVVVWWTWWWVVVVVWWTUTTWWWVVVWWTTUTWVQQQQQVWTUUUUUTWVVQQPPSSSSSPQVTUTWVQQPPQQVTUTTTUUUUTUTTTTUUTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUTUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTUTTTUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUTTTTTTTTTTUUUUUUUUTTTUUUUTTUUTUTTTTTTUUUUUUUUUUTTTWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUTUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTUUTUUUUUTTWWWWTTTUUUUUUTTTTTTTUUUUTTWWWWWTTTTUUUUUUUUUTTTUUUUUTTTTTUUUUUUUUUTTTUUUUUTTTTTTTTTTUUUUUTUUUUUUUUUUUUTTTTTTTUUTTTTWTTTTTUUUUUUTTTTUUUTTTTTUUUUUUUUUUUUUTTTWWVVVVWWWWTTTUUUUUUUUUUUTTTUTTUUUUUUUUUUUUUUTUUUUUUUUTTWWWWWWTTTUUUUUUUUTTTUUUUUUUUUUUUUUUUUUTTWWVWWWWWWTUUUUUTUUUUUTTTTTTTUUUTTTTWWWVVVVWTUUUUUUUTTWWTTUUUUUUUTWWVVVVVWTUUTTTTTUUUTTTTUUUUUUUTTWVVQVVWTUTTTTTTUUUUUTWWVWTTUUUTUUUUUUUUUTWVVVWWTUUTTWWWTTUUTTWWTTTTTUTTTTWWWVVVVVWWTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTTTTTUUUUUUUUUUTTTTUUUUTTTTTTTTUUUUUUUUTTTUUUUUTTTWWWWTTUUUTTTTTTUUUUTTTTTTUUUUUUUUUUUTTTTUUUUTTUUUUTTTTTTWTTTTUUUUUUUTTTTTTUUUUUUUTTTTTTTTUUUUUUUTTTTTTUUUUUUUUUUUUUTTWWWWTTTTWWWWWWWTTTUUUUUUUUUUUUUTTUUTUUUUUTWWTTUUUTTTTTUUUUUUUUUTTTTUUTTTUUUTWWTTUUUUUUUUUUTTTUUUUUTTTTUUUUTUTTTUTUTTTTTUUUUTTTTUUUTTTTTTWWTTUUTTTTTTTTUUUUUUUUUTUTWWWWWTTTTTTWWWWTTTUUUUUUUUTTTTUTTUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUTTTTUUTTTWWWWWWWWWTTUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUTTTTTUUUUUUUTTTTTTTWWWWWTTUUUUUUUTTTTTTTTTWWTTTTUUUUUUUUUUUTTTTTUUUUUUUTTUUUUUTTTTTTTTTTUUUUUUUUTTUUUUUUUUUTTTTTTTTTTUUUTTTTWWWWVVVVVWWWTTTTTUUUUUUUUTTTTTWWWWWWWTTTTUUUUUUUUUUUUTTUUUUUUUUTTUUUUUUUUUTTTWWWTWTTUUUUUUUUUTTUUTQS]\\__\RSVTUUWS_YXZZZZX\QVRXD@C@G[_STREMIHINOCGXPTRX@OJtuI@^WYBHuJNMCD[\US^ZABLNLCD^V^FOINMLBBC[PQ\[FMNJJNBERU]CJwtJINLLA]_EABNKJuLZQFv|rvtJHNGWWDOHHHJutKASVVTZp{ez}wHB@[POvpqtuKIOEWPFBAYVIe`cfywLEYPBrxx|vHMFZPGwptFVMdmocztZPTO~detD\QVFsy|u\Tylhn`}ET_|gfes@QVtxeyuQXdijh`s\[~faepEVJzJQPxlki`r_SsdfdrAO}xuR}mkhbyDvdafyITDq~ypAu`hkldHL{ffzw\^t~{}IVRxlhnapSVqdfdrFApxypAKaihleICygf{w\QKydzsDCgoimeKTZ|dg{wY^vxzHPAzbmaxK]UYpx{|tEVTUPJ|y}uXAya`gyw@Z\V^t}|stBZY]STBp}}vLXRQQVDgafzsKA\TVOvrrquLF^SXtr|rtMDY]VBgaferH[UPOq||su@\Ewx~pIZQEvxee~vCRXOtwuL[PDupsvIZQUAqxyrJDQUGJqpwIG]WPBvrrvIERWWGw|y~pM]TAtsrqHEVTAw}|pJGQ^NvsqHXUTXMJKM[P[HwvuC^TSCJtJ@XRVQPT_GBBDX]QVUSFHtuNF_VT[LKJNA[\PVPEMIOCZ]V_AONBE\QUR[EZY]QVWTTP\^Y_SVTUVR__\RQTW][EDZ^]PVWTTUUTQR\\]SQWWVQQQVTUTTTUP\^^_]SPQVTWPR]]RSQWUUTTTUTWVVQQQQQQVVVVVVVQQQQQVVVVWTTUTWWVPSRRRRRRSPPQVVQPSR]\\____\]SQWWQS\^X[EEDDEZX^]SQVVVQPSR]]]]SQWUWQR\^YX[[X^\RPWWP\XEFAAFD[Y_\]]\\\\\RSQWUTWVQPSSSPQQPPQVWWTWQR_ZF@CAD[^]SPQPPSSRRRRRSRRRRRRSPPVTWQS]\^XZEGGFGDZ[XYX[ZZEEEZ[Y_]QTUUTTTUUUTUTTTTTTTTTTWWTTTTTTTUTTTTTTWVVQPPPSSRRR]]]RRSPQVWWTTTUUUUUTTTTUUUTWWWWWWWTUUTTTTTTTTTTTTTTTTTWWVVVVVVVWWWWWWWWVVVVVVVVWWWWWTWWWWVVVQQQQQQQQQQQVVVVVVQVQVVVWWTWTTTTUUUUUUUUUUUUTTTTWWWWWWWWWWWWWWWVVQQQQQQVVVVWWWWWWWVVVVVVVVWWTTUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUTTTTUUUTTUUUTUUUUUTTWWTTUUUTUUUUUUTTWWWTTUUUTTTTTTTUUUTTWWWWTTUUUTTWWWWTTUUUUTTWWWWWWTUUTTTTTTUTWVQQQVWTTUUUUUUTTTTTUUUUTUUUUTTTUUUUTTTTTUUUUTTWWTTUUUTWWWWTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUTTTTTTTUUUTTTWWWTTTUUUTTWWWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTTUUUUUUUUUUTTUTTTUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUTUTUUTTUUUUUUUUTTTTTTTWTTTTTTTTTTTTUUTTTTWWWWWWWWWTTTTUUUUUUUUUUUUUUUUUUUUTTTTTUUUTUUUUUUUUUUTTTTUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTWTTTTUUUTTTTTTTTTUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUTTUUUUUUTTTTTTUUUUTTTWTTTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUTTUTUUUUUUUUUUUUUUUTTTWWWWTTTUUUUTTTTTTTTUTTUUUUUUUTTUUUUUUUUTTTTTUUTUUUUUUUUTTTTTTUUTTTTTTTUUUUTTUUUUUTTUUUUUTTTTTUUUUUTTTUUUUUUTTTTTTTTTUTTTTUUTTTWWWTUUUUUTTWWWWTTTUUUUTTTUUUUUUUUUTTUUUUUUUUUUUUUUTUUUTWWWWTTTWWTTTUUUUTTUUTTTUUUTTUUUUUUTUUUUUUUUUUTUUUUUUUTUUUUUUUTUUUUUUUUUUUUUUUUUUTTUUUWVWTUUTWVWTUUTWVQQVWTTUUUUUTWWVWWTUUUUTTWTTWWVWTTUUUUUUUUUTUUTTTTTTTTTTWTTUTTUUTTTTTTUUUUUUUUTTUUTTTTUUUUUUUTTUUTWVVVWTTWWWTTUUTTTTUUTWVVVWWTTTTTTTTWTTUUUTTTUTTTUUTTUUUUTTUUTTTTUUUUTWTTUTTUUUUUTTTTTTTUUUTTTTUUUUUUUTTUUUUUTTUUTWWTUUUUTTTWWWTTUUTTWWWTWWWTTTTTTTUTTUUUUUUTTTUUUTUUTTTTUUUUUTTUUUUUUUUTWTTTTTUUTWTTUUUUUUUUUTTUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTUUUUUUUTTTTTTWTTUUUUTTTUUUUUUUUUUTTTTTTTTWWWWWTTTTTTTTTTUUUUUUUUUUTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTUUUUUUTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTTTTTTUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTTTTUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUTTTTTUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUTUUUUTTTTTTTTTTTWWWWWWVVVVVWWTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUTTTTTWWWWWWTTTUUUUTTTTTTTTTTTTUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTWWWWWTTTUUUUUUUUUUTUUUUUUUUTTTTTTTTTTUUUUTTTTTUUUUUUUUUUUUTTTTTTTTTUUUTWWVVVVWWTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUTTTUTTTTTUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUTTTTTTWWWTTTUTTUUUUTTTTTWWWTTTTTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUTUTTTTTTTTTTTTTTUTUUUTUUTUUUUUUUUUUUTTWWWWWTTTUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTTTTUTUUUUTTTTTTTTTTTTTTTTTUTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUTWQSR]]]]RSPVWTUUVP\]S\_RPWPR_Y^YX^]STUWP]RR__\\PVVTUWWPR_XXYXX^\PTSYDFGFAGZ_QWUPXGG@MBAD_SQTV^[\XDZDE]\^WT\\^GAF@EX[\WQ_@CAOJJLGX]PRSZMNuJMCA_WWT[OIuJ@NLRS[AO@GMBXRVQ]_RQZv||y|tH^QZJtuKBZ^WP_^XZYV^EtzdzyqIEU[BursHAEVYGGACAZS_LKxfgxpKAV_BwsrJEZSXCFG@FYQVCA|fgypuOQQZwpvIEERP[CAF@E]VTMG|agstL]SGsrwK@ZQYFCMIO[]SM@}afyrvNQSEprtKMZVZGLKuI[\_LBsaaz|vJUUSYv}wuHF]]ZBMNNZ]\WGZwfazpv[V]JrvuuMX][ANHH@^_SYAJg`d{}vL\MspwwO[QQ@]BvJM^SGVMOrffx}tMYGJswO@_YCMwvuM^RSUHJ~afxpNFTZCwrqKCYVR@IJwtOY]SBOqzeypLFW[ELwtOA\V]OKLJJESV[Nv~{qNETR^FHNCF^V_FFDGXSV\ELp|ptND\V_EGDEZRTR^DAFFXRQTW^YOvqwO[_QX@AF@D\QWS^[ZX_]RSVUS_FJqpvMZ_VUYGAAD[XSWPS]^^_\RRRVWYGLJuwJG_QPYEE\SPTWRYEZY\_]PTQ\XEEF@XSUUQ]YX\QWUTS\_\]SQVW]XZ[ZGEX_PUUS\\]SR]]RSPWUTVQPWUSYDFDZY_\STV]YXX[Y^]QUWS\_\RSQWTUUWWVPSSPVTTTUUWVQVVVQQPSS]\\]SQVWUUTTUTTTVQQPQPPQQVVWWWWWUUTUUVVVVWWWVQPPQVWWTTWWVSR]\RPQWTUTWQPPPQVTTTWVPSR]\^^YYY^__]RQVWWWVP]^[EZXY\RSQTUVPR]RRSSPPQWWUUUUUUTTVPSSQWTUUUUTUUUUUTUUUUTWTTUUUTTTTTWWTTUTVQQVWVVQQVTTTWWTUUTTTUUUUUTWWTUUTTTUUTTUUUTWQQVTTUTWTTTTWVVVWTTWVVWUUTWWWWWWTWTTUUTWTUUUUUUTTTTUTTUTTTUUUTTUUUUUTWWWTTTTTWTUTWVTUTWTUVVWUTQVVTUTTUUWQQWUTTUUWWWTTWWWWWWWTTTUUTUUTTUUUTTUUUUWWUTTWTUUTWQQVVWUUTWWWTTUUWQQVTUUTWWWTTTUTVVWUUUUUUUUTTTUUUTTTWTTUUWVVWUUUWTWTTTWTTUUUUTUUTTWWWWWTTTTWWVWTUUUUUTTTTUUUUUUUUUTTTUUTTTTUUUTTTUUTTTTTTWVQQQVWVVVVWWTTWTTUTUUTWWWTWWWWTTUUUTTUUUUTTWTTUUTTVVQQPPQQQQVVVWWTWWWWWTTUUUUUUUTTWWWTTTTTTTUUUUUUUUUTTTUUUUUUTTTTTTTTWTWTTTUUUUTTTUUUUUUTTTWTTUTTWWWWTTUTWWWWTTTTTUUUUUTTTTTTTTTTTTUUUUUUUTTTTTTUUUUUUUTUUTTTTUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUTTTTWTTTTUTTTTTWWWVVVWWTTUUUTTTTTTTUUUUUUUUUUUUUTTTTTTUUUUUUUTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQQVVVWWWWWTTTTTUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTUUUUUUUUUUUUUUTTTTUUTUUUUUUUUUUUUUUTTTTTTTTTUUUUTTTUUUTTTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWWTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTTTTTTTUUUUTTTTTTTTTTTUUUUTUTTTTUUUUTTWWWWVVVWWWTUUTTTTTTUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUTWVVVVVWTUUTWVQVVWTUTTTTTUUUUUUUUUUTTTTUUUTWWWWTTUUUUTTTWTTUUUUUUUUTUUUUUUTTTTUUUUUUUTTWTUUUUUTTTTUUUUUUTUUTWWWWTTTUUUUWWWTTUUUUUUUTTWWWTTTTTTTTTTUUUUUUUUUUUUUTUUUUUTTTTTTTUUUTTUUUUUUUTTUTUUUUTWWWWTWTTUUUTTUUUUUTTTTTWWWWTUUTTTWTTTUUUUUUUUUUUUUUUUTTTUUUUWWVVWWWTUUTTTTWWTTTTTTUUUUTTWWWTTUUUTWVVQQVVWWWWTWTTWWTTUUUUUUUUUUUUUUUUTUUUUUUWWVVWWTUUUTTTTTUUUUTUUUUUUTUUUUUUUUUUUUUUTTTTUUUUUUUUUTTWWWWWWWWWWTWWWWWTTTUUUTTTWWWTTUUUUUTTUTTTTTUUUTUUUTTTTTTTUUUUUUUUUUUTTTTTTTTTTUUUUUUUUTTTTTUUUUUUUUTTTTTTUUUUUUUUUUUUTTTTTWWWWWWWWTTUUUUTTWTWWTWTTTTTTTTTTTTTTTTTUUUUUUUTTTTTUUUUUUTTTWWWWWWTTUUUUUTTTTTTTUUUUUTTWWWWWWWTTTUUU \ No newline at end of file diff --git a/test/support_test/chip.g711a b/test/support_test/chip.g711a new file mode 100755 index 0000000..d770a55 --- /dev/null +++ b/test/support_test/chip.g711a @@ -0,0 +1 @@ +UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTWTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUWUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUTTUUTUUUTTUUUUUUUUUTTTUUUUUUUUUUUUUUTTTTTTTTTUUUUUUTTUUUUUUUTUUUUUUUUUUUUUTTTTTTUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUTTUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUTUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUTUUUUUUUUUUUUTTTTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTWWWTWWWTTTTUUUUUUUUUUUUUUTUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTTUTTTTTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUTUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUTUTUUUUUUUUUUUUUUUUUUUUUUSQLUTRPQ\VUQTXM\WSQSQ^XTYWPWURWVSQZWSWWTS]WQUWVSWTRQTVTQVUUUWRQVUUWQVVTUTTQWUVTWTTWWTUWTTTTTTUUTUTWTTTUUUTUTTUUUUTTTTTTUUUUUUUUUUUUUUUTTWVVWTUUUUUUUUTTUUUUUUUTUUUUUUUUTTUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVQPSSSPQQVVWWTTUUUUUUUUUUUTTTTTTUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVQQQVVWWTTTUUUUTTUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUTWVVQQQQVVWWTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWWVQQQQVWWWTTTUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQQVWWTTTUUUUUUUUUUUUUUTUTTTTTTUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQPQVVWWWWWTTTUUUUUUUUUUUUTTUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUTWVQQPQVWWTTTUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUWVQQPSPQQVVVWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQPQVVWWTTTUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUTWVQQPQQVVWWWTTTUUUUUUUUUUUUUUUUUUTTTTTTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTTTTTUUUTWVVQQQVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUTWVQQQVVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQVWWTTTTUUUUUUUUUTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUTTTTTUUUUUUUUUUUUUUUUUUTTTTUTUUUTWVQQPQQVVWWTTTTTTUUUUUUUUUUUUTTTTTTTUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTTTUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWWVQQQQVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUTWVVQPPQQVVWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUTTTTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUTTWVQQQVVWWTTTUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUTWVVQQQVVWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQVVWWTTUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTUTTTTTTUUUUUUUUUUUUUUUUUUUTWVQQPPQQVVVWWTTUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVQQQQVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTWVQPPQVVWWWTTTTTTUUUUUUUUUUUUUUTUTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTVVQQPPQPPPSSSPPPPSSSPPPPSPSPPPPPPPQQQQQQQQQQQQQQQVWTUUUTWVVQQQQPPPSPPPPPPSSPPPPPPPPPQQQQQQVVQQQQVVQVQQQVWWWWVVVWTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVQQQVVWWWWTTTTTUUUUUUUUUUUUTWWVVQQVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVQQVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUTTTTTUTUUTTTUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUTWVQQPQVVQQPPPPPPPPPSSPPPPPPPPQVVWTTUUUTWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVQQQVWWTTUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWWVQPPQQVWWWTTTTUUUTWVQQQVVWWTTUUUTTWVQQVWWTTUUUTWVVQQQVWWVVQQPPQVWWTTUUTTWVVWVVQQQPPQVWWTTUUTWWVWTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUTUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTUUUUUUUUUUTTUUTUTTUTUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWTTTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUTTUUUUUUUUUTUUTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUTTWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTTUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWTTWWWWWWWTTTUUUUTTTWWTTTUUUUTTWWWWWWTTTTUUUUTTTWWTTWWWWWWWTTUUUUTTTTUUTTTTWWTTWWWWWWTTTTTTTTUUUUUUUUTTTTTTTTUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWUUTUUUWWWTUUTUUUUTTTTUUUUUUUUUUUUTTTTTTUUUTTWWTUUUTTTTTTTTTUUUUUUUUUTTTTTTTTTTTTUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUWQSPQQVWVWWWVVUUUTTTWWVQQPPQWTPVTWTWWWWWVVQQQVVVVVWWWWWWWTTTUUUUUUUUUUUTTTTTWTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUTTTUTUUUUUUUUUUUUUUUUUUUUTWWWTTTUUUTWWQWTUTWVVTUUTTWTTWTUUTTUUWTTTUUWUTTUUUTUUTTTTTWWTTUUVQPPQWTUTWQPWUUTTWVWTTTUTTUUTUWTVVQQS]_\]RRPQWTUUUUUTQTUTTTUUWVTWTWTWWTWTTWVQWWTWWWUUUTWWVVVVWTUUUTTWTWWVWTUTTUUUUTTUUUUTUUUUTWWVWWTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTWTTTTUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUVTUUUUTWTUVUUUUUUUUUUUUUUUUUUUUUUUUTWTTTTTTUTUTTTTUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUTTTTTTTTTTUUUUUUUUTTTUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUTUUUUUUUUUUUUUUUTUUUUUUUUUUTTUUUTUUUUUUUUTTUUTUTTUUUTTTTUUUTUUWTTWVTUUUUTTTUUTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUTTUUUUTTP4FZUWVZUQR^_EISRR_SZ_DPXW_UWQU_S]QPQVQWVW]\^PPVSVVQSPRPPSQSVVVQQPPRSWUTWTTTVQQVVWWUUWVVVVVVVVVVVVQSSSSPPQQVVVQVVVWWWTTTUTTWWTTTUUUUTTTWWWWWTTTUTTWWTWTTTTTUUUTTTTTTTTTTWWWWWVVVVWWWWWWWWWTTTVTTUUUUUTUUUUUUUUUUUUUUUTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTW`]TSWPRRPWQTTWWTTWQPSPQVVVWUUTVQQVWTUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUTTUUUUTUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTUUUTTTUUUUUUUUTUUUUUUUUUUUUUUUUUUUPW^QTQTVWWWSP_SQTVWQPSVWPTVQVVUTQRWWUVWWVWVUUUTTUTWVWWTTUTVWTUUUTUUUUTTUUFutuHOB@GEEZX_SPPSRRRPQVWTTWWWTTUUTTTUUUUUUUUTTTUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUTUTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUTTTTTTTUUUUUUUUUUUUTWVVVVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUTWVQPSSQQVVWWTTUUUUUUUTTTUUUUUUUTTUUUUUUUUUUUTUUUUUUUUUUUTWTUUUUUUUUUUUUUUUUUUUVYRTVTQWWWSTTUTVW\]WUQQWUTUTTVTUVWTUTTTTWTUTTWT\RUUWVPVUVWVWVQPQWTWVWWTWUWUVRVUUUTTTTWTTUUTTTVQQWTVQWWWUUTUUTWWTTTTUUTTUTTUUUUTUTWTTUUTTTUUUTTTTWUUTTTTWUUUUTWTUUTTTVTUTWTTTUUUUUTTWTUTTUUUUUUTTUTTTWTUTTUTWVUTTWUUWTTVQWUUWWTWWTUUTTTVWTTUTWTUUUTTWWWWTUUTTTTUUUVQWUTWTTWTUUUTTWTTWTTTUUUUUUUTTUUTWWTUUUUTTTUTUUUTUUUUUUUUTUUUUWWUUUTTUUUUTTUUUUTTTUUUUTUUTTTTTTUUTTTUUUUUUUUTTTTTUUUUTTTTTTTUUUUUTTTTTTTUUUUUUUTTTVVVWWWTTTTTWWWWWTTTTTUUUUTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTWWTTTTWWWWWWTTTTTTTUUTTTTTTTUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUTTTTTTTTTUUUUUUUUUUTTTTUUUUTUTTTTTUUTUUUUTUUUUTTUUUUUUUUTUUUTUUTTTUUTUUTTTTUUWWUUTTTWWTUUTUUTTUTTUUUUTTUUUTTUUUTUUTUWTUUTTUUTUUWTUUUTUUUTUUUTUUUUUUUUUUUUUUUUUUTUUTTTTTUUUTTTTTTTTTTUUUUTTTUUUTTUUUUUUUUUUUUUTUTTUTWTUUUUUUUUUUUTTTTTTTUTTTUUUUUUUUUUUUUTTUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUTTUUTTTTTTTTTTUUUUUUUUUUUUTTUTTTTTTTTTTTUUUUUUUUUTTTUUUTTTTTTWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUTUTTUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTTTTUUTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUTTUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUTTTUUTTUUUUUUUTTTUUUTUUUUTTUUUTTUUUTUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUTUTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUTUUUUTTTTTTTTTTTUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTTTTTUUUUTTTTUUUUUUUUUUUUUUUTUUTUTUUUTTTUUUUUUTTUUTTTTWWWVWTUUUUUTTTTUTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWTUTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTWWWWWWUUTUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTWWWWWWWWWWWWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUTUTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTWWWWWTTTTTUUUUUUTTTTTTTWWWWWWWTTTWWWWTTTUUUUUUUTTTTTTTTTWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTUUUUTTTTUUUTTUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUTTTTTUUUUUUUUTTTTTUUUUUUUUUUUUUUTTTUUUTTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTTUTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUTTWWVWWWTTTUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUTTTTTTWWWWTTTTTUUUTWWVVQVVVWWWWTTTUUUUTWWVQPSSPQQVWWTTUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTWWVVQQQPPQQQVVWWTTTUTTWVVQPPSSSPQWTUUUUUUUUUUUUUUTTUUUUUUUTTWVQPPSPQWTUTWWWWTUUUUUUTTTTTTUTTTTTUUUTWVVQQQQQQVWTUUUTTWWWWVVWTTUUUUTUUUQR\_\]]]]]RQTUTWQPSSQTUUUTTTWWWWTTQR__]SVUUUUTVQQVWTUUUUUUTWWVWUW_ZAFXRTUUWQPQWUTWTTUUWQQVWTUUUTR_ZZ_PUWVQVWWVWWTUWVTTTWWWUUWQVUUWVQSSSPVTWWWVWTUUUUTTTWWWVQVVVWWWTTTTUUTTWVWWTU]DGF[QPSSSQVVWVPQQVWVVUTWWTTUUTTUVYDGZRWSRRRQWVSPQVWWWUUTWWWWVWUUTWTT]DDE_TUSRSSPTUWQPVVVWTTUUWQQVTUTWWU\GGD^UTR]SRSTWVPPVVWUUWUUWWQSPQQWUUUTRDDZ\WS\RSPTUQQQPVTTUUWTUWWWQSPVTUTTTTT^FDXRWVR\RSQTVWVQVTTUUTQVWWTUUTWQPQWUUUQXZY]VUTVSRSPTWVVPQWUTTTUTPVUUUWURDEX_VTVSPSVPQQSPVVTTTUUUUTVQPQWTTUUTWVQVUUREEX\VQ]]SQVVPPVUUUVVPQTTWWWTUUUSX^]SVWSQWTWVVQWUUUWQWTUUTTWSX[\SVUTSRPVTTVVQQWTUUUTUUUWPQWWUTWWUUV\ZX_SWUTP]]SQTUVVQPQVWUTVQWTTWWUUQXZ_\RVTR]PQQUTWWQPQVWUUTWVVWTTTUTTUW\DY\]QUP]SQPWWTWPPVWTUUUUWQQVWUUTUUW\DY\_PUQRQQSVTUWPPWWTUWQSVTWUTTUUTURDZYY]TWPQPSQUTQQVVVWTTWVQWWWWTTTUUUUT\D[YX_VWVVQSPTUTTTQSSPPPQWUTWVWQQVWTTUUVXEYZ[SUTTVSSWTUWVVQPQVWUWVQQVVTUUUUUUUURZXY[\VTUTTQPVTTUTTVVVVWUTWVVQQVTUUUS[XXE^QWTVQVWVTUUWQVQQVUTQVQSQWTTY[YGE]PVUUWPVWQVTTUUTUWVVQQVWUUWVQQQVWP[Y[F[RSVUTWTVPVWWTUUTVVWTTTWVVTWT\^^GD\]SUWTTQVTTTWWTVVUTUWUUWTUTWTTTUUV^]X@Z\_STUUWUWQVTWTUUTUUTTTTTWVWWVWTTWTUTUUW_][CE^Y]VTUUVSPQPQWUUUUWTTVWQPVPSVVQWTWTUU\SYMGYZYQTTPQQSSVTUTTTWQSPQPPWWWTUW]QGOEZF^PQUVPQR_]SQQWUVPPQPQVWWTTVV@B[A@_RPTWQ\^\]RSVWQS]RRRPVVUWQBGXCA_\STQ_^_^^\PWTTWPR]RRRQTUTUFD^@CYX^SWUS]_X[X^\]VVPPRRSPVUTYF_EBZYX\QUVP]^Y^^^]QTUTVPQVVTQFYXBG^[_PTUVR_^Y[X\]PUTWWTUA[[IB[FZ]WV]YYXG[\^RUWWUD[\NIGMCE_VVQ\XXFCXZEQTTTVTTTXTXHFAI@G^]QTP^YGG[D[PPWUTUTUS^BXAO@FXYSV][DE@GY^RUWUXD_@BFFZ[RQ]XXGGYX_WTPT[P_AZDEZXPVWTQ]^ZEXXYPVWQ]RT[X^ZXX]QQWV_Y[ZXY\QVVQR_\XS^_\RVVUTWS_^Y^^]PVT]^STY]S_]RPQVWWP\^^YX_RSWQ\^[Q^XP\]PVTUVV\^Y[XY_RPTRSZR^D\^YRSQTUQ^]\[\R_QUTVTQ\S]^]\\RSQWTUQPVRSVPWTRRR^\\\RPQWUVWQSPSSQQWWTQ\R]_SSSWTUVQV]RP_RVSWTUTPWWPWWQWTWTTWWVVQQQPPQQQWWUUTTWVWVQQQQVVVVWWVVVVWTTUUUUTTTWWWWWVVVQVVWTUTTWTWWWWWWWWWWTTUUUUUUUUTTUTTUUUUUTTTTTWTTTTUUUUWTTWTTTTUTUUUUUUUTUUUUUUUUUUUUUUUUUUUUTTTTTUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTWTTTTTTUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUTTWVVVVWWWWTTTTTUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUTTTTTTUUUUTUUUUUUTTTWWWVWWWWWTUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTTTTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUTTTTTTUUUTUUUUUUUUUTTTUTTTTTTUUUUUUUUUUUUUTTTWWWWTTTTTUUUUUUUUUUUUUUUTTTTTTTTTTWTTTTTUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUTTTTTTTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWWVVWWWWWTTTUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUTTTTUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTWWWWWWWWWWTTTTWWWWWWWWTWWWTTTTUUUUUUUUUUUUUUUUUUUUUTTTUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUTTTTUUUUUUUUUUUUTTTTTUUUUUUUUUTTTWWWTWWWWWTTTTTTUUUUUUUUUUUUTUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUTTUTUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUTTTTTTTTTTUUUUUTVQPPPQVWWWTTUTWVVWWWTTTUUTTTTTTTTTTTTTTUUUUUUUUTTTTTUUQR\_\]RSPPQVTTWVVVVVVVVWUUTTWWWTUUUUUTWVQQVVWUUUUUUUUUUUTQR]\\RSQWTUTWVVQVVVQQQVTUUS[GFG[\PVUTQRRSPVWWTUUWWWVVPSRRSQTV_FMBFXSTVR]\SWUTWWWUUUTWVWVQQRRPVQZIwJBYUQ^Y]QUVPRSTUTVR\]PTWVWU_DNKCXQTUUVS]\SVVQQVPRPVVSPTUWSRQTUTUPAKtI[UWVQS\\PUWQQPR]]PWS\]RRQTTVPQTUUDNtuFVVR\]R_]WWR]PSRRSVP]\\SVVWTTUUTUTUUUVBtwHYQS\^Y^PWSSSPSRQTPS]^_SWWQPPVTTUTAtwHXS\Y[X\WVRRRRSSWTQR\]SWUQPPVUTTWUSOwtMRTS_[EXPWR_\^\RWTVP]\SPUUQSQVUTTVWTGutNXS_ZGESUP__^_QTUTQS]PQWUTWVWWUUUYJtNDVV\XEDRW\^__PUWQQSQWTUTPQPPTWFuKMYP_XEXUV_^^\VVQQQVVWUVVQSPVWU]NtHC]\[GG_SXY^]VTQVPQTTTTPRRSVVWUU\IuOGR_ZDDR][X^\QTWQQSPWUTTTP]]RRPQTTCtN@XURZZD^TUQ^__Y\VWWVSSQTUUWP\Y]SQTUVWUQMH@GXR^\X\WTUSS]^STWWPVWQWURSVRRWUTYHNBCXSS][_PWQS]_]RSTQRSSRWUTWVVSPQVVUW@OCCA\TUTW]^]QTVQS]\\SUUVP]]SRSWTWTTWVQPQWTU]B@@LARUTR]PQQTV]\RPVUUVSQQPSWUUVVWWWUXX[MND]UWWWVPSVUVVQSPWUTWPRSSPVTUUVXX@HLZ]VUTS]]SQQTUWPPQQQTUTPPPSPVT\SZHOG[\STVQRR]SVUUTWVVWVVWVWVPQVQVWTUUUPPENMFDX\WTQR]RRPWTTWVPPPPPQVWTUUUUUUVTPBOFFDXRTPRR_\PWWUTP]RRRSQWUUUZL@ABFXPWUVS^[^\]STPSR_^]SQWGADCMF[\RUPYXY[[\PVTQR\\\]RPWUPEYXAFEYY_TQ]]YZZY^\QUVSR\\\SQWYE_ZFZY_^SVPS^XY^^]QWTVQSRRSQWWGZX@@EXY^VVVR^Y^Y^]QTUVPSPPQWTEY^FGZYY^VUWS^Y^XX\SQTWQPPPQWVEYX@AE[X^WUTR_^Y[Y_]PWWQQPPVTWX_XB@DDEXPWTVR_YX[X^]SWTWWUQ\S[CFFFG[RQUQ\^XZZ[^\PTUTWWTUPVSDGEFGE^RQVR\YXXY_RQUUTTTWWVYE[GGE[_SWTP]_YYY_]PWTVQ_[XDE[X_SWQR_YXX^\SVUUVP^X[GD[[^SWVS\_YX^_RPWUVS_^XEZXY\SVVS\_^^\]PWWS_YY[Z[X_RVTUVS]^YY^\SVUWP\YX[[XY_SWUWP]_^^_]PVUTVR^X[Z[Y_]QTWPR\^Y^_RQTUVP]_XZZ[Y_SQTTQS\^^^\SQTVS]_Y[EEZX\PWUWQS]_^^\SVUTP]_^YXZZ[Y]VWPR]___\]PWP\^YYXX[X^RWUVR\^^^_]SVUUV\XZ[XYY^_RWUVS_^Y^_]PVUUTWWWWUPXDGGZY^_\RWTVS]_^^\RQWUTTTTTTUVYGAADY_\]SQUWVQS]\\]PQWUTVVWTUUUUU]ZGGEX\RPVTTVQPS]]RSQWWTUTWWUQYF@FZ\PVWUWQPQQSSPVTUTTUWWWU]DBCD^QUUWPSPQVVQVTWVQVWWUU\FMCE]TWPRPVTTTWTUP]RQUUTTUW^GB@[]WTPPPVUUUWVS]SWTTTUSZCCE]WUWSRSVUUTTTUVPR]STTTWWTU_GBCXPUUWQRSWUUUTWQSPPWUVVWT]DC@ZPTPSRRWUUWVPPPVTTQQWWUW[@MF]UWS]\]VUTWQPSPWTUUTWQWTTPDBBEPTQ\_]PUTVPSPPVTTTTTWWTUURFB@XVWP__RVWQSSPVTUTUTTTUUXCCG]TQ\^]QUTQSRPWTUTUTWWTTSGCA[QVR^^RVVPRSVUUTUUWWTUXFADSWQ_YRVUWPSRPTTTTUWWTUTWVVWTUUSGCAXVV]X_PTQR]RVTTUWWVWTVVTTWWWV_F@FYVTUP_X\PWWPRRPWTTUWWVWTWTTTTUV[GG[SVV]Y\PVQPPQWTTTTVQWWWUUUUYFFD\QTTS__SQWWPPPQVWUWWQPQWWUTTTUQZ@AD\SUQ\Y_]PVVPSSQQVUTWQQVWWTUTUUU]F@FXSVP_Y\SQTTPSSSQVWUVQQVTUTZC@E\PTW]^\SPVWQPSRPVUVPQQVTTUUU_ACAXRVP_^]RSUUVQR]RSVUP]SPPWUUWWUUX@AFXPQTP\^RSRUWVS]SQWTQPQQQWUTWUUXCCAZ\VUP]\\]RTVR]RPVTWQPSPQWUTTUWGB@DXRWVSR\]\PUQR]PVWTTVPSSPTUUUUEB@AZRTWQR__\RUVR\SPPTUTQPPPWTW_CCCF^QVQ]__^VP\RPSQUWPPSQWTGC@CF]VTWV__Y\TVR_RR]PUTQRRSVTWVTVCC@MG]WTV__Y\WS]QR]VVSPQVWQWUE@@BB^P\_Y^QUVRPR^SUTQQWQVQVDG@L@YPR\^]TTTVQQ]_SUVWTVVVT\FDBNG^UW\\_SUTWUTVQS^_VTVTWVQVUPDZ@NAYWR_\RVVQTTWQRYYSTUTWWWWWTUYFECIFXTUR_RSVPVUVR__PWTWWWVVVTU[^XB@XRTSSQQPPVRRSWTUUTTWWTU_X\GME^TVSQVP]WSRPPVWTTWWTTUX_YBCX]TVQVVRRWRPQPQVTUTWWTWX\YCCX\TTVQQ]]TUWTWSPQS]STUWWWVVX\XMCX_UTVQS]TWVTTQVQR\PVWTUUTWVWUVX\EO@[_TTVQRRWQQTTWTVRRVWTUWWU^R^MB[XQTUUWQRVQPVWPSVQVUUU_P^MB[ZRQTUWSQPSPTUTVPVQQWUUU]V^B@[E\SWTSPR]]QVTTWUWVVWWTUUUUUUPTXBD[D^]VQVP]]PQWQWUTTUTTTTTUQRADYEX\QWTTTP]SQQPQWWTUUUTUUWYAXXDY]PQTWR\PRRSPQWTTUVVDFXGF[_]STUV]RS\\]SPVTUTTZE^EGY\RRUTRRS__\SRQT]E^XFZ^\_QVSP\^_]\]VUQY\^GEX^^]TTVQVR]]SSPWU_\S_^SQPPUTTTVPQQSPVVVT]]P]\PQQQTUWUTUUWWVPQQPVVWWTW]SS\RPQQQTTWTUTWWPPPSPQQVWTVRSP]SQVQVTWVUUWTWQSSSRRSSPQVWWUUQPQPPVVVQVVQVWWWWQVQSPQPQVVVWUUQPQQQVWWVWTWWTWVQQSSPPPPVQQVWWUTVQQQPQQVQQQPPQQQVVQQQQQVVVWUUTWVQQQPSPSPPPPPPPQQQQVVVWTUTVWQPPPSSSSSSPPPQPPQVQVWTUTWWQQPPSSSPSSSSSPPQVWWWWTUUUWWVQPPSSSSSSSSSPQQVVWTTUUTWVQQPRR]]]]RRSSPQVWTUTTTWWVVVQVVVVWWWWWTTUUTTWWWWWWWWVWWWWTUUUTTTTTTWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWWWWWWWWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUTTUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUTTTWWTTTTUUUUUUUTTTTTTTUUUUUUUUUTTTTTUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUTUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUTTUUUUUUTTTTTUUUUUUUUUUTTUUTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWTTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUTTTUUWTTTTTTWUTTUUWWUUTUWQUVTWUTTWUTWWUUUWUTUTUWTUTUTTUTTUUUWUUTTUUWWTTWUUUTTTTTWWTUTUUWQTTWVPTTUUUUTWWVTTUTUWUUWUUTWUWUTTTUTUUTUTUUUUUUUUTUTUUUTTUUTUUUUUUUUUUTUUUUUUUUUUTWUTUUTUUTUTWWUTUWTUTUTUUTTUUUUUUUTUUUUUUUUTUUUUTTWWUUUTTUUUUTUUTUTTTUTUTUUUTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWUUTTTUTUUUUUUUUUUUTUUUTUUUUTUTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUTTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUTTTTTTTUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUTTTUUTUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUTTUTUUTTUUTUUWWUTTUUUUUUUUUUUUUUUUUUUTUUVTUUTWUUTUTTUUUUTUUUUTTUUUUUUUUUTUUUUUTUUUUUUUUTTTUTUUUUUUUUUUTTTWWWWWTVWWWWWWTUTTTUUUUUUUUUUUUTUUUUUUTTTTTTUTTTUUTUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUTUUUUTUUUUUUUUTUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUTTTTTTUUUUUUUTTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUTUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUTUUUUUUTTTTTUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUTTWWWWWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTUUUUTTTTTTTUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUTTTTUTUUUUUTUUUUUUUUUUTTUUUUUUUUUUUUUUTTTWWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUTTTTTUTTTUUUUUUUUUUUUUUTTTWWWWWWWWTTTTTUUUUTWWVVVVVVWWTTTTTTTTTTTTTTUUUUUTTTWTWVWVWVVWVWWWWTTUUUWWVPQPPPQVVWWWWWTWTTWWWWWTTTUTQV\P\_]^R\PPPWWUTUTTTTUUUUTTTTTTUUUWTQPQRSSSSQVWWUUTTWWVWWWWTTTWWRSR\\\\\RPQVUUWUUWUUUUUWQRR]^_\_]SPVTWTWVVVWQWWWTUTTPP\^\Y[Y^Y]RQVUTUWQVPPPSPPQVVTWWQ]_XX[DZY[]PVTSWR^P_^R]]SQVVWV\][EZDAX[YSQWSSS^^\Y_]RRPTTUWQ]_XFFD@AY[\VUTWR]RX__Y]\SPVWS]XZ@CFBCYZ_WWPR^^^Z^_^RPQTU\R[EAL@@LEX[VTWQ]]XZ_Z[]^\VWU\\[DCLBCLEYXWVR]_DYXD^_\RWUU^_ZFMLCBB^^\TP]]YD\[ZRRRW]YXFBLLCLF\YWS]R[Z^[X_RSQTVXYGBOOBMB_^RP\\YEXYX^RPQUUU^ZEBNIILOA^YWT]_^ZEY[^]PQUTWTU\[E@NIOLLG^_TV\_XEZZXY]QVUUTVQVTUSZDCNHIOOA^^VW\^YED[XX]QWUVQVWTTXEAOKHNOBX\PURYYZDEXYYQTWTWWPPVWT\DFMKKINOG\RQ^Y[DDZXY]VVTVPSPVWVEG@IKINL@Y]VW\YXEDEXY_VWUWWQSPVWYGAOKKIOBZ]PP_Y[DEYY_QWTTVPQVV\DDBHINOME]SP_Y[DZXY_PVTUWVPPVWRGFBKKINLF^RUUP^X[DD[X_RQTVQSSSVTUXGFOHINLC[]QTR^XZDZ[Y]SUTQQSRRSVWWXFAOKINL@X]VWR^YZE[Y^RVTWQPR]RSPTUT^FFMHNLM@[RQUS_^XZXY^RQVUWQPSPQVUT^GFMIOLBAXRVTS_^X[Y^_SQWWWQQVVU\ZDCOLLBAZ\PQ]\YXY^_RPVUTWVVVWUP[ZFLMMBAE_RTQ]_^X^Y^S\QUVUWWQVVVUPX[GMBMB@E_RTQR\^XY^YRRSUUUUWWTWRYXABCBCFZ_RVS]_^_^_RSVTUTTTT]_XAA@CADY\QTQS]____]SPVTTWWTW\\[FGA@GZ^]WWQS]\__\RSQTUTTTWTV]_[FFA@FEX_QTVP]\\\\SPSTUTTTWTPR_EDGFGDX^RTWVPR]]]RPPWUUUTUUSS^EEDGD[_\VUWQSRR]]SPPTTUUUUWRSYE[EDZX_]WUWQSRSRRPQPTUUUUTTTUSRS[[XZ[X_]SUTWQSPPSSVQVTUTTTWWUWV\P\[_Y[^\RSWTUWQQQQQVWVTUUUUTTTUUTT]]S^^]^_]RSQUUTTVQVQQVVVTUUUUUUTTUUTQSQR]R]]SSQVTUTVQQQQVWWTUTUWUWPQPRPSSPPQVWUUTTWWWWTTTTUUUTTVVVQQVQQVVWWTTTUTUTUUWTTWWTWWTTUUTTWWWWVWWVWWVWWWWWWWTTTUUUUTUTTTWWWWTWWTWWTWWTTTUUUUUTTTWTWWWWWWWWTWTTTTTTTUUUUUUUTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUTTTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUTTTTTTTUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTTTTUUUUUTTTWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTUTTTTTTTUUUUUUUUUUTUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWWWTTTTUUUUUUUUTTTUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTTUTTUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTUUUUUUTTUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUTPSRSVUUWVPSSPQTTWWWUYZA[VWWR\^\SQ\^_\QWVRSRSQWUUTQPQTSMKN[S]YZZ_TU\YY_RWVSPQVTUWRX[^RVSPV\HtuXS_EGZ]QX[^RVQSQVTTVVVWTVQWTUUAtuOWQYYGD[VXFD_QQR]VTQRSSVWVQUUWW_utJY[XEDXS]AF[PUWS]PR\_]QWWUQQW@vwJSR@DGZ\UXBA^TVS]VVR\SQQVVTUVQT]KtJEVGGE[_Q]A@[VWPRPPY^]QTUWVUUUVVYBFFSRDY_STT\E[RUUTTVTWWS]SQQVWVUUTUVLKIEVGAZYQ]FFYWTWTWWTVS^_PWTTWQTUUAHHCTYG[Y]WVZAERTWVQQUTTS\\R]RQPUTVVUBJJAXGZX]WSD@ZSTTVVVUUQPRSS]RQUVPQTGKHCU\DXY\VV[G[PUWWWVSSPQPPPVTVTVIuHDXFZX\TRZGYWUWWTWUTWP\^Y]WWS]RQDttOSRFAZ^QU\ZZ_TUTWVTS\^^RTWSRSWGtuMP]A@EXSU\EE_WUWWVWTV]_^\VTPRSQEuJBR\ACDXSU][Z\TTTTTTVQVPSSSQTUTWTT@JOG^FDX_VV_[XSUTTTUUTPRPSSQPQUUUUW[KH@RSEG[^RU]YX]TUTTTWUTTWVVWQRRRSTTWUVAKODT_DDX_QV_XYRTTWWWWUTTTTWWVQPVQWU^NI@\PZG[^RRY[_QUUUWWWWUUUUVQWWUWVWTUUFHOGT\EEX^PV_XYSTUTTTUTWVTUUTPQWTTWDNOAW]EZX^QV^Z[RWWWTTUUWVVWUUWPRSVTVVUYIKO\VX[XYRT]X[\VTUTTWTTTUUTWWTUWVWWWWTTWWUZNJMQPYXYXRW\XXSVWUUUUUUUTTUUWQQWUWVWU_F@@]UR]]_RWR_RVWUTTUTTUWTUTTTWTUUPD@@_R_\^\WVR\SWTUUUWWWTWWUWVVVUUUUUT^ZZ^WTPRRRRVWQPPVWTUUUUTWWTTUUTWTTTUUT^XX\TS]RRRTUVPSQTUWTTWUTUTWVWTTUUTTTUWQPVTUTVVQVVWUUWWWTUUUUUTTTTUUTTTUUP]\]VVSSPPWUWVVVTUUTTUTUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUTUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTWTTTTTUUUUUUUUUTTTTTTTTUTTTTTTTTTUUUUTTTUUUUUUUTTTTTUUUUTTTUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUTUUUUTTUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUTTUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWWWTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTTUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUTUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUTTWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUTTTUUUUUUUUTTTTTTWWWTTTTTTTTTTTTTUUTS\^_\_RQWUTWUUUUWWTWVQPPQPQQVWWWTUT^EGEY^PUTVVQQQPVTWTTTTUTTWQQQVWTUTWWVVWWUPE@F[_RVRRRPQVUWWVQVQPQQTUTWQQQWTTUSGM@XPWP^^_SWUUQSSPVVVUTWVWTTUTTU[AFXQS^X_STUQSRQWTUTWWWWWWWTTWTTQFB@[WT]XE^QUVR\]PWUUTWQPQVTUTTWWWTDLCDRT\ZDZ]TTP\^_RWUUUUTVSSVWVWTUTWTUDBCEQ]XEXQTVR\]QWUWTUUUV\\RQTTTUTWT[MBDST]XZXSVR_]PTTWTUTWQSRSPWTWTFLCZVV^[[^WWP]\SVUUUTTTWWTWQ]\RVWPPT_OOA_RZEZRVS\]PWUTTTTTWUTQR\SWUWQPQDMBEQU]XZ^WWS]]PVTTWTTTTWWWPSPVTWQQWVF@G_V_XXSTQRRPWUUUTTTTQPQWUWVWQF@G_W_[XRUTQRRSVTUUTWWWTUWPPQWUVPPWSAAZRPYZXRWVS]]PVTUTWWTUTVQQVTTVVW\AGXPSXZYRVTTP]\\SQVTUUTTTTUTQPVTWVWURDDXPQ^[YRVUWSRRSQVWTUUWWTUTWVWWTTWT_EEYQQ^X_SQUWQPSPVVTUWTTTWWWTTTUUUTTT_ZX_QS^Y\PQTWQQPQVWUTTWWWTTUTUUUUURY^]VUQ]_]PQWTVVQQVVTUTWWWVWTTUUUUUUTTTU\_\RWUUP]\SQQWVVQQQVWUTTTWWWTUUUW__\PTUTR]]PQQTTQQQPQVWUUUUUUTWWWTUTTTUTUUT\\]SWUUSR]SQQTWVVQVWTUTTTUUTTUUUTQ\\RPWTVSSSPPQTUWWVQQWTUUUTWTTTTTTUUUUVRRRPVWWPPSPPQWTWWVQPQWWWTUTTWVWWTTTTUUTUUVSSPQVWVPPPPQVTUTTWVVWTTTWTTTWWWTTWWWTTTTWQPQVTUWQQQVVWUUTTTTUUUUUUUUUUUUPSSSQVTUTQPPPQVWUUTTWTUUUTTUTTTTUUUTUUUUWQQQVWTTVQQQQVWUUUUUTTTUUTTTTTTTUUTTTUUVPPQQVVWWVQPQQQVWTUUUUTTTUUTWWTTUUUTTTUTVVVVVWWWVQQQQQVVTTTTTTTWVVVVVWWTTTTTTTTUUUTWWWWWWVVVVVVVVVWWWWTTTTUUUTTUTWWWVVQQVVWWWWWWWWWTTTUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUTTTTTUTTTTUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUTTUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTTTTUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWTTTTUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUTTTTTTTTWWWWWWVVVVVVVVVVVWWWTTTTTTTWTTWWTTTUUUUUUTTTUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUTUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUTTUUUUUUTWWWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUTTTUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTTUUUUUUWTTUUUUUTUUUTUUUUUUUUUUUUUTTTWWWWWWVWWTUTUTTUUUTUUUTUTUUUUTUUUUUUTUUUUUUUTUTUTUUUUUTTTUTUTUUUUTUTTUWUUUWTUTTWVTTWWTUTWWWTUTTUUUTUTUUUWTTWWWTTTTUUUTTUUUUUUTUUUTTTTTTVWWUWWTWTTWWTUVWWUTWUUUTTWTWTUTUUUTUUUTUUUUUUUWTUTTTUTTUUUWUUTTTUUUUTTTTTUUUUUUUUUUUUUTTUTTUTTUTUTTUUTUTTTUUTTUUUUTUTUUUUUTUUUUUUUUUUUUUUUTUUTTUTTUUUUUUUUUTUTTUUUTWQQPSSPPPQVWWWUTTUTUUTVPSRR]]]SPVWUUWVVQQQPQVVWTUUTVR]\_^^^\RPTUTVPPSSPPQVWWUUUUUUUUUQR\^YYXX_]PWUTQSSRSSSPQQVWTTS]^XX[[Y\RQTTVPSRRRSPVWWTTUUUQ]\YXXZ[^\SWUQSSR]RRPVTTTTUUVR_YX[ZZ[_RQUUVSR]]RSPVWUUTTUUUTQ]^YX[ZZ[_RQTWPR]\\]RSQVWWWTUUUUUUUTTUTV]^X[ZZE[_RVWP]\\\]RPVTUUUUTWWTTWR_Y[[ZZ[_RVTQR]\\RSQWUTWWVVVVTWP\YX[[[[^]QTUVS]\\]RPVUTWWVVVWUTVS^YX[[[[^RQUTQR]]]]SQWUTVQQQQVQWUTVS^YX[[Z[^]PTWQR]]]RPQTUTVQQQQQVTTQ\^Y[X[X^]PTTQS]\]]RPVUTVQPSSSPPQQWVS\Y[[Z[[Y\PWWQR]]RSQWUTWQPSSSPQVWUUVP]^YYY^^_RQTTVSRRSPQWUWVPSRRSPQVTWVP]__^^^^_RQWTVPSSPPQWUUWVQPSRSPQWWUTWQ]_^^^^Y^RQTWPRRRRSPVTTVVQPSPQVWTUUWWS\\_\]\\PVUTVSRSSPQWUUWQSRRRSPVTUUWRR]\]\\RPWTVPRSPPVWUUWQPR]RRSPQVTTVR_^YY^^^RQTUVPR]RRPQWUWVVPSSPPVWTTWQR__^_\\RQTTVPR]\RSPVTUWQPPSRSSPQVWTUUUUUUUWVS]]\]RSPWTWQPSRSPVTTWVQPQVVTTUTVPR]]]RSPVTUWVQQQVWUUTTWWWVWWTTUUTWQPPSPPPVWTUTTWWWWWUUTWWWWWWWTTUUWVQQQQQQQVWWTUUTTTTTTTTTTUUUUUUUTWWVQQQVVVVWWTTUUUUTTTTTTTTTUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTUUUUUTTTTTUUTTTTTTTTTTUTTTUUUUUUUUUUUUTTTWWWWWTTWWWTTTTTTTTTTTUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTTTTTTTTTUTTUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUTTTTTUUUUUUUTTTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUUUUUUUUUUUTTTUUTUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUTTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUTWWVVVVVVWWTTTUUUUUUUUUTQPRRRSSSSPPVWUUTTTTWVVVVTUUTTWWVVVWWTUUUUWP]\]]RSSSPVTTTWVQPSSSQWUUUUTUUP_[ZZY_\]]]SVUVQQQQSR]]SWTTTTTWVQQVWT][GAGZY\]RSQUP]\\\\_^_\PTPRRRPPSRRRQUPXFBBAEX_]RPTP_YYY____\STV\YXXY^\]SQT_FLOMF[_]RSWQ^[[Y^\\\]PQ^ZEE[Y_RPVUUXBIKOAZ^]RSW]ZGD[^\]]RQRXGFGZ^]SQWUUXMJuKBE^RQVU_EAGZ^]SSQW]EFFD[_]RSPWPFKttIF^PWTUP[A@DY]QVVWUPXGFD[_]SSPVTPFJwwK@XRQVWQXGADY]PQQVTVYDFDX\SQQVW\BuwtNF^SVWUSZA@DY]PQQWPXGGEY]PQQVTTUTEItwJBZ]VTUU_D@FZ^SQVVTU\ZDEX\PVWWTTVVWUPGKwwuME\QTU\EAAEYRQVWUP^ZZX\SQVWWUTTURFHutIAXSTU\EAFE^SVWTQ^[[X\SQVWWTTWTWXBHJHCZ]VQYDFDX]QWTRY[X_RQVVQQVUWQQVTRDLIHMD^SVTUR[DDZ_RQVWTW]YX^]PVTTTUWQQQWUPXAMMAZ\QWTUQ^[[X\SQWWTQ]\\RPVWWWWWUWQPQVTU]E@MCDYSTURXZZX]PWTS_^_]PVWWTTTTVVWTPYDAGZ_SWTP\YY_RVTTP]_\RPWUTWVWTQ_ZGGZ^RVTUUWR_^_RQWUWPRRSPVWTTTTTUUTVVVVTUTS_XXY_]PVWTTVPRRSQWTUTVPPPQVWWTTUUQ_[EE[_RQVWWTWS\_]SQWTUTQPSPQWTUWVVVWTUUUUUUUUUTWQPPSSPPQQQVVVVVWWWVVVQQVVVWTTUUUVPR]RSPVTTUTVQQVWTUTTWWTTUUUUUUUUUUUTTTWWWTTTTTTTTTTTTTTTUUUUUUUUTWWWVVWWTTUUUUUTTTTTTTTTTTWWWWWWWWWWWWTTUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUTTTTWWVVVVVVVVVQQQQQVVVVVVVVWWWWWWWWWWWWWVVWWWTTUUUTTWWVVVVVVVVVWWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTWWTWTTTTTTTTTTTTTUUUUUUUUTTTTTWTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUTTWWTTTTTUUUUUUUUUUUUUUUUTTTTTTWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUTTTTUTUUUUUUUUUUUTTTWWWTTTTUUUUUTTTTTTTUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUTTTUTTUUUUUUUUUUUUUUUUUUUUUTUTTTTUTUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUTUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUTUUTTTTUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTWWWTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWQQPPPPQVWTUUTWVVVQVVVWTUWPP]\]]\RPQWUWVVPPPPQWTUTWTWWWWTUTQVSRSR\RPPVTTTTVQQPPQVWTUTTTUVQP]\R__R]RVWTWVVPPVVVTUWQ\\\X[^Y^SSQTWVSPQSPVWUUWTWPTWQU^ZECDYFZSQTWS_X^_[YSWUQ]\]]]SV_GDFF[EXVQ__^Y^^\WP_\_X^]SWUZMFA@GG^UU][YYE[\SWQ]YZZEX]SVVECAF@FZ]VTS_[ZZ[_SVUQ_XXZZYRQTWEB@@MBG_STWS\ZEZ[^RVS_XZEZY\QUQD@AACAE]PTTP][ZZXY]VP_Y[EZX\PWSG@@CBBG_]QUQ\XZZ[Y\QUP\XZEZX\PWSEFFCCAG^RQUP\X[ZZY]QQ]X[ZZX_PTVZFACM@AYRPVRY[ZZX\SUQ]Y[XXY\PT\A@@MBAE]QUTS_[Z[X_PWVR^[ZXY\SVQDCBMLBFYRVUS_[Z[Y\QTVR\^^XY\RRVUTSGMBBBFZ]TQ_X[Z[_]VQR\^Y\]SVWTSDBB@ADXSTP_XXX^]QTTQR]R]\SVTUUTTUQ\FB@FDX\VUVS_^_\RQTUWPPSPSQWTTWWTTUVSZAGDZ^\PUTVS]]RPVWTTWQQQQVWVVTWWWWWWTTWQ^DFFF[^]QUUQSRRRQQVUTVQQQQVVWUUUQ_ZGGFE_RQUUVPR\]SPQWTUWVPPQVWTUUTTWTUUWRY[[[[_SVUVPS]]RPQTUTTTTUUTWWVVWWTTUQ_XXX[Y\SWUVPPRRSPVTUTTTWWWTTWWTTTTTUTS_Y^^^\RQUTVPSSPQQVTTTWTTUUTWWVVVWWTUUUUVR__\\RPVUUVPSSPVVTUUTWWWUUWWWWWTTUVR\\RRSPQWUTWVQVWWTTTTUUUUUUTTTUUTTWWWWWTTTUUUUUUUQ]\\RSQVVWUTTWVQQVTUTTUUUUUUUUUUUUUUUTTTTUUUUUTTTTUTVQQQVVVVWTUUTTTWWVVVVWTTUUUUUUTTTUUUUUWVQVVWTWWTTUUUUTTWWWWWTUUUUUUUUUTTTUUUUUTTWWTTTTTTTTTTTWWWWTTTUUUUUTWWWVVVVVWWWWWWWWWTTWWWWWWTTUUUUUUUUUUTTWWWWWWWWWWWWWWTTTTUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUTTTTTTUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTTTUUUUUTUUTUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUTTUTTTTTTTUUUUUUTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUTTTTTTTUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWTUUUUUUUUUUUUUUUUTTTTTTTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTUTTTTUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUTUTTUUUUUUUUUUUUUUTTTTTUUUUUUUUTTTTTUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTTTTUUUUUUUUUUUUTTTTTUUUTTTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTUUUTTTTTTTTUUUUUUUUUUUUUUUUUUTUTUTUTUUUUUUUUUUUUUUUTTUUUTUUUUTUUUUUUUUUTTUUUUUUUUUTTUUUUTUTUUUUVWTTTTUUVWUTVWTWVWWVTUTVUTTVPPWTTQ]PTUWWTVTUUTVVWVTUTUUUUTUTUWQVWWQPPUWVQVTTPVTTUUUUUUTUUUWUUUTUTUTVTVTVTUWQWTTTWUUTTTUUUTUUUTTUTTTWTTVTUTWUUUWWTTUUUUTUUUUUVTUTUTTTUTTTTTTTVWTUTTUUTUUTTUUUUUUTUUUUUUUUTTTWTTWTTTUTUTUUUUTTVQPPSSSSSPQQVWWTUUUVPP\^_^^^_RRPTUUWVPR]]]RSQVTV]SYZYZ[YY]SPWWQSSSSPQWUUTUTUP_RXEYZ[^^RPQUTQPR\]R\PPQTUTTTTS]][ZXE[Y^RRVWPS]\]]]PQVTWVVVTTV]SXEXZZXY]]PTQPR\]]]PQWUUTTWTURQ]ZYXZ^Y\RSTWQS]]]\RPQTTTTTR]\ZEXZX^_RPTTWPS]\]]RVTTWVVVVWURSR[Y^X^_]RSWTVPR\\\]RPVTUTTTUQS][[XZ[Y_]PTUWPR\___]SQWUTTWWTUUSS_[YXX_\SQTTVSR]]]RPVUUTWVVVWTUTSS_XYYY_\RPWUWPR]]]RSQWUUTWWWWTQV\Y_YX__]SQWPS]]]]SPVUTWVWWTUPV\X_XX^_\RQUUVSR]\\]SPVTTWWWWTUSPSXY^[Y__RSWUVPR]]]RSPVUUTWWWTPVRX_^[__\RPTTQPSRRRSPVTUTWTTUQVV^_]Y_]]SPWVQPSSSSQQTUUTWTTUWQV\^R^^]]SPVUWQPSRSSQQWUTWTTUPVSY\\Y\]]RPWTUUUTQPPSSSPQVUUUTWTTTTVWR\S\\RRSQVUUTUVQPPSSPQVTUVTQ]SR\RRSPVTTVVQPPPQVWUUTTTUWWWRRP]RSSPQWTTUUVQQPSSPPQWTUUUTTTTTUVTQ]PS\SSSPQWTUUTVVVPPQVVWUUUUWVVRRS\]RRSPVTTUWVVQQQVWWTUUUWTPSPRRSSSPQVWTUUTWVVQVVWWUUTTPPQSSPSPQVWTUUWWWVQVVWWTUTUWQVQSPQPQVWTUUUTVVVQQVVWTTUTTQQQPPQPQVWWTUUUTWWWWWWTTUUUUUUUTWWQQVPQQQQVWWTUUUTTTWWWWWWWWTUUTWWVVWVVVVVWWWTTUUUUTTTTTWWWWWTTTTTWVVWVVVVVWWWWTWTTWWWWWWTTTUTWVVVQQVQQWVVWWWWWWWWWWWTTTUUUTTTWWWWVVVVVVVWWWWWTTTTTTUTTTUUUUUUUUUUTTTTWTWWWWWWWWTTTUUUUUUTTTTUUUUUTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUUUUUUUTTUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUTUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUTTTTTTTTTTTTUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTTTTTUUUUUUUUUUUUUUTTWWWWWTTTTUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUTTTTUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUTTUUUUUUUUUTTTTTTTTTTUUTUTTTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUVR\_\SVTUUUWWWWTTUTTTTWWWTU\DCA^VWSSRQTUUWVVPQTWWUTVSPVVUZBO@\WP]Y]QUQSSRSWPPTTVPVPQU^NJIXU]YXDYTUP]_]]RWUWUS]SRVTVUWVUULHNAT_ZX[ZVUS]X^SPTTWVSSVUUWQQVVVWTUFutNRREEEDP\Y[XSTVQQUUQPPWUTTTVVWTQNuuFT[E[D^S^X[RUVQPVUQQSQTVQTWVTUTCttLQYAD[[WT^X[^WWPQQV]]PWTUTQQQWSKtKZRABD[_\Z[XSPPQWQ^^RVUWPPQW^JtI^\@@[XSYE[YVUSPQWS^_SWUTWVWMuJAVEBE^]P[X_PPSVWQ^X]VTWTT_JtIY_@@Y\V^ZY]TWRPQWUSXYSWTQWWW^utH^^C@YRW_[_RUWRSVUW]X_SWUQQSQ@JHGQE@Z]PQXY]QUPRVTUWR\SQTTTTWTZuuMQ_AD\STUY[\SUUUVRQTTWVVWPSQVWUVVTU]MN@RRDD_SVR^]PWUVVTUUTUQ]RWWVPQUGIOXQ[G[]PUV^^]QVPVTUTUUWPSSVUTWWUP@LC]T_DZ\RQP\RPTUVVWTUTWTTWQPSQUWVWT]ACGRV_EXRPVPRPQUUTQVTTTWVVTUUTTVSPTVVQVSG@GRRXYRPVQRSQTUVVWTUUUUTVWTTWUPEFEPRX^RSWS]SQTUUTVVTTVQVUUWVWUQZDZRTRXX]SQS]SQTUVVTUUUWVVTTVVTUUREDXVW\X^RQTTSSQTUWVWUUUUWWTTWWUUUU\XYSQ\_SWVPVTUUTTWWUUUUTWWUUWVWTUUT_^\QTP\\PVWVQWTUTTTWWUUUUTTTUUTWTUTR\RQWP]]PQWUUTVQVWTUUTTTWWTUUUTTUUUUUUUTUTQPPWTVQSPVWWUUUUUWWTTUUUTTWWWTTUUUUUTUUUTWWTUUWVVVVWTTTTWWWTTTTTUUUUTUUTWTTTWWWWWWWWWWWTTTTWWWTUUUUUUTTTWWWWTTWWWWTTTTTTTUUUUUUUUUUUUTTWWTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTUTTUUUUUUTUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUTTTTTTTTTTUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTUUTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTTTTTTTTTTUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWWWTTTTUUUUUUUUUUUUUUUUTTTUTUUUUUUUUUUTTTTUUTTTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUTUTTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTTTTUTUUTUUUTUUUUUUUUUTUWUWUUUTUUTUUUUUTTTTTUUTUUTTUTUTTUTUWWWWWTTWTTUUTUUUTTUUUUUTUWTVWTUWTTUUUTTWVWTUUTWTTUTTUTUUUUTUUUUWVUTTTUUUUTUTUWVUUTUUWWUUUTUTUTUTUUTUTTTUTTUUTUUTUUUUUUUUTTWWWVWWWVWWTTWUTTUTUUUUUQWVRR__\^\RRQTTTQQS]R]RPPWUUTR]^[XEEXY^PVTVSR]\]]]PQVUUTTTTUQV\Y[DDGFZY^PTWPR\^^^\\RQVTUTTWWWTVV^[EAA@CEZXQTVS\^YY^\]PWUUWVQPPQVTTPP[DGC@@@ZY\TP\_X[X^\SVUWVPSSPQWU_][FF@CAFZ_]TTS_YX[X^\SWTVQS]]]RPV^YYACACCGEYSVVR^X[[[^]SWWWQ]]R\RPVU\XYGCAACGZYSWUQ\Y[ZZ[^RQTUVS]\_\]SQURXYE@FGADX^SUVS^[ZZZX\SWTS]\^_\\SVU]X^ZFGDG[_]VUP]^[Z[X\PWVP]^YYY_RQTT__^EDEGZ^_RVR_X[XY\PWVS\^YXY\RPTT\^XEGFFEY_RV]Y[Z[X_STVS\YXXY_]PW]Y[FGD@D_RQVR_YZEX_]QWP]_^^^]SVUUUUV\XDAGED[]VVR_XXXX^SVTQSR]RRPVUTRXDABCFEYSTWR^XEE[Y]VWP]\__]SVUTWVQQQVTTWQSSR]RSSQWTTVQPPPQQWTUUTVPSRRPQVUS^[EEDGZY]VVS]_YY_]PTUVS]_^_]SQWUTWVQQVWTUWQPSSSPPQWTTWVVVVVVWTURXEEZZ[X_PUWR_^_\]RPWTQR]]RPVWUTWVQPQVWTTWVQQPPSPPQWTUUUTWVVQQQQVVWTQ_[[XY__\RVVS]\\RPVWUTWVQSSPVTUWVVVVWWTTUUUUWQPPPPQQVWTUUTTWVVVVVWTUS^XX^\]RRPVTVPR]RSQWUUWVVVVQVVTUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUWVQPQQVVVVVVWUUUUUUUUVPSSSQVVWWTUUUUTTTTUUUTTTTTUUUUUUUUUUUTVPSRRSQQVVWTTUTWWWVVVVVWTUUUTVQPPPQVWWWTTUUUTTTTTTTUUUTTTTTUUTVQPPPQVWTTTTUUUUUUUUUUTTWWWWWTTTTTTTTTTTTTTTTTUUUUTWWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTWWWWWTWWWTTTTUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUTTTUUUUUUUTUUUUUUUUUUUUUUTTTWWWWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUTTTWWTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUTTTTTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUTTTTTUUUUUUUUUUUUUUUUUUUTUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUTTTTTTTUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTWTTTTWWWTTTTTTWTTTWWWWWTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUTUUTUUTUTUTUUUUUUTTTUUUUUUUUUUUUUUUUUUTUUTUUUUTWUTUTTTTUTUTTTUUTTUTTTTUUUUUUUTWTUUTWTWUUUTTTTUUWWUUTQUWWVUUWTTWUTTUTUWWWWTTTTTUUWTWTTUUTUUTUTUTUUWTTUTTTTWWUWVUWVUWWTUUTUUTWTUTWWWTUTWWUWVTTUUUUTWTUUWTWTUTUUUUVTUUTTTTUWUWTTUTTVWTPVWQUTTVWVTUVWTTUUUWTPTWUUUTTUUUWUUQTTVVTTTQWWTVRQWTQWTPPUPSWTTTUTVTUUQVVTVUWUWUUQVTSQTQWUTTUWUTWPTTTTVWVVQPSVUUUTUUWWTWUSUVVRTVVTTWUTTTQPTUUWPUQVUVQTUUVTTTTVQWVWWWVTUUUTTTWQUTUVUTQVWTWTTQTTUWTUTUWTUQWUQQWVUTWTWVVUWVQWWTUUWWUWTWTUVUUWUTUUUUUTVQQVVWTQTUUUTTUTWUWWUVTUUTQUUVWUUWUTVUTUUTTWUWUTUUUUTTUUTTTTWQUUVQTTVUQWTVTVUWWTTWTTUTUUWUUTTWUQUWWWVUVWTUTTUTTUWWUUTUUUUUTTTUVWWQVQQVQVVQWWVWWTUWUTUTUTUUVWQSSSSSSPQWWTUUWWVQQQVWTWTTWQP]]]\\]RPVTUWVQPPPQVWTTTUWWTWTUUWPP\\]^_\\QVWWVQSSSSQWWTTTTTWTUUVPR_\_^^^\SPWTVPSRRSPVWTUTWWWWVTTUVP]\\^^^_RPVUUWQPRRRSQVWUTWWWVTVTVP]^_^YYY\SQTWVPR]]RPQWUUTWVVVVWTUUQP_^_XYYY]PQUWQS]\]RPVTUWWQQQQVTTTVP_^_YXXY\SQTUVQR\__\RPVTWQPSRSSQWWUWP\^^YX[X^RPVTVPR]\]SQVUUWVQSPPQWTUWP\^^YYXY\SQTTVS]\_]SQVUTVQSRRSQWTUWP]^Y^XXY^RPVWQR]\]RPVTUWVQSRPQVTUUVP\Y_YXY[^SPWTQS]___RPQTVPPRRSSVWWWPR^XYX[[X_RVTWPR\__\RQWUTQPR]RRSQVUWQR^XYX[XX\SQTVPR\__]SQWUWVVQPPQWUWP]XY^[YY^PVTWPR\_^_RPVUWQPSSSPVUUVS_XX[[[[^RQTVS]\_^_RPWWQPRRSSQWUUQR_[[[EZZYRPTVS]_YY_]PVUWPSR]RSQWUWS\XX[Z[[YSVUWQR\_^_RQTTQPSRSPQTUVR^YYXXXY\PWUVS]_^^]SVUWPS]]RSQTTP]Y[X[[YY]VTVR\^YYY_SVTTQR]\]RRQWUWS\X[XX[Y^SWUVP]_^^_RQTWQ]]]]SPVTQ\YEDZZZX_STWS\^YXY_RVUTVR\__]RSVTUWS_[DZX[Y_RWVS\_^^\]QUUTQ]R]RQQWVR^[GGZE[^]WUVR_^YY_]QUTVP]]\]PPWVR_[DE[ZY_RWVS]_^_\RVUUVPS]RSSQWWS\YEZ[[Y^RWVS]\__\SVUQPR]RRSQVUWP]_Z[YXY^_QWWPR]\\]RQTUTVQQQVVTWPRYY_^_\_QTTQR]\\\]PWTUWVVVVWTWPS^_]^]\\QVTUVQPSRSSQWTUUUUUVQP\RSRPSSWTUTVQQPPSSQVWTUWVPPRRPSQPQWWUUUUUTWQQPPPPQWWTTTTWVQQSPQPQVVWWTTWTTWWWWWVVVVWWTUUTWVQPQPPPQVWWTTUTTTTTUUTWWWVWWWWTWWTTUUUUTTTUUUUUUUUUUTUUUUUUUUUUUTTTTTTTUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUTTUUUUUUUUUUUUUTTTTWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUTUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTWWWWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUTTUTUUUUTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWTTTUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWTTTTUUUUUUUUUUUUUUUUTTTTTTUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTWWWWWWWTTTTTWWWWWWTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWWWWWWWTWTTWWWTTTUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUTUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTTTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTUUUUUUUUUUUUUUUUTTUTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUTUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQQVWWWTTTTUTTUTTTTUUUUUUUUUUUUUUUUUUUUUUTUUUUUTTUUUUUUUUUUUUUUTTTUUUUUTTUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUTTTUUUUUUUUUUUTUUTTTTTTTTTTTTUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTTTTTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUTTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUTTUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTUUUUTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUTWVQQPPQVVWWWWTTTTUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVQVVWWWTTTUUUUUTUUUUUTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUTUUUUUUUUUUUUVVWUTTUTWTUUWWWTUUTTTTTUTTUUUTWWWWTUUTTTUUUUUUUTTTUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVQVVWWTTTTUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWWWWTTTUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTTTTTUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWWTTTTUUUUUUTUTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTTTTUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUTTTWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUTTWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUTWWVWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTUTTTTUUTUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUTTUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUTWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTTTTTUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUTTTWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUTTUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUTTWWWWWWTTTTTTTUUUUUUTTUUUUUUUUUUUUTTTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWWTTUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUTTTTTTUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWVVVVVVWTTTTWWWWWWTTTTWWVVVVVVVVQQVVVVVWWVWWWWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTUTTWWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTTUUUUUUUUUUTUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWWVVWWWWWTTUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTTUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTTUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWWTTTTTUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTTTUUUUUUUUUUUUUUUTTTTUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWTTTTUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUTTTWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUTUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTWWWTTTTTUUUUUUUUUUUUUTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUTTTWTTTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWWWTTTTTUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWVWWWWTTTTUUUUUUUUUUUUUUTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWTTTTTUUUUUUUUUUTTUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUTUUUUUUUUUUUUUUUUUUUUUUTTTWTTTTUUUUUTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUTTTWWWTTTTUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWVVQQPPQQVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUTWWVVVVWWWTTTUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVQQVVVWWTTUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUTTTTTTTTTUUUUTTWWWWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVVVWWTTUUTTWWVVVVWWTTUUUTTTTUUUUTWVVQQQQQVVWTUUUUTTTTTTUUUUTWQQPPPPQQVWTUUTTTWWTWTTTUUTWVQPSSRSSPPVWTUUUTTTTTTTUTUUUUUUUUTUUTUUUUTWQPSR]RSSPVTUUTTTVWVVVVVVWTTUWWPR]\\_]SSQTUUWWQVQPQQQVWWTUTVQP]\___\RSQTUTVQQPPQPQQVWWTUUVQS__YYY^]RPTQQQRRRR]RPQQTUUUVPS_^^Y^^RPVUVQPRSSRSQQWUQSS_X^XY^]PVTQSS]\]]SSVWTUQ]\^Z[ZZX^RQTTQS\__Y^\\SVWQ]_^DZZ[X^RQWVP]^^^X^]]STUWQ_^_GE[EY_PTUVS]^Y_X_SRVUTWWWTWPYXXFEZZ^]VTQR\YY^^\PVTUTWTTUUV_[XGFEEX_PUTP]_Y[YY^RQWUTVQVVWWTR[[ZAEZ[\PTVS\^XY^_SVTUWVQQVVTVV\Z[EAEZX\QUTS\YX[[^^RQWUWVPSSQVTVQ][DZFGZ[_STUP]_YXX_\SWWQQRRRSPWQPR[E[FGXX_PTTS]_YYY_\SWWQPRRRRPWUPS]ZEXDDY^]VUPR\^^_]RQTUWQSRR]RPQTPR][EXEE^_]VQS]__\]SVTUWQSSRRSPVUP]]XD[EDY^\PTUPR]__\]SQTUWPSR]]RSQTU]\\[ZYZ[\\SWWPSR\\RSQWTVQSSSSPVTSSR^Y\X^R]STUPS]_^\]SQWUUTWQQPPQVWUTPP]Y__X_]]PTUWVPSSSPQWTUTVVVQVWTWQP\^\YX__\PWUUWQSRRRSPVTUUWWVQQQQWUQPS_^_XX\_]QTWVPR]]]RPVWUTWWVVWWTTVVR_\_X_\\SVTUWQS]]]RSQVTTWWVVVWTUWVP\\\Y^]\RVTWVPSRRRPQVTTWWVVWWTWVS\]\^\]]SWTUWVPSSRSPQWTUTUTWWWWTUWWPRS]_RRRPWTTVQSSSSSQVWTUUTTWWWWWTTWTPRP]\SRRQWTUWVQPPPPQVWTUUUUTTUUUUTUQPVSRPSSVWTUUWVVVQVWTTUUUTTTUUTUWPQVSPQPQTTUTWWQQQQQVWTTUTUTTTTUUTUTQQWSPVQQTTUUTVWVQVVVWTUUTUUTTUUUTTTQSQR\R]]PQQWTTUUUTTWVWVVVWWTUUUUTUUVWTQVTVWTTUUTTTWTTTTTUUWTTVWWVWTWTUTUUUUUTTTWWWWWTTUUTTWWWWWWWWWWTTTTWWTTTTTUUUUUTTTTTTTTWTTTTTWWWWWTTTTTUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTTTWTTTTTTTUUUUUUUUUUTTTUUUUUTUUTTTUUUUUUTTUUUUUUUUUUUUUUUTTTTTTUUUTUUUUTUUUTWTTTUTUUTTTTUUUUUUUTTUUUUUUUUUUUUTTUTTTTUUUUUTWTTTTUTTTUUUUTUUUUTWWTTUUUTUUTTTTWWWWWWWTTTTTUUUUUUUUUTUUUUUUUUTTUUTTTTUUUUUUUUUUUUUUTUUUUUUUTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVQVVWWTTTTUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUTWWWVVWWWTTTTUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUTWWVVVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUTTUTTTTTTUUUUTWWVVVWWTTTUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUTWVVVVVVWWWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTUUUTTWVVVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUWQPQUUTTTWWVWUUWWWWTVQQWUUUVGAA_U\RPR\QVV_^QTUTUQVUP\SQTTTWPQVQTU]EC@RQ]SPRPSWV^_SVUUWTVPVTUT][YPVSPU_OKBQQRR]Y_QV^[]UUUVVUTQ\^RVSSWVGBCX_X^\RWW]Y]VUVPWTVS\\RWVSSVDOND\^]]\QTRX[RUWUTQQTQSSQPQQWTQRPURF@DPQ_]SRPTV\Y]TUUWWUVPSQVVWTWQQ[@@Y]\RSSWS^YPUTTWPRPVQVWUVPSV]YX^VQRPQQUVS]QTUUUUUTTTVS_\SVTQSQVXAB[]RQSRPPU]_PWTUTWWUWQR\PVTUUWWTTTXDD_W_\SPQUTP\\VTUUTUUQ_^RWTVQVVTSDAGSP_]PPVTWR_RTTUUVQQVVQVUUVPQTSEGZPV\]PPWVPSPUTUUTWVVWWWWWTUWVWTWXE[STSSQQQTWP]]PVWWVVTUTUTWWWVWWTUUTVWUQXZYVVRRPPVTWS]RVTTUTWUUTWWTWVQQVUUTWWTU_Z[]TSRQQQWVSSQTTTTWTUTWWWTTTWWWTTTWWT\[Z\TWSSPPSVWQQWTUTUTUUUTWWWWTUTWWWUUUUURXZ^VVQVVQVUTVQWTTTTTUUUUTWVVWTUTWTTUUUU_[Z_TUQQQPSVUVPPVWWWWWTUUTTTTUTWVVWUUTWTUUTTTUUR^Y]UUVQQPPVUUVPPQWTTTTTUUUTTTUUUUTWWTP^Y]TUQQVVVTWQQTUUUUTUUTWWTUUP_Y\TVVVVVTUWQVTUUTTTUUUTUUUUUUUUUUUUUUUUWS_\QTWTWQVWUTVQWTTTTWTUUUUTTUTTTUUUUTWTUVSRPUTWWTVVWWUUTWVWTTTTTTUUUUUTWWVQQQVVWWWWWTUUUUUUUTTTUUVVWUUUTWWTUUUUUWVVWUUTWWVWTUUTTTTTTUUUUUUUUUUUUUUUUUUTWWWTUUUUUTTTTTTTUUUTTTTTUUUUUUUUUUUUTTTUUUUUUUTUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUTTWWVVWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUTTWWVVVVVQQQQQQQQQQQVVVVVVQVVVVQQQQVQVQQQVVVVVVVVVWWTTUUUUTTWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWWVVQQQVVWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTWWVVWVVVVQVVVVQQQQVVVVVQQVVVVVVVVVVVVVVVWWWWWVVWTTUUUTTWWWWVVVVQVVVVVVVVVWWTTTUUTTTWWWTWWWVVVVVQQQVQVVVVVVVVWWWWWWWWWWWWWWWTTTTTTWTUUUUUTUUUTTTTWWWWWWWWWWTTTTTTTTTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWWTTTTTUUUUUUUUUTTWWVVWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVVVVWVVVQQQQVVWWTTTTUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUTUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVWWWTTTTUUUUUUUUUUUUUUUUUTUUUUUUUUUTWWVVVVVWWTTTUUUUUUUUUUTTWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTUTTTTTTUUUUUTTWWVWWVVVVQQQQQQQQQQVWWWTTTWWVVVVVVVVQQQVVVVVVVVWTTUUUTTTUUUUTTTTTWWWWWWWWWWVWWWWWWWWWWWWWWWWWWWWWWTTTTWWWTTUUUUUUUUUUUUUTUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVVQQVVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTTTTUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUTTWVVVVVQQQQQQQQQQQPQQQQQQPQQQQQQQVVVVQQQQVVVVVVVVWTTUUUUTTUUTTWWTTUUUUUUUUUUQ]]RPSQWUUUWWTTUTTUUTTUUUUUTUUUTUS^Y_SPVUTQQVTTVVTUUWTUUTVQVUUVPSPWTTTTUUUTTVYEXRTWPRPWWTTVWWQQUWPSQTUUTTUUTVWT_EZ\WUUP\RVTUUQQWWQWUUUWP]RQUTTUUTWWTR[Z_QUVR\PUTUUVPVUTWUUUUTVPPQWTUUUWVWTV[G[SUQR_RTTWTUWQQTUWWUTUTWPRSVUTWQVUWZF[SUPR_RUTWTUTQQTUVWUTTUUWQPPQWUUVQQW_AAYWUQR_YSQQTTQPQTWVTWTUTWTTS]PTVVTUU[FZPUTPR^\WWVTTQPWUWTWTUTWTWPSQUTQQVWT[GXVUTWP]^]UWVUWQQWUWTTTTTUWQQVUTVVVTUUYEXQUTVP\]TWVTQQWUWTTUWWTVPPQUTWVWUUY[_WUTWQS_RUVVUWQQTTWTUWTUWVVQPQVUUWVQVTU\X_VUUVS\RWWWUUWQQTUWTUWTUVQQVVVWTUWQQVTUP^_PUWQRRVUWUTVVTTTTUWQVTTUUUUTWTT]_RTTVS]PUTTUWVWUWWUUUTVVWTTTTTWWTW\_RWUUTQR]PTUWTTWVWUUWWUTUTWWTTTTUUTTUT]_]VTUUTVS]PTUTUTVVTUWWTUUUTWTUUUUUUTTR\RVTUUTVS]SWUTUUWVVTUTVVTUTTUUTVVWTUUTTUTTUTRRQTTUVSRQTUTTTUWWTWWTUTUUTWWTUUUUPPVTUTTVPSVTUTWWUTWVTWVWUTWTUTTUUUUVPVTUTUUWQPVTUTTWUUUWVWTUWWVWTTWTTUUUTTTUUWVVWTTTUUTVQQVWWWVVQQPSPQVVVVVVVVWTUUUTTTTUUUUUTWWTTUUUUUTWWTUUUUUUUUTTTWWWWWWWWWWWWWWTTUTUUUUUUUUUUUUTTTWWTTTTTTWWWTTWTTUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTWWWVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQVVVVVWWWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUTTWWVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQVVWWWWWTTTTTTTTTUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVQQVVVWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTUUUUUUUUTTTTUTTUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWVVWWWWWWWWTTTTTTUUUUTWVVVQQQQVWTUUTWWWWWTTUUTVQPSSSSPVWUUTWWWVWWTUUVPSR]]]]SQWTVQPQQQVVTUUUUUTUUUTVR]\____]PVUUWVQQVVWTUTTWTUWPSR_YY[Z[Z[Y\SVTTWQS]\\]]RPVTUTWWVVQVTTWS\___\\]QTWPR]]SPQWUUWVQQQPPVTTVP]_^^_]]PWTQS]\]PQWUWQQPSPPQWWPS_YYY^\]SWTWQR\\]SQWWQR\_____]PVTQPR_^^_]RSVTWS\_^\RSVTQSSRRSSPQWTVPR_^^^\]RQTUVP]^YX_RQWTQR]\_^^^\RQTTVPR__\\RSPTUWQR]\]SVTTQSSRRRSQWUWQS]^^^_]RSVTVQR]\]SVTVPR]]\]SPWUTVPR\^^_\RSQTWVPR]]RQTUQR\\_\\RPWUWPPR\_\\RSPVUUWVPRRRPWUWP]\__\]SQTUWVVQR]]]RRRSVUUTVQSSPVTUTVSR]]RRPVTUUUWPRRRRR]SVUTQPPQVWTTVQQQPPQWTUUUUTWPRRRRSSPWWQPSSPVWUUWVQPPPQVWTUVQPSPPPQWUUWQPSPVWUUWVQQPPQVWTUWQR\__\]]SQTTVQSSSPVWUTVVQQQQVWTUTVVQSRRSPVVWUTWVQPPPQVWUTTTTTTTTUUUUTWVQQQVVVWTUTTTTTTTUTTTTWTTTUUUUUUUUTWWWVWWWTTUUTTTTTTTUUUTTWWTTTTUUUUUUUUUUUTTTTTTTTTWTTTTTTTTTTTTTUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTWWVQQQQVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTUTTUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUTTVVQQQQPPPPPPPPPQQQQQQQQQQVVVVVVVVWVVVVVVWWWWWWWTTUUUUUUUUUUUUUUUUUTTWVVQVVWWTTTUUUUUUUUUUUUUUUUUUUTTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTUUUUUUTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUTWWVVVVQQPPPPPPPSPSPPQQQQQQQQQQQQPQQQQQQQQVQQQQQQVWWTTUUUUUTTTTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQQQVVWWWWTTUUUUUUUUUUUUUTUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTUUUUUUUUUUUUUUUUUTTTTTTTUUUUTTWWVVWWTTTTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUTWWVWWWTTTTTUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVWWWTTTTTTUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUTWWVVQQVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVVWWTTTTUUUUUUTUTTTTTTTTUTTTUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUTTWWVWWWWTTTTUUUUUUUUTWVVVVVTUTVQQQVVVVVWWUUUTTTTWWWWTUUTTTTUUUUUUUUUTTUUUTVQQQVVVVWWUUTTWWVVVVWUUTTTTUUVPSSPPPPPPVTUTTWVQPPQVUTVQSSPQWUUVS]]]]RRRRPWUWVVVQPSRSQTUWQPSPQWUWP]]]]RRRRSQUUWVVQPRRRSVUWVPSSPVTWR_^^\]RSSQTTVQQQPSRRSVUTWQSR]RPWQ\^Y^^__\\SWWQPSSR\\\RVUTWVQPR]]SQWUUW]XZZX_]SSPVWS\\]]]]\\SVWVVQPS]_\RVUUWRY[Z[Y^\RPWTP]___\\\RPTTQPPSSRRSPVUUUTRXEDZ^]PQWTUP\_\]SSPPQTWPSSSPPPPQVTUTTTUW]XEE[^RPWTUTS\_\]SPQQVUWR\_\]SSPPQVTWVVWTUWRYZZX\SVTTTS\__]SPPPQTVSSSPQVVVWTUWVVWTUP^[Z[_SVTUTUUP\^_\RPPPQWTPSSPQVVVVVTTWVVWUTRY[[Y]PWTTTUWS\_\SQVWWWUVSRSPQVVVWTTVVVWTUUP\YX^]PVWWTUUP]\]SQWWTTVSRSPQVWWWTTVVWTUTS_YY\RQVVVVTWS]]RPVWWTTVPSPQVWWWTTTWVWWTUWR^XX^\SQVVVTTPR]RPQWTTUWPPPQVWTTTTWVVVWTUUVS]\]RPVTTUTVPPPVWTUUWQQQVWTUUTVQPQWTUTS\^_]PVTTWWTTQR]RSQVWTTUWQPPQWTTWVVVVWWTTTTTTUUUUUWVQPQVVWWWWTTUUTWWWWTTTTUUTWVQVVWTUUUUUUTWWWWUTP]___]SQVWUTVQPQVWTUUWVVVWTUUTTTTTTUUTVQQQQVWWTTUUTWWWWTTUTTTTTTUUUUUUUTWVVVVWWTTTTUUUTWWVVVWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUTTUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWWTTTUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUTWWVVQVVWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUTWVVQQQQVWWTTTTUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUTTTTTTUTTTTTTTTUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTWWVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQVVWVVVQQQQQVVVWWVVQQQQVVVVVVVVWWWVVVVVVVVVVVVVWWWWWVVWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTWVVQQQVVWWTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUTTWWVVWWWWWWTTTTTTTTTUUUUUUUUUUUUUUUUUUTTWWVWVVVQQQQVWWWVVVVQQVVVVVVVVVWVVVVVVWWTTTTWWWWWWWWWWWWWTUUUUUUTTWWVQQVVVWWWTTTTTTUUUUTTWTTTTTWWWVVWWTTUUUTTWWWTTTUUUUTTTTTTTTWWWWWWWWWVWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTUUUUTTWWVVWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWTTTTUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUTUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUTWWVVQVVWWWTTTUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUTWVQPPVQVWWTUUTTTWWWWTUUUUUUUUUUUTTTTTTTTTTTUUUUTWVWVQVVWUUUUWVWVWWWTTWQPQPSPPQWTUUWWWWWWTUUTTWWWWWTTUTQQQSSSRPVVTTTWQVVQVWWTUUUUUUUWQSSRR]]]RSQVTUTWWVVVWWTTTUUUUUTUTQPPR]R\]SSQWWTWWVQVQPVQVTTTUTVR_\^Y__]VTUVSR]]SQVUUVPS]\RRSVTUVR_YXYY^]QTUVS\_^Y_SVUTP\^Y^^\SVUW]XZGDZ[^VUS\^X^_\QUV\Y[EDZX_STW\[EGGXY_WW]YYXY\RQUQ\^YXY_RVUTP^EDGDY_SUQ]^^_\]PTUPR\\\_\PWUUTQ^EDEDX^RTV]_^^_]SWTP]\^^_\SQTUTSYZEDEY^SUVR]\_]]SWUQS]\\_RPQTUTS^[ZEZY^STVR]\_\]SWUQS]_^\]PVTV\XZDG[Y_QQR]__]RPTTPR]_\]RQTUUW]YZEE[Y]VUUQR]\\\]PTTQSR]]RPWUUTTTUTR^YZEY^\VUVSR\]R]PWUVPSRRSPVUTTTWWUV]YXEDY_]VTTPR]\]RRPTTQQPRSQVTTWVQVVWTUWR\_^XY]RQWTTQPSRPPPVWVQPSPVTUUTWWWVWTUUVSRR\_]SQUWPS]RSRRPVTVQQPPQVTUTTWTWTUVR]]\_]SQTWQSRSQQVTWVQQVWWUTWVQVVVTUTP]]]^^_]QTTWQS]]RPVTTVQQQWTUTWQQPPPQVTVSRR\^_]PTWQPSRRSVTTVQPPQVTTWVVWWTUQ]]]\\]SVWQPSPPQVTUWVQQVVWTUTWVVWWTUUWSRR]]RSQTWQPQQVVWTTWVVWWTUUUTWWVWWTTTTUUUUTTTTUWPRRSSPQQWUWVVVWWTTTUTWWTTUUUTTTTTTTTTUUUUUUUUUUTQPPQVVVQVTUUTWWWWTTTTTUUUUUUUUTTWTTTUWQQQVVWWWTUUTTWWWWTUUUUUUUUUUUUUUUUUUUUUUTWWWWWWTTTTTTTUUUUUUUUUUUUTWWVVVVVWTTTTTTTTTUUUUUUTWWWWTTTTUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUTTTTWTTTTWWWWWWTTTTTTTTUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTUUUUUUUUUUUUUTUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUTTWVVQQVVWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVVVWWWTTTUUUUUTTUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTTTTUUUTUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTUUUUUUTTWVVVQVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTUUTUTUUUUUUUUUTTUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUTWWWVVWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQQQVVVWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTWWTTUTUUUUUUUUUUUTTTUUUTUTUTUUUUUUTTTTUUUTTWWTTUTWWTUTTUUUUUUUUTWWTUUUTTUUUUUTTUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUTUTUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTWWVVVQVVWWTTTTUUUTWWVQQQQVVVWTUUUUVVQRSR]RRSQVTUWVVQQVVWUUUTUUUVPPR\]\\RRPVTUTVQQPQVVTUTVVQQVVVTUVPP]_]_^\]SVWUWQQPPPQVTUTVVQQQVVWTUWQQ]_\^^_\]QWTVPPSSPQWUWVVPSPQQVWTTVVS\R\_]]RQWWVQPPQVTUUWVVQPQVVTTQQS_]]_\]RQWUUTVPPSSQVWUQQQSSPSPWWUTQVP\R]_]RRQWUWWQSSSRPQVUTUVPPPRPPPVTUUQVP\]]_\]RPWTUTVPPSSPQWUTWVSSPSPVVTUVVP\R\_]RRQWUUVQPPPQVTUWQQPSPPQVTUTVVS\R\_]]RQWTTQPSSSPQWUTWQSSSRSQQVTUQQP]\]^_\\RQWUUVQPPPQVWUWVQPSPPQVWUTTQRR]_]]]PVTWQPSSPPVTUTVQPSSPQVTUTTQRS]\]]RQWUUVQPSSPQWTTWVQQQVWTUTQSS]\]]RPVTTVQPSPPVTUTWVVVVWTUUUQRS]_\]]SQWTVQPSSPQWTUWVVVQVWWTTTP]R\_\\]SQTWQQSSSPVWUTWWVVVVWTUUTTPRP]\SRSQWUUVQPPSPPQWTUUTVVVQVVWTUUUWSPP]SPSQWTTVVQPPQVWUUTWWWVWWTUTTVSQSRPPPVTUUUWVQQPQVVTUUTWWWWWTUUTTWPPQRSPSQWTUTVVQQVVWTUUTWTTTUUTTWPPQSSPPPVWUUWWWVWWTUUTTTTUUUUUUUUUTVWVQVVVWTUTTTWWTTUUTTWQVVPQVVVTTUUTWWWWWTTUUUUUUUTTVQVQPQQQVWTUUTTWWWVVVWWTTUUUUUUUUUUUUTTWVVVVVWWWTTUUUTTWWWWWWWTTTUUUUTTWWWVVVVVVWWTTTTTTTTTTTTUUUUUTTTTTTWTWWWWWWWWTTTTTUUUUUTUTTTTWTWWWTTTTTTTTTTTTUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTTUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUTTWVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUTWWVVWWWWWTWWWWTTTTTTTTTUUUUUUUUUTUTTTTTUTUUUUUUUUUUUUUUUUUUTTTTTUUUUUTTUUUUUUUUUUUUUUUUUUTUUUUUUTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTWVVVWWTTTTUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUTTWWVVVWWTTTTUUUUUUUUUUUUTTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUTUUTUUUUUUUUUUUUUUUUUUUUUUUUTTWVVQQQVVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUTWWVVQVWWWTTTTUUUUUUUTQPSPVWTUUUUTWWWTTWWWTTTTTTTTTUUQ]\]PTUUWWWTUUTWWWTUUWWWWTTTTUUUTTTTTTUWR__RTUUTQQVTTUUUTTVWTUUTTUTTWTUUUTTUVYE[RTVRSWUTWVQWTTUUUTTUUTTWTTTUTR^_SUWWVPVUTTWWWWWTUUUTTUUTTTTTWWWTUUUUTTWTUV[GESTQQP]RTWQVVWWWTUTUUTWTUUUUUUUUUUUUTUUTWVWU^GERUVQQ]RTQPQVWTTUUUWVVVWTUTTTURZD_UQPQPSWVSSQWUUTTTTTWTUUUWQPQWUUUUSZD^TUQSQPSVWSSQWUUTTTWWWTUTWTUUUWQPQWTTTTTWVWTTUU_Z[STPPQPQUQSPQTUUUTTTUUUUUUUTWVVWWUUUUU_ZXSWPPQQVVPQWUUUUTTUUUTWWWWTUUUU\[XSWSRPPVVSPVTUTTWWTUUUUUTWVVWTTUTTUU_[YPVSSPQVUPRPVTUTWWWWWTUUTTUUUUUUTWVVTUUUUUQ^[_TTPSPQVWWSSQWUUTWWWWTUUUUUTTTUT\Y_WWPQVVTVQVWUUTTTTUUUUUUUUTTWTUT_X^PUQRSPQVUQSQVTUUTTWWWUUUTUUUUUTTWTUUR^_QVPQVVWUQSQWTUTWWWVWUUUUUTUTTWWTUUUUUUUUUW\Y_VTQPQVVVUWPSQWTUTWWWTUUUTWTUT]_]TUVQWTWTUVVWUUTTUTUUUUUUUTWVWTUUTUUUUP]]PTVVWTTUTWTUUUUUUTTTUUVSSQTWQQVVVTUTVVTUUUUUUUUUTTTUUUTPSSVTVQVWWTUUTTUUUUUUUUUUUUUTTTUUUUTTUUUUUVVVUUTVWTTTUTWTUUUUUUUUUUUUUTUUUUUUUUTWWWUUTWWWTTUUTWTTUUUUUTTTTUTTTTUVQVWUUTVVQVWWWTUUTTTUTTWTTTUUUUUUUUUUUTTTTTTWWWWTTTTTTTTTTUUUUUUUUUUUUUUUTWWTUUUTTTTTTTUUUUUUUUUUTTUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTWWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUTTUUUUUUUUUUTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVQQVVWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQVVWWTTTTUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWWTTTUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUTWWVVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUTUTTUTTTUUUUUUUUUUUUTTUTTTUUUUUUUUUUUTWWVVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUTTTTTTUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUTUUTUTUTUUUUUUUUUUWVVQQQQQQVWTUUTTWWVVVVWWTWVQPSRRSSPVTUTVQPPQQWTTWVQPPPQVVTUUWVPSSRRRRSPQWTWVQPSSSPVWUUUUUVPPR\]\]SSQUTWQPPSPVWUUTVQPPPQQVTTUUTTTUUWPS]_^__]RQTUWQS]]]RPVUTVQSSSSQVVTUUTTWVWWWTUPPS]]]\SPPTWVQSSSSQWUUWQPPRSPQVUVSR\___]SQTWQS]]RSQTWVPSSSPQVTUUUUUPRSR]RRPWWUTVPPSSPQVUUWVQPPQQVWTUTP]]\__\]PVUTVPRR]RPVTUWQSSRSSPVWTUTWWWWTTUUVQQPSPSPVVTTTWVVQQQVWUUTTWVVVVWTUUUWP]\__\]RQWUUWVPSSPPVTUWVQQQVWTUUUUTTTUUWVQPSPQQWTTUTWWVVWWTUUTTWWVQVWWTUUUWPR]\]RSSPVWUTVQPSSRSPQWUTWVQVVWTUTTTTUUUTWVQQPQQQVWUUUTTTUUUUUTTUUWQSS]]SQVWUUWQQVVVWTUTWVVVWTTTTTUUUUUTTTTTTUUUUTTTWWWWWWTTTUUUUUTTTTTTTTUTTUUUWQSSSRRSPQWTTUTWWVVWTTUUTWWTTTUUUUUTUUUUUUUTTTTTTTUUUUWQQQQQVWTUUTTTTTTUUUUUUUUUTUUUWVQVVVVVWTUUUTTUUUUUUUUUUUTTTTTTUUUUTVQQQQVVVVWTUUUUUUUTWWWWWTTUUUUUUUUUTTTTTTTUUTWVVVVVVVWWTTUUUUUUUUUUUTTTTTTUUUUUTTTTTTUUUUUUUUUUUUUUUTTWTTTTTTTTTUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUTTTTTUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUTTUUUUUUUUUUUTWVVVVWWWWTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUTUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVWWTTTTTTTTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVQPPPPSPPSSSSSSSSSSSPPQPQPQPPQQQQQQVVVVVVVVVWTTUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUTWWVVQQVVWWWWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUTTUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVWWTTTTUUUUUUUUUUUUUTWVVQQQVQQPPPPPQVVVQQQQQVVWWTTTUUUUTTWWWWVVVVVVVVVVVVVVVVQQQQVVWWTTUUUUUUUUUUUUUUUUUUUUUUTWWWVVVVQQQQQPQQQPPPPQQQQPPPQQQQQQQQVVVQQQQVVVVVVVVWWTUUUUTTTTTUTUTTTUTUUUUUUUUTTWVVQVVWWVVQQQQQVVVWWTWWVVVQVVWVVQQQQQQQQQPQQVWWTTUUUUTUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVVWWWTTTTTUUUUUTTWWVWWWWVVVQQVVQQQQQQVVQQQQQVQQQQQQVWWTTUUUUUUUUUUUUUTWWWVVWTTTTTUUUUUUUUUTTWWWWVVVVQQVVQQQQQQQQQVVVVVVVVVVVWTTUUUUUUUTTTTWWWWVVWTTTUUUUUUUUUUUUUUTWWVVVVQQQQPQPPPPPPQQQQQPPPPPPPPPPPPPPQQQVVVVVVVWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQPQQVVWWTTTUUUUUUUUUUUUUUUUUUUUUUTTUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUTTWVVQQVVWWWTTTTTUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUTTTUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUTTUUUUUUUTTTTUTTTUTTUTTWUUUUUUTUUUTTUWTUUUUUUUUUUUTUUUTUUUUUUUTUUUUUUTUVWQQVVVTQUVTVWUTWWUTTUUTUTUUUUTUTTUUUUUTTUTUUTTUTUUUUUTUUUUTTUUWUUUUUUTTUTUUTTUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQQQVVWWWWTTTTTTWWWVVWWTTUWQSSRRRSPVWTTWVVVVVWWTUTWWWWWWTTQR]\\_\RQWTUWQPSPQVVTUUQPSSSSPQWTUWS\__^^\SVTUWQPSSQVWTUUVPRRRSPQWWR^Y^^X_RQUTWQSRRQWTUTQPSSSSSPVTS_XXYX[_SWVQPSR]SVTUUWQSSSPPSPVTUUR^XXYXYRQWUTUQSR]\\\RVTUTVVVTTVVVVTTTUR^YY^Y^SWUVVQSRRQUUVPPQQQVVTUUTWWTTTTUS_[[XXY]PTUVQPSSSQWUUWVQQVWWWWWTTWVWWTTUUP]^YX[X]QTWWQSRRPWTTUUVPSPPPPQTTTTTTUTTTTP\^XX[YRVUUVQPRRSVUUWQPPQVWVQWWTTWWTUTTTS\^X[[X]QTUWVPSSPVUUUVQQVWVVWTTWWTTTTUS\YX[[YRVUWVQSRRPWTVQVWTWWTTWVVWWWTUUS\^XXX_PTWVQSRSQUTVVWWWWWTUUTVVVVWWWUP]_YX[YRWUWQQS]RPWTVVWWWWWTTTWVVVWTTUP]\_YX\QUWVQPRSVUWWWTTWWWWTWVWTUP]__^Y_QVQPS]RPTUWWWWTWVVVWWTTTUUUUUS_YYYY]VTQPSRRRPWTTTWTTUTWWTWWVVQQQQVWWTUUUUUUUUTS\_^^_STTVPSRRRQTUTWQPQVWWTUUUP]\\^_SWTTWVPSPVTUUUTTTTVQPPQVVWTTQSSR]SVUUTTTWVQVTUUTTTUUTWWWTTUUVPPPPPVWUUUUUTTWQQPQWWWWTUUTVVQPQVWWWWTUUUTTWVVVVWWWTTTTTUUTWWVVVVVQQQQQVVVQQVVVVVVVVVWWTTUUTTTWWTWWWWWWWWWVVVWWWTTTUUUUUUUUUUTTWTTTTTTUUUUTTTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUTWWVVWWWTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUTUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQVVWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUTTUUUUUTTWWVVWWWTTTUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTUTWVVVQQVVVWWTTTTUUUUUUUUUUUUUUUUTTTTUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTTUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUTWWVVVWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWWTTTTUUUUUUUUUUUUUUUTWVVQQQQQQQQQQQQQQQQQQQQQQQQQVWWTTTUUUTTTWTTWWWWVWWWWVVVVWTTUUUUTTTWWWWVVVWVVVVVVVVVVVVVWVVVVVVWWWWWWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUTWWWVVVWWWWVVVQQQVWWTTTUUUUUUTWWVVVVVQQQQVVQQQQQVWWTTUUUUTWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUTTUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTTUUUUUUTWWVVVWWTTTTTUUUUUUTTWVVWVQQPPPPQQVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVVVVWWTTTTUUUUUUTWWVVWWTTTUUUUTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWWTTTTTUUUUTTWVVVVVQQQQQQQPPPPPPQVWWTTUUTTWWWWWVVVVVVVVVVVVVVWVVVVVVWWWWWWWWWTTUUUUUUTTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWTUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUTUUTTTTTUUUUTWWVVVVWWWTTTTTTUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUTUUUUTTUUUUUUUUUUUUUUTUUTTTTTUUUUTWVVQVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTTWVVQVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUTTTTUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUTTWVVVVVWWTTTUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUTWWVVVVWWTTTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUTUTTUUUUUUUUUUUUUUUTTTTTTTTUUUUUTWVQQQQVVVWWWTTTTUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUTWWVQQVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUTUUTUUUUUUUUTTTUUUTUTUUUUTWWVVQQVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUTTTUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVQVVVWWWTTTTTUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUTWWVVVVWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQQVVVWWWTTTTUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUTTUTUTUUUUUUUUUUUUUUUUUUUTTUTTTTTTTTTTUUUUTVVQQQQVVVVWWWWWTTUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUTUTTTUUUTTTTUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVWWWTTTUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTTTTTTTTTUUUUUUUUUUUUUUUUUUTWWVVQQVVVWWWWTTTTTUUUUUUTWWVVQVVWWWTTUUUUUTWWVVVVVVWWWWVVQQPPPQQVQQQQQQQVVWWWWVVVQVWWTTTWWWWVVWVVVVQQVWWTTUUUUUUTTTTTTTTTUUUUUUUUUUUUUTUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVWWWTTTTWWTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTWVVQQQQPPPPPPPPPPPPPPPPPPPPQQQQQQQVVVVVVVVWVWWWVVWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWWTTTTUUUUUUUUUUUTTTTTTTTUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUTUUTWVVQQVVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTTTTUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWWTTTUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTUUTUUUUUUUUUUTTWWVWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUTUUUTUUUUUUUUTTWWVWWWTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWTTTUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUTTTTTTTTTTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWTTTTTTUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTUUUTTWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWWWWWWWTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTTTUUUUUUUTTTTTTUUUUUUUUUTUUUUUUUUUUUUUTUUUUUUUUUUUUUUUTTTUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTTTTUUUUTUUUUUUUUUTTTWWVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVWWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUUUUUUTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUTTTWWVVVWWWWWTTTTTTUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUTTTTUUUUUUUUUUUTTTWWTTUUUUUUUTTTTTUUUUUUUUTUUUUUUUTTTUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTUUUUUUUUUUUUTTWWWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTWWWWTTTTTUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVQVVVWWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUTWWWWWWTTUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUTTWWVVWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTUUUUUUUUUUUTTTTTTTTTTTUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTTTUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUTUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUTWWWWWWTTTTUUUUUUUUUUUTTTUTUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUTTUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTWWWVVVVWWWWTTTTTUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTTTTTTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUTTWWWWTTTTTUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUUUUUTTWWVVWWWTTTTTTTUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUTUUUTUUUUTUUUUTTWWVVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUTUTTTTTTTTTTTUUUUTTWWVVWWWWWWWWWTTTTTTTTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWTTTTUUUUUUUUUTUTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUTWWVVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUTTUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTTUTTUUUUUTTWWVWWWWTTWTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWTTTTTTUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVWWTTTUUUUUUTTTTTTTTTTTTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVVQQVVVVWWWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUTTTTUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUTUTTUUUUUTTWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUTTUUUUUUUUTWVVVVWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUTTTUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUTTTTTTTTTTTTTUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTTUUUUUUUUUTTTUTTTTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTUUUUUUUUUTTTTUTUTTTUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUTTTTUUUUUTTWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWWTTTTTTTUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUTTTTTTTTTTTTTTTTTTUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTUUUUUUUUUUUUTTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQVVWWWWTTTTTTUUUUUUUUUTTTTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUTUTUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWWTTTUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWTTTTUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWTTTTUUUUUTTUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTTTTUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVWWWTTTTUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTUUUUUTTTTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVQVVVWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVWWWTTTTUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUTUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVVVVWWWWTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWWTTTTTTUUUUUUUUUUTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTTUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTTTTTTTUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWTTTTTUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWWTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVWWWWTTTTTUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVVVQQVQQQQQQQQQQQQQVVVVVVVVVVVVVVVVVVVVVWWTTTUUUUUUUUUTTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVVVWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWTTTTTUUUUUUUUTWWWVVWWTTTTUUUUUUUUUUUUUTTWWVWWWTTTTTUUUUUUUUTWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWTTTTUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWVVVVQQQQQQQQQQQQVVVVVVQVVVVVVVVWWTTTUUUUUTTWWTTTUUUUTTTWTTTUUUUUUTTWWVVWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWWTTTUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUTWWVWWWTTTTTTUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVVQQQQQQQQQQQQQQQQQQQVQQQVVVVVVVVVVVVVVVWVWWTTUUUUTTTTWWWWWWWWVVVVVVVVVVVVWWWWVVWWTTTTWWWWWWWWWWWTTUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWWWWTTTUUUUUTTWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVVWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWVWWWTTTTTTUUUUUTTWWWWTTTUUUUUUUUUUUUTUUUUUUUUTTWVVVVVWWTTTTTTUUUUUUUUUTTWWWTTTTTUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWWWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVWWWTTTUUUUUTTWWWWWTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVQQQVVVWWWTTTTTUUUUUUUUUUUUUUUUUTTWWWWVVVVQQVVVVVVQVVVVQQQVVVVVVVVWVVVVVVWWTTUUUUUUUTTWWWWWVVVVVVVQQQQPQQQQQQQQQVVQQQQQVVVVVQQVVVVVVVVWTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVVVWWWWWTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWWVVVVWWWWTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTWVVVVWWTTTUUUUUTUUUUUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWVVVWWTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTWWWWTTUUUUU \ No newline at end of file diff --git a/test/support_test/chip.h264 b/test/support_test/chip.h264 new file mode 100755 index 0000000..2521ee6 Binary files /dev/null and b/test/support_test/chip.h264 differ diff --git a/test/support_test/gtest2html.xslt b/test/support_test/gtest2html.xslt new file mode 100755 index 0000000..e4da40a --- /dev/null +++ b/test/support_test/gtest2html.xslt @@ -0,0 +1,154 @@ + + + + + + + Unit Test Results + + + + + + + + + + +

Unit Test Run

+

+ Executed test cases in + test suites, + test cases failed. + Execution time +

+ +
+ + +

+ + + + + + + + + + + + + + + + + + + + + +
Test (/ failed) MessageResult
+ + + + + + FAIL + + =

+
+ + + +
OK
+
+
diff --git a/test/support_test/rv1106_jpeg_encoder.jpg b/test/support_test/rv1106_jpeg_encoder.jpg new file mode 100755 index 0000000..672da44 Binary files /dev/null and b/test/support_test/rv1106_jpeg_encoder.jpg differ diff --git a/test/support_test/video_sync.h264 b/test/support_test/video_sync.h264 new file mode 100755 index 0000000..f9781c9 Binary files /dev/null and b/test/support_test/video_sync.h264 differ diff --git a/test/utils/LinuxApiMock/include/LinuxApiMock.h b/test/utils/LinuxApiMock/include/LinuxApiMock.h index f1d6eae..2909ebe 100644 --- a/test/utils/LinuxApiMock/include/LinuxApiMock.h +++ b/test/utils/LinuxApiMock/include/LinuxApiMock.h @@ -18,6 +18,8 @@ #include constexpr int INVALID_HANDLE = -1; constexpr int MOCK_SELECT_TIME_OUT = 0; +constexpr int MOCK_SYSTEM_RESULT_SUCCESS = 0; +constexpr int MOCK_SYSTEM_RESULT_FAILURE = -1; class LinuxApiMock { public: @@ -35,6 +37,7 @@ public: virtual int fx_fstat(int fd, struct stat *statbuf); virtual int fx_access(const char *pathname, int mode); virtual FILE *fx_fopen(const char *pathname, const char *mode); + virtual int fx_system(const char *command); }; /** * A simulation interface class used for automated testing in Ubuntu systems, implementing the function of piling on @@ -54,6 +57,7 @@ public: MOCK_METHOD2(fx_fstat, int(int, struct stat *)); MOCK_METHOD2(fx_access, int(const char *, int)); MOCK_METHOD2(fx_fopen, FILE *(const char *, const char *)); + MOCK_METHOD1(fx_system, int(const char *)); public: /** @@ -65,7 +69,26 @@ public: */ virtual int GetHandleForMock(void); +private: + static void InitSDCardDefaultStatus(std::shared_ptr &mock); + public: + /** + * @brief The functions from here on are all used in the simulator under the Ubuntu system. When cross-compiling, it + * is forbidden to use the following functions directly. + */ + /** + * @brief Create an instance of the LinuxTest class. The LinuxTest class instance can only be created through this + * function. + */ static std::shared_ptr CreateLinuxTest(void); + /** + * @brief Controls the return value when the system function executes a shell command. This function can only simply + * control the return value and cannot capture and analyze the parameters of the shell command. + * @param mock + * @param command shell command + * @param result return value of system function + */ + static void SetSystemCommandResult(std::shared_ptr &mock, const std::string &command, const int &result); }; #endif \ No newline at end of file diff --git a/test/utils/LinuxApiMock/src/LinuxApiMock.cpp b/test/utils/LinuxApiMock/src/LinuxApiMock.cpp index e719089..b5db393 100644 --- a/test/utils/LinuxApiMock/src/LinuxApiMock.cpp +++ b/test/utils/LinuxApiMock/src/LinuxApiMock.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ #include "LinuxApiMock.h" +#include "ILog.h" #include "LinuxTestImpl.h" #include "WrapApi.h" #if defined(__x86_64__) @@ -81,12 +82,32 @@ FILE *LinuxApiMock::fx_fopen(const char *pathname, const char *mode) { return __real_fx_fopen(pathname, mode); } +int LinuxApiMock::fx_system(const char *command) +{ + return __real_fx_system(command); +} +void LinuxTest::InitSDCardDefaultStatus(std::shared_ptr &mock) +{ + LinuxTest::SetSystemCommandResult(mock, "mkfs.vfat", MOCK_SYSTEM_RESULT_SUCCESS); + LinuxTest::SetSystemCommandResult(mock, "mount", MOCK_SYSTEM_RESULT_SUCCESS); + LinuxTest::SetSystemCommandResult(mock, "umount", MOCK_SYSTEM_RESULT_SUCCESS); +} std::shared_ptr LinuxTest::CreateLinuxTest(void) { std::shared_ptr test = std::make_shared(); LinuxTestImpl::ApiInit(test); + InitSDCardDefaultStatus(test); return test; } +void LinuxTest::SetSystemCommandResult(std::shared_ptr &mock, const std::string &command, const int &result) +{ + std::shared_ptr test = std::dynamic_pointer_cast(mock); + if (test) { + test->SetSystemReturn(command, result); + return; + } + LogWarning("The mock is null.\n"); +} int LinuxTest::GetHandleForMock(void) { return INVALID_HANDLE; diff --git a/test/utils/LinuxApiMock/src/LinuxTestImpl.cpp b/test/utils/LinuxApiMock/src/LinuxTestImpl.cpp index 4720b57..3a236b3 100644 --- a/test/utils/LinuxApiMock/src/LinuxTestImpl.cpp +++ b/test/utils/LinuxApiMock/src/LinuxTestImpl.cpp @@ -21,6 +21,7 @@ #include #endif #include +#include #include #include #include @@ -136,6 +137,26 @@ void LinuxTestImpl::ApiInit(std::shared_ptr &mock) WithArgs<0, 1>(Invoke(api_fopen)), Invoke((std::dynamic_pointer_cast(mock)).get(), &LinuxTestImpl::ApiUnlockThread), ReturnPointee(&fopenResult))); + static int systemResult = -1; + auto api_system = [=](const char *command) { + // char command_buf[1024] = {0}; + // size_t length = strcspn(command, " "); + // strncpy(command_buf, command, length); + // command_buf[length] = '\0'; + // std::shared_ptr testLinuxApi = std::dynamic_pointer_cast(mock); + // if (testLinuxApi && testLinuxApi->CheckIfNeedToMockSystem(command)) { + // systemResult = testLinuxApi->ReturnMockSystem(command); + // } + // else { + systemResult = __real_fx_system(command); + // } + }; + EXPECT_CALL(*mock.get(), fx_system(_)) + .WillRepeatedly( + DoAll(Invoke((std::dynamic_pointer_cast(mock)).get(), &LinuxTestImpl::ApiLock), + WithArgs<0>(Invoke(api_system)), + Invoke((std::dynamic_pointer_cast(mock)).get(), &LinuxTestImpl::ApiUnlockThread), + ReturnPointee(&systemResult))); } void LinuxTestImpl::Init() { @@ -161,6 +182,42 @@ void LinuxTestImpl::ApiUnlock(void) // mApiMutex.unlock(); // LogInfo("unlock api.\n"); } +void LinuxTestImpl::SetSystemReturn(const std::string &command, const int &returnValue) +{ + LogInfo(" command = %s, return value = %d\n", command.c_str(), returnValue); + mSystemReturn[command] = returnValue; + if (mSystemReturn.size() > 1) { + return; + } + static int systemResult = -1; + std::shared_ptr mock = LinuxTest::shared_from_this(); + std::weak_ptr weakMock = mock; + auto api_system = [=](const char *command) { + char command_buf[1024] = {0}; + size_t length = strcspn(command, " "); + strncpy(command_buf, command, length); + command_buf[length] = '\0'; + auto mock = weakMock.lock(); + if (weakMock.expired()) { + LogWarning("weakMock is expired.\n"); + systemResult = __real_fx_system(command); + return; + } + std::shared_ptr testLinuxApi = std::dynamic_pointer_cast(mock); + if (testLinuxApi && testLinuxApi->CheckIfNeedToMockSystem(command_buf)) { + systemResult = testLinuxApi->ReturnMockSystem(command_buf); + } + else { + systemResult = __real_fx_system(command); + } + }; + EXPECT_CALL(*mock.get(), fx_system(_)) + .WillRepeatedly( + DoAll(Invoke((std::dynamic_pointer_cast(mock)).get(), &LinuxTestImpl::ApiLock), + WithArgs<0>(Invoke(api_system)), + Invoke((std::dynamic_pointer_cast(mock)).get(), &LinuxTestImpl::ApiUnlockThread), + ReturnPointee(&systemResult))); +} void LinuxTestImpl::ApiUnlockThread(void) { // LogInfo("ApiUnlockThread\n"); @@ -173,4 +230,22 @@ void LinuxTestImpl::ApiUnlockThread(void) // }; // std::shared_ptr test = std::dynamic_pointer_cast(LinuxTest::shared_from_this()); // mApiThread = std::thread(api_unlock, test); +} +int LinuxTestImpl::ReturnMockSystem(const std::string &command) +{ + auto returnInt = mSystemReturn.find(command); + if (returnInt != mSystemReturn.end()) { + LogInfo("command = %s, return value = %d\n", command.c_str(), returnInt->second); + return returnInt->second; + } + LogWarning("command = %s, return value not found.\n", command.c_str()); + return -1; +} +bool LinuxTestImpl::CheckIfNeedToMockSystem(const std::string &command) +{ + auto returnInt = mSystemReturn.find(command); + if (returnInt != mSystemReturn.end()) { + return true; + } + return false; } \ No newline at end of file diff --git a/test/utils/LinuxApiMock/src/LinuxTestImpl.h b/test/utils/LinuxApiMock/src/LinuxTestImpl.h index 1a5623c..2d3c20f 100644 --- a/test/utils/LinuxApiMock/src/LinuxTestImpl.h +++ b/test/utils/LinuxApiMock/src/LinuxTestImpl.h @@ -17,6 +17,7 @@ #include "HandleManager.h" #include "LinuxApiMock.h" #include "WrapApi.h" +#include #include #include /** @@ -35,9 +36,12 @@ public: void UnInit() override; void ApiLock(void); void ApiUnlock(void); + void SetSystemReturn(const std::string &command, const int &returnValue); private: void ApiUnlockThread(void); + int ReturnMockSystem(const std::string &command); + bool CheckIfNeedToMockSystem(const std::string &command); public: static void ApiInit(std::shared_ptr &mock); @@ -45,5 +49,6 @@ public: private: std::mutex mApiMutex; std::thread mApiThread; + std::map mSystemReturn; }; #endif \ No newline at end of file diff --git a/test/utils/LinuxApiMock/src/WrapApi.cpp b/test/utils/LinuxApiMock/src/WrapApi.cpp index b994ee5..19f3d14 100644 --- a/test/utils/LinuxApiMock/src/WrapApi.cpp +++ b/test/utils/LinuxApiMock/src/WrapApi.cpp @@ -77,6 +77,12 @@ FILE *__wrap_fx_fopen(const char *pathname, const char *mode) std::lock_guard locker(gMutex); return LinuxApiMock::GetInstance()->fx_fopen(pathname, mode); } +int __wrap_fx_system(const char *command) +{ + static std::mutex gMutex; + std::lock_guard locker(gMutex); + return LinuxApiMock::GetInstance()->fx_system(command); +} #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/test/utils/LinuxApiMock/src/WrapApi.h b/test/utils/LinuxApiMock/src/WrapApi.h index 734729c..d10b96c 100644 --- a/test/utils/LinuxApiMock/src/WrapApi.h +++ b/test/utils/LinuxApiMock/src/WrapApi.h @@ -32,6 +32,7 @@ int __real_fx_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except int __real_fx_fstat(int fd, struct stat *statbuf); int __real_fx_access(const char *pathname, int mode); FILE *__real_fx_fopen(const char *pathname, const char *mode); +int __real_fx_system(const char *command); #ifdef __cplusplus } #endif diff --git a/test/utils/SharedData/CMakeLists.txt b/test/utils/SharedData/CMakeLists.txt index 4f3b70e..8c21a74 100644 --- a/test/utils/SharedData/CMakeLists.txt +++ b/test/utils/SharedData/CMakeLists.txt @@ -15,10 +15,6 @@ link_directories( ${EXTERNAL_LIBS_OUTPUT_PATH} ) - - - - aux_source_directory(. SRC_FILES) aux_source_directory(./src SRC_FILES) diff --git a/utils/LedControl/include/LedControl.h b/utils/LedControl/include/LedControl.h index af95786..d62af5f 100644 --- a/utils/LedControl/include/LedControl.h +++ b/utils/LedControl/include/LedControl.h @@ -19,6 +19,7 @@ #include constexpr unsigned int NEW_TOP_LED_STATE = 0; constexpr unsigned int LED_NOT_BLINK = 0; +constexpr unsigned int BLINKING_SUPER_FAST_MS = 200; constexpr unsigned int BLINKING_FAST_MS = 500; constexpr unsigned int BLINKING_SLOW_MS = 1000; constexpr long int KEEP_ALIVE_FOREVER = 0; diff --git a/utils/LinuxApi/include/LinuxApi.h b/utils/LinuxApi/include/LinuxApi.h index ceef546..5200ef2 100644 --- a/utils/LinuxApi/include/LinuxApi.h +++ b/utils/LinuxApi/include/LinuxApi.h @@ -24,7 +24,20 @@ #ifdef __cplusplus extern "C" { #endif +/** + * @brief This function can be used to test the function, and the system function can be used as an imperative return + * result. + * @param command + * @return int + */ int fx_system(const char *command); +/** + * @brief Under the circumstances of the return of the system, there is a "warning" written by the organization, and the + * use of the function does not exist. + * @param command + * @return int + */ +int fx_system_v2(const char *command); int fx_open(const char *pathname, int flags); int fx_tcgetattr(int fd, struct termios *termios_p); int fx_tcsetattr(int fd, int optional_actions, const struct termios *termios_p); diff --git a/utils/LinuxApi/src/LinuxApi.c b/utils/LinuxApi/src/LinuxApi.c index 7fd4b4c..d8e2c6a 100644 --- a/utils/LinuxApi/src/LinuxApi.c +++ b/utils/LinuxApi/src/LinuxApi.c @@ -28,6 +28,10 @@ int fx_system(const char *command) { return system(command); } +int fx_system_v2(const char *command) +{ + return system(command); +} int fx_open(const char *pathname, int flags) { return open(pathname, flags); diff --git a/utils/McuProtocol/include/McuProtocol.h b/utils/McuProtocol/include/McuProtocol.h index 74302c6..b56a7a5 100644 --- a/utils/McuProtocol/include/McuProtocol.h +++ b/utils/McuProtocol/include/McuProtocol.h @@ -121,6 +121,8 @@ protected: const StatusCode SetFeedingCycleForWatchDog(const unsigned char &hour, const unsigned char &min, const unsigned char &second, std::shared_ptr &context); + const StatusCode SetFeedingCycleForWatchDog(const unsigned short &feedCycle_s, + std::shared_ptr &context); const StatusCode McuSetDateTime(const unsigned short &year, const unsigned char &mon, const unsigned char &day, const unsigned char &hour, const unsigned char &min, const unsigned char &second, std::shared_ptr &context); diff --git a/utils/McuProtocol/src/McuProtocol.cpp b/utils/McuProtocol/src/McuProtocol.cpp index 481c96e..474ab0b 100644 --- a/utils/McuProtocol/src/McuProtocol.cpp +++ b/utils/McuProtocol/src/McuProtocol.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -142,6 +143,20 @@ const StatusCode McuProtocol::SetFeedingCycleForWatchDog(const unsigned char &ho return WriteProtocolData( handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); } +const StatusCode McuProtocol::SetFeedingCycleForWatchDog(const unsigned short &feedCycle_s, + std::shared_ptr &context) +{ + unsigned short feedCycle = 0; + char byteOrder = ProtocolHandle::GetByteOrder(); + if (ORDER_LITTLE_ENDIAN == byteOrder) { + feedCycle = htons(feedCycle_s); + } + std::shared_ptr param = + std::make_shared>(PROTOCOL_COMMAND::ASK_SET_FEEDING_CYCLE, feedCycle); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} const StatusCode McuProtocol::McuSetDateTime(const unsigned short &year, const unsigned char &mon, const unsigned char &day, const unsigned char &hour, const unsigned char &min, const unsigned char &second, diff --git a/utils/McuProtocol/src/ProtocolHandle.cpp b/utils/McuProtocol/src/ProtocolHandle.cpp index bec0c57..3b8dbcf 100644 --- a/utils/McuProtocol/src/ProtocolHandle.cpp +++ b/utils/McuProtocol/src/ProtocolHandle.cpp @@ -178,7 +178,7 @@ void ProtocolHandle::MakeAskFeedWatchDogPacket(const std::shared_ptr ¶m) { - MakeProtocolData(param); + MakeProtocolData(param); } void ProtocolHandle::MakeAskSetDateTimePacket(const std::shared_ptr ¶m) { diff --git a/utils/MediaBase/CMakeLists.txt b/utils/MediaBase/CMakeLists.txt index d9cf06c..0a8bde7 100644 --- a/utils/MediaBase/CMakeLists.txt +++ b/utils/MediaBase/CMakeLists.txt @@ -5,9 +5,9 @@ set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) include_directories( ./src ./include - # ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/LinuxApi/include ${UTILS_SOURCE_PATH}/StatusCode/include - ${UTILS_SOURCE_PATH}/ModBusCRC16/include + # ${UTILS_SOURCE_PATH}/ModBusCRC16/include ${UTILS_SOURCE_PATH}/Log/include ${EXTERNAL_LIBS_OUTPUT_PATH}/ffmpeg/include ) @@ -20,10 +20,9 @@ aux_source_directory(./src SRC_FILES) set(TARGET_NAME MediaBase) add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} LinuxApi avformat avcodec avutil swresample avdevice avfilter swscale postproc fdk-aac StatusCode Log) if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) - target_link_libraries(${TARGET_NAME} avformat avcodec avutil swresample avdevice avfilter swscale postproc fdk-aac z StatusCode Log) -else() - target_link_libraries(${TARGET_NAME} avformat avcodec avutil swresample avdevice avfilter swscale postproc fdk-aac StatusCode Log) + target_link_libraries(${TARGET_NAME} z) endif() add_custom_target( diff --git a/utils/MediaBase/include/MediaBase.h b/utils/MediaBase/include/MediaBase.h index 9942307..d825386 100644 --- a/utils/MediaBase/include/MediaBase.h +++ b/utils/MediaBase/include/MediaBase.h @@ -24,20 +24,43 @@ enum MediaHandleType MEDIA_HANDLE_TYPE_READ_H264 = 0, MEDIA_HANDLE_TYPE_READ_G711A, MEDIA_HANDLE_TYPE_COMBINE_MP4, + MEDIA_HANDLE_TYPE_TAKE_PICTURE, MEDIA_HANDLE_TYPE_END }; enum StreamType { STREAM_TYPE_VIDEO_H264 = 0, STREAM_TYPE_AUDIO_G711A, + STREAM_TYPE_JPEG, STREAM_TYPE_END }; -typedef struct stream_info +/** + * @brief + * NOTE: The timestamp parameter is critical. The time base of the timestamp must be {1,1000000}, which means the unit + * is 1us. + */ +typedef struct StreamInfo { - const StreamType mType; + const StreamType mType; ///< Type of the stream. + const unsigned long long mTimeStamp_us; ///< Timestamp of the stream. } StreamInfo; -typedef void (*ReadVideoFileCallback)(const void *, const unsigned int, void *); -typedef void (*ReadAudioFileCallback)(const void *, const unsigned int, void *); +enum OutputFileStatus +{ + OUTPUT_FILE_STATUS_MUXING = 0, + OUTPUT_FILE_STATUS_FINISHED, + OUTPUT_FILE_STATUS_END +}; +#define OUTPUT_FILE_NAME_MAX 256 +typedef struct output_file_info +{ + char mFileName[OUTPUT_FILE_NAME_MAX]; ///< Name of the output file. Must be an absolute path. + char mThumbnailFileName[OUTPUT_FILE_NAME_MAX]; ///< Name of the thumbnail file. Must be an absolute path. + const bool mTakePicture; ///< Whether to take a picture. + const unsigned int mDuration_ms; ///< Duration of the output file in milliseconds. + int *const mFinished; ///< Whether the output file is finished. See OutputFileStatus. +} OutputFileInfo; +typedef void (*ReadVideoFileCallback)(const void *, const unsigned int, const unsigned long long, void *); +typedef void (*ReadAudioFileCallback)(const void *, const unsigned int, const unsigned long long, void *); void *ICreateMediaBase(const MediaHandleType type); // StatusCode Init(void *object); // StatusCode UnInit(void *object); @@ -47,9 +70,20 @@ StatusCode ISetReadAudioCallback(void *object, ReadVideoFileCallback callback, v StatusCode IStartReadFile(void *object, const char *path); StatusCode IStopReadFile(void *object); -StatusCode IOpenOutputFile(void *object, const char *fileName); +StatusCode IOpenOutputFile(void *object, const OutputFileInfo *info); StatusCode ICloseOutputFile(void *object); void IGetStreamData(void *object, const void *data, const size_t size, const StreamInfo streamInfo); +/** + * @brief When encapsulating a video, you ultimately need to obtain the video's duration information. + * + * @param object + * @return OutputFileInfo + */ +OutputFileInfo IGetOutputFileInfo(void *object); + +StatusCode IOpenJpegFile(void *object, const OutputFileInfo *info); +StatusCode ICloseJpegFile(void *object); +StatusCode IWriteJpegData(void *object, const void *data, const size_t size, const StreamInfo streamInfo); void IMediaBaseFree(void *object); #ifdef __cplusplus diff --git a/utils/MediaBase/src/FfmpegDecoder.cpp b/utils/MediaBase/src/FfmpegDecoder.cpp index 8be9226..c5dff05 100644 --- a/utils/MediaBase/src/FfmpegDecoder.cpp +++ b/utils/MediaBase/src/FfmpegDecoder.cpp @@ -25,6 +25,7 @@ extern "C" { #include #include #include +#include #include #ifdef __cplusplus } @@ -35,7 +36,13 @@ extern "C" { #include #include FfmpegDecoder::FfmpegDecoder(const enum AVCodecID &codecId) - : mCodecId(codecId), mCodec(nullptr), mCodecCtx(nullptr), mFrame(nullptr), mPacket(nullptr), mParser(nullptr) + : mCodecId(codecId), mCodec(nullptr), mCodecCtx(nullptr), mFrame(nullptr), mPacket(nullptr), mParser(nullptr), + mVideoWidth(DECODER_UNSUPORTED), mVideoHeight(DECODER_UNSUPORTED) +{ +} +FfmpegDecoder::FfmpegDecoder(const enum AVCodecID &codecId, const int &width, const int &height) + : mCodecId(codecId), mCodec(nullptr), mCodecCtx(nullptr), mFrame(nullptr), mPacket(nullptr), mParser(nullptr), + mVideoWidth(width), mVideoHeight(height) { } bool FfmpegDecoder::Init(void) @@ -63,18 +70,26 @@ bool FfmpegDecoder::Init(void) /* check that the encoder supports s16 pcm input */ mCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; if (!check_sample_fmt(mCodec, mCodecCtx->sample_fmt)) { - LogError("Encoder does not support sample format %s", av_get_sample_fmt_name(mCodecCtx->sample_fmt)); + LogError("decoder does not support sample format %s", av_get_sample_fmt_name(mCodecCtx->sample_fmt)); return false; } /* select other audio parameters supported by the encoder */ mCodecCtx->sample_rate = select_sample_rate(mCodec); + LogInfo("decoder sample_rate:%d\n", mCodecCtx->sample_rate); + // const AVChannelLayout src = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + // av_channel_layout_copy(&mCodecCtx->ch_layout, &src); ret = select_channel_layout(mCodec, &(mCodecCtx->ch_layout)); if (ret < 0) { LogError("Could not set channel layout\n"); return false; } } + else { + mCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; + mCodecCtx->width = mVideoWidth; + mCodecCtx->height = mVideoHeight; + } if ((ret = avcodec_open2(mCodecCtx, mCodec, nullptr)) < 0) { char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; LogError("Could not open codec:%s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); @@ -90,48 +105,49 @@ bool FfmpegDecoder::Init(void) LogError("Could not allocate video frame\n"); return false; } - mParser = av_parser_init(mCodec->id); - if (!mParser) { - LogError("mParser not found : %s\n", avcodec_get_name(mCodec->id)); - return false; - } + // mParser = av_parser_init(mCodec->id); + // if (!mParser) { + // LogError("mParser not found : %s\n", avcodec_get_name(mCodec->id)); + // return false; + // } if (AVMEDIA_TYPE_AUDIO == mCodec->type) { - mFrame->nb_samples = mCodecCtx->frame_size; - mFrame->format = mCodecCtx->sample_fmt; + // mFrame->nb_samples = mCodecCtx->frame_size; + // mFrame->format = mCodecCtx->sample_fmt; ret = av_channel_layout_copy(&(mFrame->ch_layout), &(mCodecCtx->ch_layout)); if (ret < 0) { LogError("Could not copy channel layout\n"); return false; } } + LogInfo("init success pix_fmt = %d\n", mCodecCtx->pix_fmt); return true; } bool FfmpegDecoder::UnInit(void) { - if (mFrame) { - av_frame_free(&mFrame); - mFrame = nullptr; - } - if (mCodecCtx) { - avcodec_free_context(&mCodecCtx); - mCodecCtx = nullptr; - } + LogInfo("uninit %s\n", avcodec_get_name(mCodecId)); av_packet_free(&mPacket); mPacket = nullptr; if (mParser) { av_parser_close(mParser); mParser = nullptr; } + avcodec_free_context(&mCodecCtx); + mCodecCtx = nullptr; + if (mFrame) { + av_frame_free(&mFrame); + mFrame = nullptr; + } return true; } -void FfmpegDecoder::DecodeData(const void *data, const size_t &size, std::function callback) +void FfmpegDecoder::DecodeData(const void *data, const size_t &size, const unsigned long long &pts, + std::function callback) { if (nullptr == mParser) { mPacket->data = (uint8_t *)data; mPacket->size = size; - // mPacket->stream_index = 0; - mPacket->pts = AV_NOPTS_VALUE; - mPacket->dts = AV_NOPTS_VALUE; + mPacket->pts = pts; + mPacket->dts = mPacket->pts; + // LogInfo("source data mPacket->pts:%d\n", mPacket->pts); AVDecodeData(mPacket, callback); return; } @@ -167,6 +183,34 @@ void inline FfmpegDecoder::AVParseData(const void *data, const size_t &size, } } } +// static void save_code_stream_file(const void *data, const size_t &size) +// { +// char OutPath[128] = {0}; +// const void *pData = data; +// FILE *file = NULL; +// LogInfo("save_code_stream_file size = %d\n", size); +// sprintf(OutPath, "./test.yuv"); +// file = fopen(OutPath, "a+"); + +// if (file) { // TODO: Don't open very time. +// fwrite(pData, 1, size, file); +// fflush(file); +// } + +// if (file) +// fclose(file); +// } +// static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize, char *filename) +// { +// FILE *f; +// int i; + +// f = fopen(filename, "wb"); +// fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255); +// for (i = 0; i < ysize; i++) +// fwrite(buf + i * wrap, 1, xsize, f); +// fclose(f); +// } void inline FfmpegDecoder::AVDecodeData(AVPacket *pkt, std::function callback) { int ret = avcodec_send_packet(mCodecCtx, pkt); @@ -185,6 +229,17 @@ void inline FfmpegDecoder::AVDecodeData(AVPacket *pkt, std::functionsample_fmt); + // for (i = 0; i < mFrame->nb_samples; i++) + // for (ch = 0; ch < mCodecCtx->ch_layout.nb_channels; ch++) + // // fwrite(frame->data[ch] + data_size * i, 1, data_size, outfile); + // save_code_stream_file(mFrame->data[ch] + data_size * i, data_size); + // save_code_stream_file(mFrame->data[0], mFrame->linesize[0]); + // if (mCodecId == AV_CODEC_ID_H264) { + // pgm_save(mFrame->data[0], mFrame->linesize[0], mFrame->width, mFrame->height, "./test.yuv"); + // } + // LogInfo("decode frame pts = %llu, nb_samples = %d\n", mFrame->pts, mFrame->nb_samples); callback(mFrame); } break; diff --git a/utils/MediaBase/src/FfmpegDecoder.h b/utils/MediaBase/src/FfmpegDecoder.h index 3145704..9bec571 100644 --- a/utils/MediaBase/src/FfmpegDecoder.h +++ b/utils/MediaBase/src/FfmpegDecoder.h @@ -33,14 +33,23 @@ extern "C" { } #endif #include +constexpr int DECODER_UNSUPORTED = 0; class FfmpegDecoder { public: - FfmpegDecoder(const enum AVCodecID &codecId); + FfmpegDecoder(const enum AVCodecID &codecId); // TODO: should be delete + /** + * @brief When decoding a video stream, you need to use this constructor to provide the required parameters. + * @param codecId Video stream format + * @param width Video height + * @param height Video width + */ + FfmpegDecoder(const enum AVCodecID &codecId, const int &width, const int &height); virtual ~FfmpegDecoder() = default; bool Init(void); bool UnInit(void); - void DecodeData(const void *data, const size_t &size, std::function callback); + void DecodeData(const void *data, const size_t &size, const unsigned long long &pts, + std::function callback); private: void AVParseData(const void *data, const size_t &size, std::function callback); @@ -58,5 +67,7 @@ private: AVFrame *mFrame; AVPacket *mPacket; AVCodecParserContext *mParser; + const int mVideoWidth; + const int mVideoHeight; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegDecoderV2.cpp b/utils/MediaBase/src/FfmpegDecoderV2.cpp new file mode 100644 index 0000000..ed67d59 --- /dev/null +++ b/utils/MediaBase/src/FfmpegDecoderV2.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FfmpegDecoderV2.h" +#include "ILog.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +#include +#include +FfmpegDecoderV2::FfmpegDecoderV2(const enum AVCodecID &codecId, const AVPixelFormat &decodePixelFormat, + const int &width, const int &height) + : mCodecId(codecId), mCodec(nullptr), mCodecCtx(nullptr), mFrame(nullptr), mPacket(nullptr), mParser(nullptr), + mVideoWidth(width), mVideoHeight(height), mDecodePixelFormat(decodePixelFormat) +{ +} +bool FfmpegDecoderV2::Init(void) +{ + int ret = 0; + LogInfo("find decoder : %s\n", avcodec_get_name(mCodecId)); + mCodec = (AVCodec *)avcodec_find_decoder(mCodecId); + // mCodec = (AVCodec *)avcodec_find_decoder_by_name("libfdk_aac"); + if (!(mCodec)) { + LogError("decoder not found:%s\n", avcodec_get_name(mCodecId)); + return false; + } + mCodecCtx = avcodec_alloc_context3((const AVCodec *)(mCodec)); + if (!(mCodecCtx)) { + LogError("Could not allocate codec context\n"); + return false; + } + if (AVMEDIA_TYPE_AUDIO == mCodec->type) { + LogInfo("Audio decoder.\n"); + /* put sample parameters */ + mCodecCtx->bit_rate = 64000; + // mCodecCtx->bit_rate = 352800; + // mCodecCtx->sample_rate = 8000; + + /* check that the encoder supports s16 pcm input */ + mCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; + if (!check_sample_fmt(mCodec, mCodecCtx->sample_fmt)) { + LogError("decoder does not support sample format %s", av_get_sample_fmt_name(mCodecCtx->sample_fmt)); + return false; + } + + /* select other audio parameters supported by the encoder */ + mCodecCtx->sample_rate = select_sample_rate(mCodec); + LogInfo("decoder sample_rate:%d\n", mCodecCtx->sample_rate); + // const AVChannelLayout src = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + // av_channel_layout_copy(&mCodecCtx->ch_layout, &src); + ret = select_channel_layout(mCodec, &(mCodecCtx->ch_layout)); + if (ret < 0) { + LogError("Could not set channel layout\n"); + return false; + } + } + else { + mCodecCtx->pix_fmt = mDecodePixelFormat; + mCodecCtx->width = mVideoWidth; + mCodecCtx->height = mVideoHeight; + } + if ((ret = avcodec_open2(mCodecCtx, mCodec, nullptr)) < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogError("Could not open codec:%s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + return false; + } + mFrame = av_frame_alloc(); + if (!mFrame) { + LogError("Could not allocate video frame\n"); + return false; + } + mPacket = av_packet_alloc(); + if (!mPacket) { + LogError("Could not allocate video frame\n"); + return false; + } + // mParser = av_parser_init(mCodec->id); + // if (!mParser) { + // LogError("mParser not found : %s\n", avcodec_get_name(mCodec->id)); + // return false; + // } + if (AVMEDIA_TYPE_AUDIO == mCodec->type) { + // mFrame->nb_samples = mCodecCtx->frame_size; + // mFrame->format = mCodecCtx->sample_fmt; + ret = av_channel_layout_copy(&(mFrame->ch_layout), &(mCodecCtx->ch_layout)); + if (ret < 0) { + LogError("Could not copy channel layout\n"); + return false; + } + } + LogInfo("init success pix_fmt = %d\n", mCodecCtx->pix_fmt); + return true; +} +bool FfmpegDecoderV2::UnInit(void) +{ + LogInfo("uninit %s\n", avcodec_get_name(mCodecId)); + av_packet_free(&mPacket); + mPacket = nullptr; + if (mParser) { + av_parser_close(mParser); + mParser = nullptr; + } + avcodec_free_context(&mCodecCtx); + mCodecCtx = nullptr; + if (mFrame) { + av_frame_free(&mFrame); + mFrame = nullptr; + } + return true; +} +void FfmpegDecoderV2::DecodeData(const void *data, const size_t &size, const unsigned long long &pts, + std::function callback) +{ + if (nullptr == mParser) { + mPacket->data = (uint8_t *)data; + mPacket->size = size; + mPacket->pts = pts; + mPacket->dts = mPacket->pts; + // LogInfo("source data mPacket->pts:%d\n", mPacket->pts); + AVDecodeData(mPacket, callback); + return; + } + AVParseData(data, size, callback); +} +void inline FfmpegDecoderV2::AVParseData(const void *data, const size_t &size, + std::function callback) +{ + if (nullptr == data) { + LogError("data is null\n"); + return; + } + uint8_t *frameData = (uint8_t *)data; + size_t data_size = size; + while (data_size > 0) { + int ret = av_parser_parse2(mParser, + mCodecCtx, + &mPacket->data, + &mPacket->size, + frameData, + data_size, + AV_NOPTS_VALUE, + AV_NOPTS_VALUE, + 0); + if (ret < 0) { + LogError("av_parse_frame failed\n"); + break; + } + frameData += ret; + data_size -= ret; + if (mPacket->size) { + AVDecodeData(mPacket, callback); + } + } +} +// static void save_code_stream_file(const void *data, const size_t &size) +// { +// char OutPath[128] = {0}; +// const void *pData = data; +// FILE *file = NULL; +// LogInfo("save_code_stream_file size = %d\n", size); +// sprintf(OutPath, "./test.yuv"); +// file = fopen(OutPath, "a+"); + +// if (file) { // TODO: Don't open very time. +// fwrite(pData, 1, size, file); +// fflush(file); +// } + +// if (file) +// fclose(file); +// } +// static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize, char *filename) +// { +// FILE *f; +// int i; + +// f = fopen(filename, "wb"); +// fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255); +// for (i = 0; i < ysize; i++) +// fwrite(buf + i * wrap, 1, xsize, f); +// fclose(f); +// } +void inline FfmpegDecoderV2::AVDecodeData(AVPacket *pkt, std::function callback) +{ + int ret = avcodec_send_packet(mCodecCtx, pkt); + if (ret < 0) { + LogError("Error sending a packet for decoding\n"); + av_packet_unref(pkt); + return; + } + while (ret >= 0) { + ret = avcodec_receive_frame(mCodecCtx, mFrame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } + if (ret < 0) { + LogError("Error during decoding\n"); + break; + } + if (callback) { + // int i, ch, data_size; + // data_size = av_get_bytes_per_sample(mCodecCtx->sample_fmt); + // for (i = 0; i < mFrame->nb_samples; i++) + // for (ch = 0; ch < mCodecCtx->ch_layout.nb_channels; ch++) + // // fwrite(frame->data[ch] + data_size * i, 1, data_size, outfile); + // save_code_stream_file(mFrame->data[ch] + data_size * i, data_size); + // save_code_stream_file(mFrame->data[0], mFrame->linesize[0]); + // if (mCodecId == AV_CODEC_ID_H264) { + // pgm_save(mFrame->data[0], mFrame->linesize[0], mFrame->width, mFrame->height, "./test.yuv"); + // } + // LogInfo("decode frame pts = %llu, nb_samples = %d\n", mFrame->pts, mFrame->nb_samples); + callback(mFrame); + } + break; + } + av_packet_unref(pkt); +} +/* just pick the highest supported samplerate */ +int FfmpegDecoderV2::select_sample_rate(const AVCodec *codec) +{ + const int *p; + int best_samplerate = 0; + + if (!codec->supported_samplerates) + return 44100; + + p = codec->supported_samplerates; + while (*p) { + if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate)) + best_samplerate = *p; + p++; + } + return best_samplerate; +} +/* select layout with the highest channel count */ +int FfmpegDecoderV2::select_channel_layout(const AVCodec *codec, AVChannelLayout *dst) +{ + const AVChannelLayout *p, *best_ch_layout = nullptr; + int best_nb_channels = 0; + AVChannelLayout channelLayout = AV_CHANNEL_LAYOUT_STEREO; + if (!codec->ch_layouts) + return av_channel_layout_copy(dst, &channelLayout); + + p = codec->ch_layouts; + while (p->nb_channels) { + int nb_channels = p->nb_channels; + + if (nb_channels > best_nb_channels) { + best_ch_layout = p; + best_nb_channels = nb_channels; + } + p++; + } + return av_channel_layout_copy(dst, best_ch_layout); +} +/* check that a given sample format is supported by the encoder */ +int FfmpegDecoderV2::check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) +{ + const enum AVSampleFormat *p = codec->sample_fmts; + + while (*p != AV_SAMPLE_FMT_NONE) { + if (*p == sample_fmt) + return 1; + p++; + } + return 0; +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegDecoderV2.h b/utils/MediaBase/src/FfmpegDecoderV2.h new file mode 100644 index 0000000..a3173b6 --- /dev/null +++ b/utils/MediaBase/src/FfmpegDecoderV2.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FFMPEG_DECODER_V2_H +#define FFMPEG_DECODER_V2_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +// constexpr int DECODER_UNSUPORTED = 0; +class FfmpegDecoderV2 +{ +public: + /** + * @brief When decoding a video stream, you need to use this constructor to provide the required parameters. + * @param codecId Video stream format + * @param width Video height + * @param height Video width + */ + FfmpegDecoderV2(const enum AVCodecID &codecId, const AVPixelFormat &decodePixelFormat, const int &width, + const int &height); + virtual ~FfmpegDecoderV2() = default; + bool Init(void); + bool UnInit(void); + void DecodeData(const void *data, const size_t &size, const unsigned long long &pts, + std::function callback); + +private: + void AVParseData(const void *data, const size_t &size, std::function callback); + void AVDecodeData(AVPacket *pkt, std::function callback); + +private: + static int select_sample_rate(const AVCodec *codec); + static int select_channel_layout(const AVCodec *codec, AVChannelLayout *dst); + static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt); + +private: + const enum AVCodecID mCodecId; + AVCodec *mCodec; + AVCodecContext *mCodecCtx; + AVFrame *mFrame; + AVPacket *mPacket; + AVCodecParserContext *mParser; + const int mVideoWidth; + const int mVideoHeight; + const AVPixelFormat mDecodePixelFormat; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegEncoder.cpp b/utils/MediaBase/src/FfmpegEncoder.cpp index 1962242..78a128e 100644 --- a/utils/MediaBase/src/FfmpegEncoder.cpp +++ b/utils/MediaBase/src/FfmpegEncoder.cpp @@ -22,7 +22,7 @@ extern "C" { #include #include #include -#include +// #include #include #include #include @@ -39,15 +39,21 @@ extern "C" { #include #include #include +constexpr long SOURCE_AUDIO_SAMPEL_RATE = 8000; #define STREAM_DURATION 10.0 #define STREAM_FRAME_RATE 25 /* 25 images/s */ #define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ FfmpegEncoder::FfmpegEncoder(const enum AVCodecID &codecId) : mCodecId(codecId), mCodecCtx(nullptr), mCodec(nullptr), mFrame(nullptr), mTmpFrame(nullptr), mTmpPkt(nullptr), - mSamplesCount(0), mSwrCtx(nullptr), next_pts(0) + mSamplesCount(0), mSwrCtx(nullptr), next_pts(0), mVideoWidth(ENCODER_UNSUPORTED), mVideoHeight(ENCODER_UNSUPORTED) { } -bool FfmpegEncoder::Init(int &outputFlags) +FfmpegEncoder::FfmpegEncoder(const enum AVCodecID &codecId, const int &width, const int &height) + : mCodecId(codecId), mCodecCtx(nullptr), mCodec(nullptr), mFrame(nullptr), mTmpFrame(nullptr), mTmpPkt(nullptr), + mSamplesCount(0), mSwrCtx(nullptr), next_pts(0), mVideoWidth(width), mVideoHeight(height) +{ +} +bool FfmpegEncoder::Init(const int &outputFlags) { mTmpPkt = av_packet_alloc(); if (!mTmpPkt) { @@ -67,11 +73,12 @@ bool FfmpegEncoder::Init(int &outputFlags) LogError("Could not alloc an encoding context\n"); return false; } - const AVChannelLayout src = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; + const AVChannelLayout src = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; switch (mCodec->type) { case AVMEDIA_TYPE_AUDIO: mCodecCtx->sample_fmt = mCodec->sample_fmts ? mCodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; mCodecCtx->bit_rate = 64000; + // mCodecCtx->bit_rate = 24000; mCodecCtx->sample_rate = 44100; if (mCodec->supported_samplerates) { mCodecCtx->sample_rate = mCodec->supported_samplerates[0]; @@ -80,17 +87,20 @@ bool FfmpegEncoder::Init(int &outputFlags) mCodecCtx->sample_rate = 44100; } } - mCodecCtx->sample_rate = 8000; + mCodecCtx->sample_rate = 16000; + // mCodecCtx->time_base = (AVRational){1, mCodecCtx->sample_rate}; + // mCodecCtx->ch_layout.nb_channels = 1; + // av_channel_layout_default(&mCodecCtx->ch_layout, 1); av_channel_layout_copy(&mCodecCtx->ch_layout, &src); break; case AVMEDIA_TYPE_VIDEO: mCodecCtx->codec_id = mCodecId; - mCodecCtx->bit_rate = 400000; + mCodecCtx->bit_rate = 300000; /* Resolution must be a multiple of two. */ - mCodecCtx->width = 1920; - mCodecCtx->height = 2160; + mCodecCtx->width = mVideoWidth; + mCodecCtx->height = mVideoHeight; /* timebase: This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. For fixed-fps content, * timebase should be 1/framerate and timestamp increments should be @@ -98,7 +108,7 @@ bool FfmpegEncoder::Init(int &outputFlags) mCodecCtx->time_base = (AVRational){1, STREAM_FRAME_RATE}; mCodecCtx->gop_size = 12; /* emit one intra frame every twelve frames at most */ - mCodecCtx->pix_fmt = STREAM_PIX_FMT; + mCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; if (mCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { /* just for testing, we also add B-frames */ mCodecCtx->max_b_frames = 2; @@ -165,6 +175,23 @@ bool FfmpegEncoder::OpenEncoder(AVDictionary *optArg, AVStream *stream) return false; } } +// static void save_code_stream_file(const void *data, const size_t &size) +// { +// char OutPath[16]; +// const void *pData = data; +// FILE *file = NULL; +// LogInfo("save_code_stream_file: %d\n", size); +// sprintf(OutPath, "./test.jpg"); +// file = fopen(OutPath, "a+"); + +// if (file) { // TODO: Don't open very time. +// fwrite(pData, 1, size, file); +// fflush(file); +// } + +// if (file) +// fclose(file); +// } int FfmpegEncoder::EncodeData(AVFrame *frame, AVStream *stream, std::function callback) { int ret = 0; @@ -198,9 +225,17 @@ int FfmpegEncoder::EncodeData(AVFrame *frame, AVStream *stream, std::functiontime_base, stream->time_base); + // LogInfo("Write mCodecCtx->time_base.num: %d\n", mCodecCtx->time_base.num); + // LogInfo("Write mCodecCtx->time_base.den: %d\n", mCodecCtx->time_base.den); + // LogInfo("Write stream->time_base.num: %d\n", stream->time_base.num); + // LogInfo("Write stream->time_base.den: %d\n", stream->time_base.den); mTmpPkt->stream_index = stream->index; + // LogInfo(" Write frame mTmpPkt->pts: %llu\n", mTmpPkt->pts); if (callback) { + // if (mCodecId == AV_CODEC_ID_MJPEG) { + // save_code_stream_file(mTmpPkt->data, mTmpPkt->size); + // } callback(mTmpPkt); } } @@ -212,6 +247,8 @@ bool FfmpegEncoder::OpenVideo(AVDictionary *optArg, AVStream *stream) int ret = 0; AVDictionary *opt = nullptr; av_dict_copy(&opt, optArg, 0); + // av_dict_set(&opt, "strict_std_compliance", "experimental", 0); + av_opt_set(mCodecCtx, "strict", "unofficial", 0); // Add for jpeg /* open the codec */ ret = avcodec_open2(mCodecCtx, mCodec, &opt); av_dict_free(&opt); @@ -239,6 +276,7 @@ bool FfmpegEncoder::OpenVideo(AVDictionary *optArg, AVStream *stream) LogError("Could not copy the stream parameters\n"); return false; } + LogInfo(" Open video success, mCodecCtx->pix_fmt = %d\n", mCodecCtx->pix_fmt); return true; } bool FfmpegEncoder::OpenAudio(AVDictionary *optArg, AVStream *stream) @@ -260,7 +298,7 @@ bool FfmpegEncoder::OpenAudio(AVDictionary *optArg, AVStream *stream) else nb_samples = mCodecCtx->frame_size; mFrame = alloc_audio_frame(mCodecCtx->sample_fmt, &mCodecCtx->ch_layout, mCodecCtx->sample_rate, nb_samples); - mTmpFrame = alloc_audio_frame(AV_SAMPLE_FMT_S16, &mCodecCtx->ch_layout, mCodecCtx->sample_rate, nb_samples); + // mTmpFrame = alloc_audio_frame(AV_SAMPLE_FMT_S16, &mCodecCtx->ch_layout, mCodecCtx->sample_rate, nb_samples); /* copy the stream parameters to the muxer */ ret = avcodec_parameters_from_context(stream->codecpar, mCodecCtx); if (ret < 0) { @@ -273,9 +311,13 @@ bool FfmpegEncoder::OpenAudio(AVDictionary *optArg, AVStream *stream) LogError("Could not allocate resampler context\n"); return false; } + const AVChannelLayout src = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + AVChannelLayout ch_layout; + av_channel_layout_copy(&ch_layout, &src); /* set options */ - av_opt_set_chlayout(mSwrCtx, "in_chlayout", &mCodecCtx->ch_layout, 0); - av_opt_set_int(mSwrCtx, "in_sample_rate", mCodecCtx->sample_rate, 0); + av_opt_set_chlayout(mSwrCtx, "in_chlayout", &ch_layout, 0); + // av_opt_set_chlayout(mSwrCtx, "in_chlayout", &mCodecCtx->ch_layout, 0); + av_opt_set_int(mSwrCtx, "in_sample_rate", SOURCE_AUDIO_SAMPEL_RATE, 0); av_opt_set_sample_fmt(mSwrCtx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); av_opt_set_chlayout(mSwrCtx, "out_chlayout", &mCodecCtx->ch_layout, 0); av_opt_set_int(mSwrCtx, "out_sample_rate", mCodecCtx->sample_rate, 0); @@ -293,17 +335,18 @@ AVFrame *FfmpegEncoder::ConvertAudioFrame(AVFrame *decodeFrame, struct SwrContex LogError("decodeFrame is null\n"); return nullptr; } - decodeFrame->pts = next_pts; - next_pts += decodeFrame->nb_samples; + // LogInfo("decodeFrame->pts = %d\n", decodeFrame->pts); + // decodeFrame->pts = next_pts; + // next_pts += decodeFrame->nb_samples; int ret = 0; int dst_nb_samples = 0; /* convert samples from native format to destination codec format, using the resampler */ /* compute destination number of samples */ dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, mCodecCtx->sample_rate) + decodeFrame->nb_samples, mCodecCtx->sample_rate, - mCodecCtx->sample_rate, + SOURCE_AUDIO_SAMPEL_RATE, AV_ROUND_UP); - av_assert0(dst_nb_samples == decodeFrame->nb_samples); + // av_assert0(dst_nb_samples == decodeFrame->nb_samples); /* when we pass a frame to the encoder, it may keep a reference to it * internally; @@ -322,8 +365,12 @@ AVFrame *FfmpegEncoder::ConvertAudioFrame(AVFrame *decodeFrame, struct SwrContex LogError("Error while converting\n"); return nullptr; } - - mFrame->pts = av_rescale_q(mSamplesCount, (AVRational){1, mCodecCtx->sample_rate}, mCodecCtx->time_base); + // LogInfo("mCodecCtx->time_base.num = %d, mCodecCtx->time_base.den=%d\n", + // mCodecCtx->time_base.num, + // mCodecCtx->time_base.den); + mFrame->pts = av_rescale_q(decodeFrame->pts, (AVRational){1, 1000000}, mCodecCtx->time_base); + // LogInfo("decodeFrame->pts = %d\n", decodeFrame->pts); + // LogInfo("mFrame->pts = %d\n", mFrame->pts); mSamplesCount += dst_nb_samples; return mFrame; } @@ -354,7 +401,7 @@ AVFrame *FfmpegEncoder::alloc_audio_frame(enum AVSampleFormat sample_fmt, const { AVFrame *frame = av_frame_alloc(); if (!frame) { - LogInfo("Error allocating an audio frame\n"); + LogError("Error allocating an audio frame\n"); return nullptr; } @@ -365,7 +412,7 @@ AVFrame *FfmpegEncoder::alloc_audio_frame(enum AVSampleFormat sample_fmt, const if (nb_samples) { if (av_frame_get_buffer(frame, 0) < 0) { - LogInfo("Error allocating an audio buffer\n"); + LogError("Error allocating an audio buffer\n"); return nullptr; } } diff --git a/utils/MediaBase/src/FfmpegEncoder.h b/utils/MediaBase/src/FfmpegEncoder.h index 9686cf4..cffcfe4 100644 --- a/utils/MediaBase/src/FfmpegEncoder.h +++ b/utils/MediaBase/src/FfmpegEncoder.h @@ -33,12 +33,20 @@ extern "C" { } #endif #include +constexpr int ENCODER_UNSUPORTED = 0; class FfmpegEncoder { public: FfmpegEncoder(const enum AVCodecID &codecId); + /** + * @brief When encoding a video stream, you need to use this constructor to provide the required parameters. + * @param codecId Video stream format. + * @param width Video width. + * @param height Video height. + */ + FfmpegEncoder(const enum AVCodecID &codecId, const int &width, const int &height); virtual ~FfmpegEncoder() = default; - bool Init(int &outputFlags); + bool Init(const int &outputFlags); void UnInit(void); AVRational GetTimeBase(void); bool OpenEncoder(AVDictionary *optArg, AVStream *stream); @@ -64,5 +72,7 @@ private: int mSamplesCount; struct SwrContext *mSwrCtx; int64_t next_pts; + const int mVideoWidth; + const int mVideoHeight; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegEncoderV2.cpp b/utils/MediaBase/src/FfmpegEncoderV2.cpp new file mode 100644 index 0000000..bf95be9 --- /dev/null +++ b/utils/MediaBase/src/FfmpegEncoderV2.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FfmpegEncoderV2.h" +#include "ILog.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +constexpr long SOURCE_AUDIO_SAMPEL_RATE = 8000; +#define STREAM_DURATION 10.0 +#define STREAM_FRAME_RATE 25 /* 25 images/s */ +#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ +FfmpegEncoderV2::FfmpegEncoderV2(const enum AVCodecID &codecId, const AVPixelFormat &encodePixelFormat, + const int &width, const int &height) + : mCodecId(codecId), mCodecCtx(nullptr), mCodec(nullptr), mFrame(nullptr), mTmpFrame(nullptr), mTmpPkt(nullptr), + mSamplesCount(0), mSwrCtx(nullptr), next_pts(0), mVideoWidth(width), mVideoHeight(height), + mEncodePixelFormat(encodePixelFormat) +{ +} +bool FfmpegEncoderV2::Init(const int &outputFlags) +{ + mTmpPkt = av_packet_alloc(); + if (!mTmpPkt) { + LogError("Could not allocate AVPacket\n"); + return false; + } + LogInfo("find encoder : %s\n", avcodec_get_name(mCodecId)); + int i = 0; + /* find the encoder */ + mCodec = (AVCodec *)avcodec_find_encoder(mCodecId); + if (!mCodec) { + LogError("Could not find encoder for '%s'\n", avcodec_get_name(mCodecId)); + return false; + } + mCodecCtx = avcodec_alloc_context3(mCodec); + if (!mCodecCtx) { + LogError("Could not alloc an encoding context\n"); + return false; + } + const AVChannelLayout src = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + switch (mCodec->type) { + case AVMEDIA_TYPE_AUDIO: + mCodecCtx->sample_fmt = mCodec->sample_fmts ? mCodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; + mCodecCtx->bit_rate = 64000; + // mCodecCtx->bit_rate = 24000; + mCodecCtx->sample_rate = 44100; + if (mCodec->supported_samplerates) { + mCodecCtx->sample_rate = mCodec->supported_samplerates[0]; + for (i = 0; mCodec->supported_samplerates[i]; i++) { + if (mCodec->supported_samplerates[i] == 44100) + mCodecCtx->sample_rate = 44100; + } + } + mCodecCtx->sample_rate = 16000; + // mCodecCtx->time_base = (AVRational){1, mCodecCtx->sample_rate}; + // mCodecCtx->ch_layout.nb_channels = 1; + // av_channel_layout_default(&mCodecCtx->ch_layout, 1); + av_channel_layout_copy(&mCodecCtx->ch_layout, &src); + break; + + case AVMEDIA_TYPE_VIDEO: + mCodecCtx->codec_id = mCodecId; + + mCodecCtx->bit_rate = 300000; + /* Resolution must be a multiple of two. */ + mCodecCtx->width = mVideoWidth; + mCodecCtx->height = mVideoHeight; + /* timebase: This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. For fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identical to 1. */ + mCodecCtx->time_base = (AVRational){1, STREAM_FRAME_RATE}; + + mCodecCtx->gop_size = 12; /* emit one intra frame every twelve frames at most */ + mCodecCtx->pix_fmt = mEncodePixelFormat; + if (mCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B-frames */ + mCodecCtx->max_b_frames = 2; + } + if (mCodecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + * This does not happen with normal video, it just happens here as + * the motion of the chroma plane does not match the luma plane. */ + mCodecCtx->mb_decision = 2; + } + break; + + default: + break; + } + /* Some formats want stream headers to be separate. */ + if (outputFlags & AVFMT_GLOBALHEADER) { + mCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + } + return true; +} +void FfmpegEncoderV2::UnInit(void) +{ + if (mFrame) { + av_frame_free(&mFrame); + mFrame = nullptr; + } + if (mTmpFrame) { + av_frame_free(&mTmpFrame); + mTmpFrame = nullptr; + } + if (mCodecCtx) { + avcodec_free_context(&mCodecCtx); + mCodecCtx = nullptr; + } + av_packet_free(&mTmpPkt); + swr_free(&mSwrCtx); +} +AVRational FfmpegEncoderV2::GetTimeBase(void) +{ + switch (mCodec->type) { + case AVMEDIA_TYPE_AUDIO: + return (AVRational){1, mCodecCtx->sample_rate}; + + case AVMEDIA_TYPE_VIDEO: + return mCodecCtx->time_base; + + default: + LogError("Unsupported media type.\n"); + return (AVRational){0, -1}; + } +} +bool FfmpegEncoderV2::OpenEncoder(AVDictionary *optArg, AVStream *stream) +{ + switch (mCodec->type) { + case AVMEDIA_TYPE_AUDIO: + return OpenAudio(optArg, stream); + + case AVMEDIA_TYPE_VIDEO: + return OpenVideo(optArg, stream); + + default: + LogError("Unsupported media type.\n"); + return false; + } +} +// static void save_code_stream_file(const void *data, const size_t &size) +// { +// char OutPath[16]; +// const void *pData = data; +// FILE *file = NULL; +// LogInfo("save_code_stream_file: %d\n", size); +// sprintf(OutPath, "./test.jpg"); +// file = fopen(OutPath, "a+"); + +// if (file) { // TODO: Don't open very time. +// fwrite(pData, 1, size, file); +// fflush(file); +// } + +// if (file) +// fclose(file); +// } +int FfmpegEncoderV2::EncodeData(AVFrame *frame, AVStream *stream, std::function callback) +{ + int ret = 0; + AVFrame *tmpFrame = frame; + if (AVMEDIA_TYPE_AUDIO == mCodec->type) { + tmpFrame = ConvertAudioFrame(frame, mSwrCtx); + } + if (!tmpFrame) { + LogError("Could not convert audio frame.\n"); + return AVERROR_EXIT; + } + // send the frame to the encoder + ret = avcodec_send_frame(mCodecCtx, tmpFrame); + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogInfo("Error sending a frame to the encoder: %s\n", + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + return AVERROR_EXIT; + } + + while (ret >= 0) { + ret = avcodec_receive_packet(mCodecCtx, mTmpPkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogInfo("Error encoding a frame: %s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + return AVERROR_EXIT; + } + + /* rescale output packet timestamp values from codec to stream timebase */ + av_packet_rescale_ts(mTmpPkt, mCodecCtx->time_base, stream->time_base); + // LogInfo("Write mCodecCtx->time_base.num: %d\n", mCodecCtx->time_base.num); + // LogInfo("Write mCodecCtx->time_base.den: %d\n", mCodecCtx->time_base.den); + // LogInfo("Write stream->time_base.num: %d\n", stream->time_base.num); + // LogInfo("Write stream->time_base.den: %d\n", stream->time_base.den); + mTmpPkt->stream_index = stream->index; + // LogInfo(" Write frame mTmpPkt->pts: %llu\n", mTmpPkt->pts); + + if (callback) { + // if (mCodecId == AV_CODEC_ID_MJPEG) { + // save_code_stream_file(mTmpPkt->data, mTmpPkt->size); + // } + callback(mTmpPkt); + } + } + + return ret == AVERROR_EOF ? 1 : 0; +} +bool FfmpegEncoderV2::OpenVideo(AVDictionary *optArg, AVStream *stream) +{ + int ret = 0; + AVDictionary *opt = nullptr; + av_dict_copy(&opt, optArg, 0); + // av_dict_set(&opt, "strict_std_compliance", "experimental", 0); + av_opt_set(mCodecCtx, "strict", "unofficial", 0); // Add for jpeg + /* open the codec */ + ret = avcodec_open2(mCodecCtx, mCodec, &opt); + av_dict_free(&opt); + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogError("Could not open video codec: %s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + return false; + } + /* allocate and init a re-usable frame */ + mFrame = alloc_frame(mCodecCtx->pix_fmt, mCodecCtx->width, mCodecCtx->height); + if (!mFrame) { + LogError("Could not allocate video frame\n"); + return false; + } + if (mCodecCtx->pix_fmt != AV_PIX_FMT_YUV420P) { + mTmpFrame = alloc_frame(AV_PIX_FMT_YUV420P, mCodecCtx->width, mCodecCtx->height); + if (!mTmpFrame) { + LogError("Could not allocate temporary video frame\n"); + return false; + } + } + /* copy the stream parameters to the muxer */ + ret = avcodec_parameters_from_context(stream->codecpar, mCodecCtx); + if (ret < 0) { + LogError("Could not copy the stream parameters\n"); + return false; + } + LogInfo(" Open video success, mCodecCtx->pix_fmt = %d\n", mCodecCtx->pix_fmt); + return true; +} +bool FfmpegEncoderV2::OpenAudio(AVDictionary *optArg, AVStream *stream) +{ + int nb_samples = 0; + int ret = 0; + AVDictionary *opt = nullptr; + av_dict_copy(&opt, optArg, 0); + /* open it */ + ret = avcodec_open2(mCodecCtx, mCodec, &opt); + av_dict_free(&opt); + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogError("Could not open audio codec: %s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + return false; + } + if (mCodecCtx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) + nb_samples = 10000; + else + nb_samples = mCodecCtx->frame_size; + mFrame = alloc_audio_frame(mCodecCtx->sample_fmt, &mCodecCtx->ch_layout, mCodecCtx->sample_rate, nb_samples); + // mTmpFrame = alloc_audio_frame(AV_SAMPLE_FMT_S16, &mCodecCtx->ch_layout, mCodecCtx->sample_rate, nb_samples); + /* copy the stream parameters to the muxer */ + ret = avcodec_parameters_from_context(stream->codecpar, mCodecCtx); + if (ret < 0) { + LogError("Could not copy the stream parameters\n"); + return false; + } + /* create resampler context */ + mSwrCtx = swr_alloc(); + if (!mSwrCtx) { + LogError("Could not allocate resampler context\n"); + return false; + } + const AVChannelLayout src = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + AVChannelLayout ch_layout; + av_channel_layout_copy(&ch_layout, &src); + /* set options */ + av_opt_set_chlayout(mSwrCtx, "in_chlayout", &ch_layout, 0); + // av_opt_set_chlayout(mSwrCtx, "in_chlayout", &mCodecCtx->ch_layout, 0); + av_opt_set_int(mSwrCtx, "in_sample_rate", SOURCE_AUDIO_SAMPEL_RATE, 0); + av_opt_set_sample_fmt(mSwrCtx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); + av_opt_set_chlayout(mSwrCtx, "out_chlayout", &mCodecCtx->ch_layout, 0); + av_opt_set_int(mSwrCtx, "out_sample_rate", mCodecCtx->sample_rate, 0); + av_opt_set_sample_fmt(mSwrCtx, "out_sample_fmt", mCodecCtx->sample_fmt, 0); + /* initialize the resampling context */ + if ((ret = swr_init(mSwrCtx)) < 0) { + LogError("Failed to initialize the resampling context\n"); + return false; + } + return true; +} +AVFrame *FfmpegEncoderV2::ConvertAudioFrame(AVFrame *decodeFrame, struct SwrContext *swr_ctx) +{ + if (nullptr == decodeFrame) { + LogError("decodeFrame is null\n"); + return nullptr; + } + // LogInfo("decodeFrame->pts = %d\n", decodeFrame->pts); + // decodeFrame->pts = next_pts; + // next_pts += decodeFrame->nb_samples; + int ret = 0; + int dst_nb_samples = 0; + /* convert samples from native format to destination codec format, using the resampler */ + /* compute destination number of samples */ + dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, mCodecCtx->sample_rate) + decodeFrame->nb_samples, + mCodecCtx->sample_rate, + SOURCE_AUDIO_SAMPEL_RATE, + AV_ROUND_UP); + // av_assert0(dst_nb_samples == decodeFrame->nb_samples); + + /* when we pass a frame to the encoder, it may keep a reference to it + * internally; + * make sure we do not overwrite it here + */ + ret = av_frame_make_writable(mFrame); + if (ret < 0) { + LogError("av_frame_make_writable failed\n"); + return nullptr; + } + + /* convert to destination format */ + ret = swr_convert( + swr_ctx, mFrame->data, dst_nb_samples, (const uint8_t **)decodeFrame->data, decodeFrame->nb_samples); + if (ret < 0) { + LogError("Error while converting\n"); + return nullptr; + } + // LogInfo("mCodecCtx->time_base.num = %d, mCodecCtx->time_base.den=%d\n", + // mCodecCtx->time_base.num, + // mCodecCtx->time_base.den); + mFrame->pts = av_rescale_q(decodeFrame->pts, (AVRational){1, 1000000}, mCodecCtx->time_base); + // LogInfo("decodeFrame->pts = %d\n", decodeFrame->pts); + // LogInfo("mFrame->pts = %d\n", mFrame->pts); + mSamplesCount += dst_nb_samples; + return mFrame; +} +AVFrame *FfmpegEncoderV2::alloc_frame(enum AVPixelFormat pix_fmt, int width, int height) +{ + AVFrame *frame; + int ret; + + frame = av_frame_alloc(); + if (!frame) + return nullptr; + + frame->format = pix_fmt; + frame->width = width; + frame->height = height; + + /* allocate the buffers for the frame data */ + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) { + LogInfo("Could not allocate frame data.\n"); + return nullptr; + } + + return frame; +} +AVFrame *FfmpegEncoderV2::alloc_audio_frame(enum AVSampleFormat sample_fmt, const AVChannelLayout *channel_layout, + int sample_rate, int nb_samples) +{ + AVFrame *frame = av_frame_alloc(); + if (!frame) { + LogError("Error allocating an audio frame\n"); + return nullptr; + } + + frame->format = sample_fmt; + av_channel_layout_copy(&frame->ch_layout, channel_layout); + frame->sample_rate = sample_rate; + frame->nb_samples = nb_samples; + + if (nb_samples) { + if (av_frame_get_buffer(frame, 0) < 0) { + LogError("Error allocating an audio buffer\n"); + return nullptr; + } + } + + return frame; +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegEncoderV2.h b/utils/MediaBase/src/FfmpegEncoderV2.h new file mode 100644 index 0000000..69d2923 --- /dev/null +++ b/utils/MediaBase/src/FfmpegEncoderV2.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FFMPEG_ENCODER_V2_H +#define FFMPEG_ENCODER_V2_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +// constexpr int ENCODER_UNSUPORTED = 0; +class FfmpegEncoderV2 +{ +public: + /** + * @brief When encoding a video stream, you need to use this constructor to provide the required parameters. + * @param codecId Video stream format. + * @param width Video width. + * @param height Video height. + */ + FfmpegEncoderV2(const enum AVCodecID &codecId, const AVPixelFormat &encodePixelFormat, const int &width, + const int &height); + virtual ~FfmpegEncoderV2() = default; + bool Init(const int &outputFlags); + void UnInit(void); + AVRational GetTimeBase(void); + bool OpenEncoder(AVDictionary *optArg, AVStream *stream); + int EncodeData(AVFrame *frame, AVStream *stream, std::function callback); + +private: + bool OpenVideo(AVDictionary *optArg, AVStream *stream); + bool OpenAudio(AVDictionary *optArg, AVStream *stream); + AVFrame *ConvertAudioFrame(AVFrame *decodeFrame, struct SwrContext *swr_ctx); + +private: + static AVFrame *alloc_frame(enum AVPixelFormat pix_fmt, int width, int height); + static AVFrame *alloc_audio_frame(enum AVSampleFormat sample_fmt, const AVChannelLayout *channel_layout, + int sample_rate, int nb_samples); + +private: + const enum AVCodecID mCodecId; + AVCodecContext *mCodecCtx; + AVCodec *mCodec; + AVFrame *mFrame; + AVFrame *mTmpFrame; + AVPacket *mTmpPkt; + int mSamplesCount; + struct SwrContext *mSwrCtx; + int64_t next_pts; + const int mVideoWidth; + const int mVideoHeight; + const AVPixelFormat mEncodePixelFormat; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegMuxStream.cpp b/utils/MediaBase/src/FfmpegMuxStream.cpp index 3d10f94..d371e87 100644 --- a/utils/MediaBase/src/FfmpegMuxStream.cpp +++ b/utils/MediaBase/src/FfmpegMuxStream.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #ifdef __cplusplus extern "C" { #endif @@ -59,7 +58,7 @@ FfmpegMuxStream::FfmpegMuxStream() memset(&mVideoSt, 0, sizeof(mVideoSt)); memset(&mAudioSt, 0, sizeof(mAudioSt)); } -StatusCode FfmpegMuxStream::OpenOutputFile(const std::string &fileName) +StatusCode FfmpegMuxStream::OpenOutputFile(const OutputFileInfo &fileInfo) { if (!InitCodecVideo(AV_CODEC_ID_H264, &mCodecVideo, &mCodecVideoContext, &mFrameVideo)) { LogError("InitCodec failed\n"); @@ -72,10 +71,10 @@ StatusCode FfmpegMuxStream::OpenOutputFile(const std::string &fileName) int ret; AVFormatContext *oc = nullptr; int have_video = 0, have_audio = 0; - int encode_video = 0, encode_audio = 0; + // int encode_video = 0, encode_audio = 0; const AVCodec *audio_codec, *video_codec; AVDictionary *opt = nullptr; - avformat_alloc_output_context2(&oc, nullptr, "mp4", fileName.c_str()); + avformat_alloc_output_context2(&oc, nullptr, "mp4", fileInfo.mFileName); if (!oc) { LogError("Could not deduce output format from file extension: using MPEG.\n"); return CreateStatusCode(STATUS_CODE_NOT_OK); @@ -88,13 +87,13 @@ StatusCode FfmpegMuxStream::OpenOutputFile(const std::string &fileName) LogInfo("Add video stream\n"); add_stream(&mVideoSt, oc, &video_codec, fmt->video_codec); have_video = 1; - encode_video = 1; + // encode_video = 1; } if (fmt->audio_codec != AV_CODEC_ID_NONE) { LogInfo("Add audio stream\n"); add_stream(&mAudioSt, oc, &audio_codec, fmt->audio_codec); have_audio = 1; - encode_audio = 1; + // encode_audio = 1; } /* Now that all the parameters are set, we can open the audio and * video codecs and allocate the necessary encode buffers. */ if (have_video) { @@ -104,13 +103,13 @@ StatusCode FfmpegMuxStream::OpenOutputFile(const std::string &fileName) if (have_audio) { open_audio(oc, audio_codec, &mAudioSt, opt); } - av_dump_format(oc, 0, fileName.c_str(), 1); + av_dump_format(oc, 0, fileInfo.mFileName, 1); if (!(fmt->flags & AVFMT_NOFILE)) { - ret = avio_open(&oc->pb, fileName.c_str(), AVIO_FLAG_WRITE); + ret = avio_open(&oc->pb, fileInfo.mFileName, AVIO_FLAG_WRITE); if (ret < 0) { char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; LogError("Could not open '%s': %s\n", - fileName.c_str(), + fileInfo.mFileName, av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); return CreateStatusCode(STATUS_CODE_NOT_OK); } diff --git a/utils/MediaBase/src/FfmpegMuxStream.h b/utils/MediaBase/src/FfmpegMuxStream.h index 308cabc..2c1623b 100644 --- a/utils/MediaBase/src/FfmpegMuxStream.h +++ b/utils/MediaBase/src/FfmpegMuxStream.h @@ -22,7 +22,7 @@ public: virtual ~FfmpegMuxStream() = default; public: // About combine file. - StatusCode OpenOutputFile(const std::string &fileName) override; + StatusCode OpenOutputFile(const OutputFileInfo &fileInfo) override; StatusCode CloseOutputFile(void) override; void GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) override; diff --git a/utils/MediaBase/src/FfmpegMuxStreamV2.cpp b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp index fbc736d..7dc1114 100644 --- a/utils/MediaBase/src/FfmpegMuxStreamV2.cpp +++ b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp @@ -15,6 +15,7 @@ #include "FfmpegMuxStreamV2.h" #include "FfmpegOutputStream.h" #include "ILog.h" +#include "LinuxApi.h" #include "MediaBase.h" #include "StatusCode.h" #ifdef __cplusplus @@ -33,13 +34,18 @@ extern "C" { #include #include // #include +#include #include -FfmpegMuxStreamV2::FfmpegMuxStreamV2() : mOutputFormat(nullptr), mOptions(nullptr), mFilesMuxing(false) +constexpr unsigned long long MUXING_NOT_START = 0; +FfmpegMuxStreamV2::FfmpegMuxStreamV2() + : mOutputFormat(nullptr), mOptions(nullptr), mFilesMuxing(false), mFileMuxingDuration_us(0), + mStartPts(MUXING_NOT_START), mMuxingFinised(false) { } -StatusCode FfmpegMuxStreamV2::OpenOutputFile(const std::string &fileName) +StatusCode FfmpegMuxStreamV2::OpenOutputFile(const OutputFileInfo &fileInfo) { - return OpenMuxOutputFile(fileName); + mOutputFileInfo = std::make_shared(fileInfo); + return OpenMuxOutputFile(fileInfo.mFileName); } StatusCode FfmpegMuxStreamV2::CloseOutputFile(void) { @@ -60,38 +66,43 @@ StatusCode FfmpegMuxStreamV2::CloseOutputFile(void) avio_closep(&mOutputFormat->pb); } avformat_free_context(mOutputFormat); + fx_system_v2("sync"); + mOutputFileInfo.reset(); return CreateStatusCode(STATUS_CODE_OK); } void FfmpegMuxStreamV2::GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) { - int ret = 0; - if (!mFilesMuxing) { - bool fileMuxing = false; - fileMuxing = mVideoStream->CheckStreamHeader(data, size); - if (fileMuxing) { - /* Write the stream header, if any. */ - ret = avformat_write_header(mOutputFormat, nullptr); - if (ret < 0) { - char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; - LogError("Error occurred when opening output file: %s\n", - av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); - return; - } - mFilesMuxing = true; - } - else { - LogWarning("Stream header not found, skip this frame.\n"); - return; - } + if (mMuxingFinised) { + /** + * @brief Packaging has been completed according to the recording duration parameters, and the excess data + * frames will be discarded. + */ + return; + } + if (!MakeSureStreamHeanderOK(data, size)) { + return; } - LogInfo("Write frame size: %zu.\n", size); if (streamInfo.mType == STREAM_TYPE_VIDEO_H264 && mVideoStream) { - mVideoStream->WriteSourceData(data, size); + if (MUXING_NOT_START == mStartPts) { + mStartPts = streamInfo.mTimeStamp_us; + } + /** + * @brief Use the video's timestamp to count the playback duration of the packaged file. + */ + CalculatingDuration(streamInfo.mTimeStamp_us); + mVideoStream->WriteSourceData(data, size, streamInfo.mTimeStamp_us); } if (streamInfo.mType == STREAM_TYPE_AUDIO_G711A && mAudioStream) { - mAudioStream->WriteSourceData(data, size); + mAudioStream->WriteSourceData(data, size, streamInfo.mTimeStamp_us); } } +OutputFileInfo FfmpegMuxStreamV2::GetOutputFileInfo(void) +{ + OutputFileInfo finalFile = {.mDuration_ms = static_cast(mFileMuxingDuration_us / 1000), + .mFinished = nullptr}; + memcpy(finalFile.mFileName, mOutputFileInfo->mFileName, OUTPUT_FILE_NAME_MAX); + return finalFile; +} StatusCode inline FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) { AVDictionary *opt = nullptr; @@ -105,7 +116,8 @@ StatusCode inline FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileNa /* Add the audio and video streams using the default format codecs * and initialize the codecs. */ if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { - mVideoStream = AddStream(mOutputFormat, AV_CODEC_ID_NONE, AV_CODEC_ID_NONE); + const std::string thumbnailFileName = mOutputFileInfo->mThumbnailFileName; + mVideoStream = AddStream(mOutputFormat, AV_CODEC_ID_NONE, AV_CODEC_ID_NONE, thumbnailFileName); // mVideoStream = AddStream(mOutputFormat, mOutputFormat->oformat->video_codec, AV_CODEC_ID_H264); mVideoStream->SetWriteSourceDataCallback( std::bind(&FfmpegMuxStreamV2::GetAVPacketDataCallback, this, std::placeholders::_1)); @@ -126,7 +138,10 @@ StatusCode inline FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileNa av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); } } - return CreateStatusCode(STATUS_CODE_OK); + if (mVideoStream) { + return CreateStatusCode(STATUS_CODE_OK); + } + av_dict_set_int(&opt, "use_editlist", 0, 0); /* Write the stream header, if any. */ ret = avformat_write_header(mOutputFormat, &opt); if (ret < 0) { @@ -135,6 +150,7 @@ StatusCode inline FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileNa av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); return CreateStatusCode(STATUS_CODE_NOT_OK); } + av_dict_free(&opt); mFilesMuxing = true; return CreateStatusCode(STATUS_CODE_OK); } @@ -152,10 +168,57 @@ void FfmpegMuxStreamV2::GetAVPacketDataCallback(AVPacket *pkt) av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); } } +void FfmpegMuxStreamV2::CalculatingDuration(const unsigned long long &pts_us) +{ + // LogInfo("mFileMuxingDuration ms: %lld, mOutputFileInfo->mDuration_ms: %lld\n", + // mFileMuxingDuration_us / 1000, + // mOutputFileInfo->mDuration_ms); + mFileMuxingDuration_us = pts_us - mStartPts; + if (mFileMuxingDuration_us / 1000 >= mOutputFileInfo->mDuration_ms) { + LogInfo("Muxing file finished, duration: %lld ms\n", mFileMuxingDuration_us / 1000); + mMuxingFinised = true; + if (mOutputFileInfo && mOutputFileInfo->mFinished) { + *(mOutputFileInfo->mFinished) = static_cast(OUTPUT_FILE_STATUS_FINISHED); + } + } +} +bool inline FfmpegMuxStreamV2::MakeSureStreamHeanderOK(const void *data, const size_t &size) +{ + int ret = 0; + if (!mFilesMuxing) { + bool fileMuxing = false; + fileMuxing = mVideoStream->CheckStreamHeader(data, size); + if (fileMuxing) { + AVDictionary *opt = nullptr; + av_dict_set_int(&opt, "use_editlist", 0, 0); + /* Write the stream header, if any. */ + ret = avformat_write_header(mOutputFormat, nullptr); + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogError("Error occurred when opening output file: %s\n", + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + return false; + } + mFilesMuxing = true; + av_dict_free(&opt); + } + else { + LogWarning("Stream header not found, skip this frame.\n"); + return false; + } + } + return true; +} std::shared_ptr FfmpegMuxStreamV2::AddStream(AVFormatContext *outputFormat, - enum AVCodecID encodecId, enum AVCodecID decodecId) + enum AVCodecID encodecId, enum AVCodecID decodecId, + const std::string &thumbnailFile) { auto stream = std::make_shared(encodecId, decodecId); - stream->Init(outputFormat); + if (thumbnailFile.empty()) { + stream->Init(outputFormat); + } + else { + stream->Init(outputFormat, thumbnailFile); + } return stream; } \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegMuxStreamV2.h b/utils/MediaBase/src/FfmpegMuxStreamV2.h index 3d429de..31e8d14 100644 --- a/utils/MediaBase/src/FfmpegMuxStreamV2.h +++ b/utils/MediaBase/src/FfmpegMuxStreamV2.h @@ -45,17 +45,30 @@ public: virtual ~FfmpegMuxStreamV2() = default; public: // About combine file. - StatusCode OpenOutputFile(const std::string &fileName) override; + StatusCode OpenOutputFile(const OutputFileInfo &fileInfo) override; StatusCode CloseOutputFile(void) override; void GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) override; + OutputFileInfo GetOutputFileInfo(void) override; private: StatusCode OpenMuxOutputFile(const std::string &fileName); void GetAVPacketDataCallback(AVPacket *pkt); + void CalculatingDuration(const unsigned long long &pts_us); + bool MakeSureStreamHeanderOK(const void *data, const size_t &size); private: + /** + * @brief Add media streams (audio streams or video streams) to the output file. If the input stream data needs to + * be encoded or decoded, set the matching encoder/decoder parameters as needed.The AV_CODEC_ID_NONE parameter means + * that no codec needs to be set. + * @param outputFormat + * @param encodecId + * @param decodecId + * @return std::shared_ptr + */ static std::shared_ptr AddStream(AVFormatContext *outputFormat, enum AVCodecID encodecId, - enum AVCodecID decodecId); + enum AVCodecID decodecId, + const std::string &thumbnailFile = ""); private: std::mutex mMutex; @@ -64,5 +77,9 @@ private: std::shared_ptr mAudioStream; AVDictionary *mOptions; bool mFilesMuxing; + std::shared_ptr mOutputFileInfo; + unsigned long long mFileMuxingDuration_us; + unsigned long long mStartPts; + bool mMuxingFinised; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOriginalPicture.cpp b/utils/MediaBase/src/FfmpegOriginalPicture.cpp new file mode 100644 index 0000000..41da91d --- /dev/null +++ b/utils/MediaBase/src/FfmpegOriginalPicture.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FfmpegOriginalPicture.h" +#include "FfmpegEncoder.h" +#include "FfmpegThumbnail.h" +#include "ILog.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +#include +original_info::original_info(const int &srouceWidth, const int &srouceHeight, const std::string &thumbnail) + : mSrouceWidth(srouceWidth), mSrouceHeight(srouceHeight), mThumbnail(thumbnail) +{ +} +FfmpegOriginalPicture::FfmpegOriginalPicture(const AVCodecID &encodecId, const AVCodecID &decodecId) + : FfmpegThumbnail(encodecId, decodecId), mPictureInfo(nullptr) +{ +} +void FfmpegOriginalPicture::Init(const OriginalInfo &originalInfo) +{ + mPictureInfo = std::make_shared(originalInfo); + ThumbnailInfo thumbnailInfo(originalInfo.mSrouceWidth, originalInfo.mSrouceHeight, 192, 216); + FfmpegThumbnail::Init(thumbnailInfo); +} +void FfmpegOriginalPicture::UnInit(void) +{ + FfmpegThumbnail::UnInit(); + if (mEncoder) { + mEncoder->UnInit(); + mEncoder.reset(); + } +} +bool FfmpegOriginalPicture::CreateOriginalPicture(const std::string &outputFile, const void *data, const size_t &size) +{ + mEncodeCallback = std::bind(&FfmpegOriginalPicture::GetEncodeDataCallback, this, std::placeholders::_1, outputFile); + return FfmpegThumbnail::CreateThumbnail(mPictureInfo->mThumbnail, data, size); +} +void FfmpegOriginalPicture::EncodeDataToPicture(AVFrame *frame) +{ + LogInfo("EncodeDataToPicture.\n"); + if (!mStream) { + LogError("mStream = nullptr.\n"); + return; + } + mEncoder = std::make_shared(mEncodecId, mSrouceWidth, mSrouceHeight); + if (!mEncoder) { + LogError("mEncoder = nullptr.\n"); + } + constexpr int NO_FLAGS = 0; + mEncoder->Init(NO_FLAGS); + mStream->time_base = mEncoder->GetTimeBase(); + mEncoder->OpenEncoder(nullptr, mStream); + if (mEncoder) { + mEncoder->EncodeData(frame, mStream, mEncodeCallback); + } + FfmpegThumbnail::EncodeDataToPicture(frame); +} +void FfmpegOriginalPicture::GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName) +{ + FfmpegThumbnail::SavePicture(fileName, pkt->data, pkt->size); +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOriginalPicture.h b/utils/MediaBase/src/FfmpegOriginalPicture.h new file mode 100644 index 0000000..9936ae3 --- /dev/null +++ b/utils/MediaBase/src/FfmpegOriginalPicture.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FFMPEG_ORIGINAL_PICTURE_H +#define FFMPEG_ORIGINAL_PICTURE_H +#include "FfmpegEncoder.h" +#include "FfmpegThumbnail.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +typedef struct original_info +{ + original_info(const int &srouceWidth, const int &srouceHeight, const std::string &thumbnail); + const int mSrouceWidth; + const int mSrouceHeight; + const std::string mThumbnail; +} OriginalInfo; +class FfmpegOriginalPicture : public FfmpegThumbnail +{ +public: + FfmpegOriginalPicture(const AVCodecID &encodecId, const AVCodecID &decodecId); + virtual ~FfmpegOriginalPicture() = default; + void Init(const OriginalInfo &originalInfo); + void UnInit(void); + bool CreateOriginalPicture(const std::string &outputFile, const void *data, const size_t &size); + +private: + void EncodeDataToPicture(AVFrame *frame) override; + void GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName); + +private: + std::shared_ptr mEncoder; + std::function mEncodeCallback; + std::shared_ptr mPictureInfo; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOutputStream.cpp b/utils/MediaBase/src/FfmpegOutputStream.cpp index ca8620c..7b053c6 100644 --- a/utils/MediaBase/src/FfmpegOutputStream.cpp +++ b/utils/MediaBase/src/FfmpegOutputStream.cpp @@ -15,7 +15,9 @@ #include "FfmpegOutputStream.h" #include "FfmpegDecoder.h" #include "FfmpegEncoder.h" +#include "FfmpegThumbnail.h" #include "ILog.h" +#include #ifdef __cplusplus extern "C" { #endif @@ -32,11 +34,15 @@ extern "C" { #endif #include #include +#include #include #include #include +#include +#include FfmpegOutputStream::FfmpegOutputStream(const AVCodecID &encodecId, const AVCodecID &dncodecId) - : mEncodecId(encodecId), mDeccodecId(dncodecId), mTmpPkt(nullptr), mStream(nullptr), mStreamHeaderWritten(false) + : mEncodecId(encodecId), mDeccodecId(dncodecId), mTmpPkt(nullptr), mStream(nullptr), mStreamHeaderWritten(false), + mH264Data2Jpeg(nullptr) { } bool FfmpegOutputStream::Init(AVFormatContext *outputFormat) @@ -64,12 +70,11 @@ bool FfmpegOutputStream::Init(AVFormatContext *outputFormat) mEncoder->OpenEncoder(nullptr, mStream); } else { + /** + * @brief There is no need to set time_base here, time_base will be automatically corrected inside ffmpeg. + * + */ mStream->time_base = (AVRational){1, 15}; - // int ret = avcodec_parameters_copy(mStream->codecpar, in_codecpar); - // if (ret < 0) { - // LogError("Failed to copy codec parameters\n"); - // return false; - // } mStream->codecpar->codec_id = AV_CODEC_ID_H264; mStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; mStream->codecpar->width = 1920; @@ -79,15 +84,14 @@ bool FfmpegOutputStream::Init(AVFormatContext *outputFormat) mStream->codecpar->codec_tag = 0; mStream->codecpar->extradata = nullptr; mStream->codecpar->extradata_size = 0; - // mEncoder = std::make_shared(AV_CODEC_ID_MPEG4); - // mEncoder->Init(outputFormat->flags); - // mStream->time_base = mEncoder->GetTimeBase(); - // mEncoder->OpenEncoder(nullptr, mStream); - // mEncoder->UnInit(); - // mEncoder.reset(); } return true; } +bool FfmpegOutputStream::Init(AVFormatContext *outputFormat, const std::string &thumbnailFile) +{ + mThumbnailFileName = thumbnailFile; + return Init(outputFormat); +} void FfmpegOutputStream::UnInit(void) { if (mEncoder) { @@ -99,32 +103,43 @@ void FfmpegOutputStream::UnInit(void) mDecoder.reset(); } av_packet_free(&mTmpPkt); + if (mThumbnailThread.joinable()) { + mThumbnailThread.join(); + } + if (mH264Data2Jpeg) { + free(mH264Data2Jpeg); + mH264Data2Jpeg = nullptr; + } } -void FfmpegOutputStream::WriteSourceData(const void *data, const size_t &size) +void FfmpegOutputStream::WriteSourceData(const void *data, const size_t &size, const unsigned long long &pts) { if (mDecoder) { - mDecoder->DecodeData(data, size, mDecodeCallback); + mDecoder->DecodeData(data, size, pts, mDecodeCallback); return; } - AVPacket *mTmpPkt = av_packet_alloc(); + AVPacket *tmpPkt = av_packet_alloc(); static unsigned long long u64Interval = 0; - AVRational in_timebase = (AVRational){1, 15}; + // AVRational in_timebase = (AVRational){1, 15}; if (mEncodeCallback) { - mTmpPkt->data = (uint8_t *)data; - mTmpPkt->size = size; - mTmpPkt->stream_index = mStream->index; - mTmpPkt->duration = 0; - // mTmpPkt->pts = u64Interval * 1000; // ת���� us - mTmpPkt->pts = av_rescale_q(u64Interval, (AVRational){1, 15}, mStream->time_base); + tmpPkt->data = (uint8_t *)data; + tmpPkt->size = size; + tmpPkt->stream_index = mStream->index; + constexpr int64_t ZERO_MEANS_UNKNOWN = 0; + tmpPkt->duration = ZERO_MEANS_UNKNOWN; + // tmpPkt->pts = u64Interval * 1000; // ת���� us + tmpPkt->pts = av_rescale_q(pts, (AVRational){1, 1000000}, mStream->time_base); + // LogInfo("vvvvvvvvvvvvvvvvvvvvvvvvv num:%d, den:%d\n", mStream->time_base.num, mStream->time_base.den); + // LogInfo("vvvvvvvvvvvvvvvvvvvvvvvvv pts:%llu, duration:%d\n", tmpPkt->pts, tmpPkt->duration); + // tmpPkt->pts = pts; u64Interval++; - mTmpPkt->dts = mTmpPkt->pts; + tmpPkt->dts = tmpPkt->pts; /* copy packet */ // av_packet_rescale_ts(mTmpPkt, in_timebase, mStream->time_base); - mTmpPkt->pos = -1; - mEncodeCallback(mTmpPkt); + tmpPkt->pos = -1; + mEncodeCallback(tmpPkt); } - av_packet_unref(mTmpPkt); - av_packet_free(&mTmpPkt); + av_packet_unref(tmpPkt); + av_packet_free(&tmpPkt); } void FfmpegOutputStream::SetWriteSourceDataCallback(std::function callback) { @@ -140,11 +155,17 @@ bool FfmpegOutputStream::CheckStreamHeader(const void *data, const size_t &size) if ((0x00 == pData[i]) && (0x00 == pData[i + 1]) && (0x00 == pData[i + 2]) && (0x01 == pData[i + 3]) && (0x5 == (pData[i + 4] & 0x1F))) { uint8_t *extradata = (uint8_t *)av_mallocz(i + 1); - if (!extradata) { + mH264Data2Jpeg = (char *)malloc(size + 1); + if (!extradata && !mH264Data2Jpeg) { LogError("Could not allocate extradata\n"); return false; } LogInfo("Found extradata\n"); + memcpy(mH264Data2Jpeg, data, size); + /** + * @brief Find the first I-frame and decode it ->encode it into a JPEG image file. + */ + FfmpegOutputStream::CreateThumbnailFile(mH264Data2Jpeg, size); memcpy(extradata, pData, i); mStream->codecpar->extradata = extradata; mStream->codecpar->extradata_size = i; @@ -160,4 +181,27 @@ void FfmpegOutputStream::GetDecodeDataCallback(AVFrame *frame) mEncoder->EncodeData(frame, mStream, mEncodeCallback); return; } +} +void FfmpegOutputStream::CreateThumbnailFile(const void *frame, const size_t &size) +{ + if (mThumbnailFileName.empty()) { + LogError("mThumbnailFileName is empty.\n"); + return; + } + auto thumbnailThread = + [](std::shared_ptr output, const void *frameData, const size_t dataSize) { + LogInfo("CreateThumbnailFile start.\n"); + output->CreateThumbnailFileThread(frameData, dataSize); + }; + std::shared_ptr impl = shared_from_this(); + mThumbnailThread = std::thread(thumbnailThread, impl, frame, size); +} +void FfmpegOutputStream::CreateThumbnailFileThread(const void *frame, const size_t &size) +{ + FfmpegThumbnail thumbnail(AV_CODEC_ID_MJPEG, AV_CODEC_ID_H264); + ThumbnailInfo info(1920, 2160, 192, 216); // TODO: + thumbnail.Init(info); + thumbnail.CreateThumbnail(mThumbnailFileName, frame, size); + thumbnail.UnInit(); + LogInfo("CreateThumbnailFile end.\n"); } \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOutputStream.h b/utils/MediaBase/src/FfmpegOutputStream.h index 58fa81e..d0b7c76 100644 --- a/utils/MediaBase/src/FfmpegOutputStream.h +++ b/utils/MediaBase/src/FfmpegOutputStream.h @@ -36,19 +36,35 @@ extern "C" { #endif #include #include -class FfmpegOutputStream +#include +class FfmpegOutputStream : public std::enable_shared_from_this { public: FfmpegOutputStream(const AVCodecID &encodecId, const AVCodecID &dncodecId); virtual ~FfmpegOutputStream() = default; bool Init(AVFormatContext *outputFormat); + /** + * @brief If you need to convert stream frames into thumbnails, use this function. + * @param outputFormat + * @param thumbnailFile + * @return true + * @return false + */ + bool Init(AVFormatContext *outputFormat, const std::string &thumbnailFile); void UnInit(void); - void WriteSourceData(const void *data, const size_t &size); + void WriteSourceData(const void *data, const size_t &size, const unsigned long long &pts); void SetWriteSourceDataCallback(std::function callback); bool CheckStreamHeader(const void *data, const size_t &size); private: void GetDecodeDataCallback(AVFrame *frame); + /** + * @brief Create a thumbnail for the wrapped video. + * @param frame + * @param size + */ + void CreateThumbnailFile(const void *frame, const size_t &size); + void CreateThumbnailFileThread(const void *frame, const size_t &size); private: const AVCodecID mEncodecId; @@ -60,5 +76,8 @@ private: std::function mDecodeCallback; std::function mEncodeCallback; bool mStreamHeaderWritten; + std::thread mThumbnailThread; + char *mH264Data2Jpeg; + std::string mThumbnailFileName; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegReadFile.cpp b/utils/MediaBase/src/FfmpegReadFile.cpp index 0ced6c9..f4f0185 100644 --- a/utils/MediaBase/src/FfmpegReadFile.cpp +++ b/utils/MediaBase/src/FfmpegReadFile.cpp @@ -27,12 +27,13 @@ extern "C" { #include #include #include +#include #ifdef __cplusplus } #endif -#include #include #include +#include #include FfmpegReadFile::FfmpegReadFile() : mReadVideoCallback(nullptr), mReadVideoCallbackContext(nullptr), mReadAudioCallback(nullptr), @@ -41,6 +42,7 @@ FfmpegReadFile::FfmpegReadFile() } StatusCode FfmpegReadFile::StartReadFile(const std::string &path) { + mFilePath = path; InitFfmpeg(); int result = 0; const AVInputFormat *iformat = av_find_input_format(FfmpegBase::InputFormat(mType)); @@ -80,6 +82,7 @@ StatusCode FfmpegReadFile::StartReadFile(const std::string &path) } StatusCode FfmpegReadFile::StopReadFile(void) { + std::lock_guard locker(mMutex); mTaskRuning = false; if (mTaskTimerThread.joinable()) { mTaskTimerThread.join(); @@ -108,7 +111,7 @@ void FfmpegReadFile::ReadFileThread(AVFormatContext *pFormatCtx, int mediaStream LogWarning("ReadVideoCallback is null.\n"); } AVPacket packet; - unsigned int playTimeMs = 0; + // unsigned long long playTime = 0; // av_new_packet(&packet, AV_INPUT_BUFFER_MIN_SIZE); while (av_read_frame(pFormatCtx, &packet) >= 0) { if (nullptr == mReadVideoCallback) { @@ -121,16 +124,30 @@ void FfmpegReadFile::ReadFileThread(AVFormatContext *pFormatCtx, int mediaStream } // Checks whether the packet belongs to a video stream. if (packet.stream_index == mediaStreamIndex) { - playTimeMs = (packet.duration * pFormatCtx->streams[mediaStreamIndex]->time_base.num * 1000) / - pFormatCtx->streams[mediaStreamIndex]->time_base.den; + // playTime = (packet.duration * pFormatCtx->streams[mediaStreamIndex]->time_base.num) / + // pFormatCtx->streams[mediaStreamIndex]->time_base.den; + // AVRational time_base = pFormatCtx->streams[mediaStreamIndex]->time_base; + // int64_t duration_ms = av_rescale_q(packet.duration, time_base, {1, AV_TIME_BASE}) * 1000; // LogInfo("Frame data address: %p, length: %zu\n", packet.data, packet.size); - // LogInfo("Play time ms:%d\n", playTimeMs); - // LogInfo("time base: num = %d, den = %d\n", + // static unsigned long long timeAmout = 0; + // timeAmout += playTime; + // LogInfo("Time amout: %llu\n", timeAmout); + // LogInfo("time base: num = %d, den = %d, duration = %d\n", // pFormatCtx->streams[mediaStreamIndex]->time_base.num, - // pFormatCtx->streams[mediaStreamIndex]->time_base.den); + // pFormatCtx->streams[mediaStreamIndex]->time_base.den, + // packet.duration); // LogInfo("pFormatCtx->bit_rate = %ld\n", pFormatCtx->bit_rate); - ReadFrame(&packet); - std::this_thread::sleep_for(std::chrono::milliseconds(playTimeMs)); + // playTime = (unsigned long long)(packet.duration * + // av_q2d(pFormatCtx->streams[mediaStreamIndex]->time_base) * + // 1000000); + // LogInfo("playTime time ms:%llu\n", playTime); + int64_t duration_us = av_rescale_q( + packet.duration, pFormatCtx->streams[mediaStreamIndex]->time_base, (AVRational){1, 1000000}); + // unsigned long long playTime_us = + // av_rescale_q(playTime, pFormatCtx->streams[mediaStreamIndex]->time_base, (AVRational){1, 1000000}); + // LogInfo("playTime_us time ms:%llu\n", playTime_us); + ReadFrame(&packet, duration_us); + // std::this_thread::sleep_for(std::chrono::milliseconds(playTime)); } // Release the data packet. av_packet_unref(&packet); @@ -139,12 +156,12 @@ void FfmpegReadFile::ReadFileThread(AVFormatContext *pFormatCtx, int mediaStream avformat_close_input(&pFormatCtx); } -void inline FfmpegReadFile::ReadFrame(AVPacket *packet) +void inline FfmpegReadFile::ReadFrame(AVPacket *packet, const unsigned int duration_us) { if (AVMEDIA_TYPE_VIDEO == mFFmpegMediaType) { - mReadVideoCallback(packet->data, packet->size, mReadVideoCallbackContext); + mReadVideoCallback(packet->data, packet->size, duration_us, mReadVideoCallbackContext); } else if (AVMEDIA_TYPE_AUDIO == mFFmpegMediaType) { - mReadVideoCallback(packet->data, packet->size, mReadVideoCallbackContext); + mReadVideoCallback(packet->data, packet->size, duration_us, mReadVideoCallbackContext); } } \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegReadFile.h b/utils/MediaBase/src/FfmpegReadFile.h index b3f3b93..50d61fd 100644 --- a/utils/MediaBase/src/FfmpegReadFile.h +++ b/utils/MediaBase/src/FfmpegReadFile.h @@ -16,6 +16,7 @@ #define FFMPEG_READ_FILE_H #include "FfmpegBase.h" #include "MediaBase.h" +#include class FfmpegReadFile : virtual public FfmpegBase { public: @@ -30,12 +31,14 @@ public: // About read media file. private: void ReadFileThread(AVFormatContext *pFormatCtx, int video_stream_index); - void ReadFrame(AVPacket *packet); + void ReadFrame(AVPacket *packet, const unsigned int duration_us); private: + std::mutex mMutex; ReadVideoFileCallback mReadVideoCallback; void *mReadVideoCallbackContext; ReadVideoFileCallback mReadAudioCallback; void *mReadAudioCallbackContext; + std::string mFilePath; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegTakePicture.cpp b/utils/MediaBase/src/FfmpegTakePicture.cpp new file mode 100644 index 0000000..2d9857b --- /dev/null +++ b/utils/MediaBase/src/FfmpegTakePicture.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FfmpegTakePicture.h" +#include "FfmpegBase.h" +#include "FfmpegOriginalPicture.h" +#include "FfmpegThumbnailV2.h" +#include "ILog.h" +#include "MediaBase.h" +#include "StatusCode.h" +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +FfmpegTakePicture::FfmpegTakePicture() : mOutputFileInfo(nullptr), mTaskRuning(false), mFrameData(nullptr) +{ +} +StatusCode FfmpegTakePicture::OpenJpegFile(const OutputFileInfo &fileInfo) +{ + mOutputFileInfo = std::make_shared(fileInfo); + if (mOutputFileInfo) { + return CreateStatusCode(STATUS_CODE_OK); + } + return CreateStatusCode(STATUS_CODE_NOT_OK); +} +StatusCode FfmpegTakePicture::CloseJpegFile(void) +{ + if (mCodecThread.joinable()) { + mCodecThread.join(); + } + if (mFrameData) { + free(mFrameData); + mFrameData = nullptr; + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode FfmpegTakePicture::WriteJpegData(const void *data, const size_t &size, const StreamInfo &streamInfo) +{ + if (!mOutputFileInfo) { + LogError("mOutputFileInfoCit is null\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (STREAM_TYPE_VIDEO_H264 == streamInfo.mType) { + char *pData = (char *)data; + for (size_t i = 0; i < size; i++) { + if ((0x00 == pData[i]) && (0x00 == pData[i + 1]) && (0x00 == pData[i + 2]) && (0x01 == pData[i + 3]) && + (0x5 == (pData[i + 4] & 0x1F))) { + LogInfo("Found extradata\n"); + CreateJpegFile(data, size); + if (mOutputFileInfo->mFinished) { + *(mOutputFileInfo->mFinished) = static_cast(OUTPUT_FILE_STATUS_FINISHED); + } + return CreateStatusCode(STATUS_CODE_OK); + } + } + } + if (STREAM_TYPE_JPEG == streamInfo.mType) { + CreateJpegFile2(data, size); + if (mOutputFileInfo->mFinished) { + *(mOutputFileInfo->mFinished) = static_cast(OUTPUT_FILE_STATUS_FINISHED); + } + return CreateStatusCode(STATUS_CODE_OK); + } + return CreateStatusCode(STATUS_CODE_NOT_OK); +} +void inline FfmpegTakePicture::CreateJpegFile(const void *data, const size_t &size) +{ + mFrameData = (char *)malloc(size); + if (!mFrameData) { + LogError("malloc failed\n"); + return; + } + memcpy(mFrameData, data, size); + auto codecThread = [](std::shared_ptr output, const void *frameData, const size_t dataSize) { + LogInfo("CreateJpegFile start.\n"); + output->CreateJpegFileThread(frameData, dataSize); + }; + std::shared_ptr impl = + std::dynamic_pointer_cast(FfmpegBase::shared_from_this()); + mCodecThread = std::thread(codecThread, impl, mFrameData, size); +} +void FfmpegTakePicture::CreateJpegFileThread(const void *data, const size_t &size) +{ + FfmpegOriginalPicture picture(AV_CODEC_ID_MJPEG, AV_CODEC_ID_H264); + OriginalInfo info(1920, 2160, mOutputFileInfo->mThumbnailFileName); // TODO: + picture.Init(info); + picture.CreateOriginalPicture(mOutputFileInfo->mFileName, data, size); + picture.UnInit(); +} +void inline FfmpegTakePicture::CreateJpegFile2(const void *data, const size_t &size) +{ + mFrameData = (char *)malloc(size); + if (!mFrameData) { + LogError("malloc failed\n"); + return; + } + memcpy(mFrameData, data, size); + auto codecThread = [](std::shared_ptr output, const void *frameData, const size_t dataSize) { + LogInfo("CreateJpegFile start.\n"); + output->CreateJpegFileThread2(frameData, dataSize); + }; + std::shared_ptr impl = + std::dynamic_pointer_cast(FfmpegBase::shared_from_this()); + mCodecThread = std::thread(codecThread, impl, mFrameData, size); +} +void FfmpegTakePicture::CreateJpegFileThread2(const void *data, const size_t &size) +{ + FfmpegThumbnailV2::SavePicture(mOutputFileInfo->mFileName, data, size); +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegTakePicture.h b/utils/MediaBase/src/FfmpegTakePicture.h new file mode 100644 index 0000000..7265de7 --- /dev/null +++ b/utils/MediaBase/src/FfmpegTakePicture.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FFMPEG_TAKE_PICTURE_H +#define FFMPEG_TAKE_PICTURE_H +#include "FfmpegBase.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +class FfmpegTakePicture : virtual public FfmpegBase +{ +public: + FfmpegTakePicture(); + virtual ~FfmpegTakePicture() = default; + StatusCode OpenJpegFile(const OutputFileInfo &fileInfo) override; + StatusCode CloseJpegFile(void) override; + /** + * @brief This function needs to return the result quickly without blocking, so as to avoid the middleware thread + * being blocked when processing a single frame of data, causing a large number of useless frames to accumulate in + * the buffer. + * @param data + * @param size + * @return StatusCode + */ + StatusCode WriteJpegData(const void *data, const size_t &size, const StreamInfo &streamInfo) override; + +private: + void CreateJpegFile(const void *data, const size_t &size); + void CreateJpegFileThread(const void *data, const size_t &size); + void CreateJpegFile2(const void *data, const size_t &size); + void CreateJpegFileThread2(const void *data, const size_t &size); + +private: + std::shared_ptr mOutputFileInfo; + bool mTaskRuning; + std::thread mCodecThread; + char *mFrameData; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegThumbnail.cpp b/utils/MediaBase/src/FfmpegThumbnail.cpp new file mode 100644 index 0000000..7095e42 --- /dev/null +++ b/utils/MediaBase/src/FfmpegThumbnail.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FfmpegThumbnail.h" +#include "FfmpegDecoder.h" +#include "FfmpegEncoder.h" +#include "ILog.h" +#include "LinuxApi.h" +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +#include +#include +thumbnail_info::thumbnail_info(const int &srouceWidth, const int &srouceHeight, const int &targetWidth, + const int &targetHeight) + : mSrouceWidth(srouceWidth), mSrouceHeight(srouceHeight), mTargetWidth(targetWidth), mTargetHeight(targetHeight) +{ +} +FfmpegThumbnail::FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &decodecId) + : mOutputFormat(nullptr), mStream(nullptr), mSwsCtx(nullptr), mEncodecId(encodecId), mDecodecId(decodecId) +{ +} +void FfmpegThumbnail::Init(const ThumbnailInfo &thumbnailInfo) +{ + LogInfo("FfmpegThumbnail Init\n"); + mSrouceWidth = thumbnailInfo.mSrouceWidth; + mSrouceHeight = thumbnailInfo.mSrouceHeight; + mTargetWidth = thumbnailInfo.mTargetWidth; + mTargetHeight = thumbnailInfo.mTargetHeight; + mDecoder = std::make_shared(mDecodecId, mSrouceWidth, mSrouceHeight); + if (!mDecoder) { + LogError("mDecoder = nullptr.\n"); + } + mEncoder = std::make_shared(mEncodecId, mTargetWidth, mTargetHeight); + if (!mEncoder) { + LogError("mEncoder = nullptr.\n"); + } +} +void FfmpegThumbnail::UnInit(void) +{ + if (mOutputFormat && mOutputFormat->pb) { + av_write_trailer(mOutputFormat); + } + if (mEncoder) { + mEncoder->UnInit(); + mEncoder.reset(); + } + if (mDecoder) { + mDecoder->UnInit(); + mDecoder.reset(); + } + if (nullptr == mOutputFormat) { + return; + } + if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { + /* Close the output file. */ + avio_closep(&mOutputFormat->pb); + } + avformat_free_context(mOutputFormat); + fx_system_v2("sync"); + if (mSwsCtx) { + sws_freeContext(mSwsCtx); + mSwsCtx = nullptr; + } +} +bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size) +{ + if (!mDecoder) { + LogError("CreateThumbnail mDecoder && mDecodeCallback\n"); + return true; + } + mDecodeCallback = std::bind(&FfmpegThumbnail::GetDecodeDataCallback, this, std::placeholders::_1); + mEncodeCallback = std::bind(&FfmpegThumbnail::GetEncodeDataCallback, this, std::placeholders::_1, outputFile); + /* allocate the output media context */ + avformat_alloc_output_context2(&mOutputFormat, nullptr, "image2", outputFile.c_str()); + if (!mOutputFormat) { + LogError("Could not deduce output format from file.\n"); + return false; + } + /* Add the audio and video streams using the default format codecs + * and initialize the codecs. */ + if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { + /** + * @brief Maybe there is no need to use avformat_alloc_output_context2 function to create ffmpeg container. + * TODO: if mOutputFormat can be deleted here? + */ + mStream = avformat_new_stream(mOutputFormat, nullptr); + if (!mStream) { + LogError("Could not allocate stream\n"); + return false; + } + mStream->id = mOutputFormat->nb_streams - 1; + LogInfo("Create video stream\n"); + } + mDecoder->Init(); + constexpr int NO_FLAGS = 0; + mEncoder->Init(NO_FLAGS); + mStream->time_base = mEncoder->GetTimeBase(); + mEncoder->OpenEncoder(nullptr, mStream); + LogInfo("Start to decode data\n"); + mDecoder->DecodeData(data, size, AV_NOPTS_VALUE, mDecodeCallback); + LogInfo("Decode data end\n"); + return false; +} +void FfmpegThumbnail::GetDecodeDataCallback(AVFrame *frame) +{ + EncodeDataToPicture(frame); +} +void FfmpegThumbnail::GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName) +{ + SavePicture(fileName, pkt->data, pkt->size); +} +void FfmpegThumbnail::EncodeDataToPicture(AVFrame *frame) +{ + LogInfo("Decode frame->width = %d, frame->height=%d\n", frame->width, frame->height); + AVFrame *thumbnailFrame = av_frame_alloc(); + if (!thumbnailFrame) { + LogError("thumbnailFrame = nullptr.\n"); + return; + } + thumbnailFrame->format = AV_PIX_FMT_YUV420P; + thumbnailFrame->width = mTargetWidth; + thumbnailFrame->height = mTargetHeight; + + int jpegBufSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, mSrouceWidth, mSrouceHeight, 1); + LogInfo("jpegBufSize: %d\n", jpegBufSize); + uint8_t *jpegBuf = (uint8_t *)av_malloc(jpegBufSize); + if (!jpegBuf) { + LogError("jpegBuf = nullptr.\n"); + goto END; + } + av_image_fill_arrays( + thumbnailFrame->data, thumbnailFrame->linesize, jpegBuf, AV_PIX_FMT_YUV420P, frame->width, frame->height, 1); + + mSwsCtx = sws_getContext(mSrouceWidth, + mSrouceHeight, + static_cast(frame->format), + thumbnailFrame->width, + thumbnailFrame->height, + AV_PIX_FMT_YUV420P, + SWS_BILINEAR, + nullptr, + nullptr, + nullptr); + if (!mSwsCtx) { + LogError("mSwsCtx = nullptr.\n"); + goto END; + } + fx_system_v2("echo 1 > /proc/sys/vm/drop_caches"); + /** + * @brief Perform pixel format conversion. + * NOTE: This function will crash when the chip does not have enough memory. + */ + sws_scale(mSwsCtx, frame->data, frame->linesize, 0, frame->height, thumbnailFrame->data, thumbnailFrame->linesize); + + if (mEncoder) { + mEncoder->EncodeData(thumbnailFrame, mStream, mEncodeCallback); + } +END: + if (thumbnailFrame) { + av_frame_free(&thumbnailFrame); + } + if (jpegBuf) { + av_free(jpegBuf); + } + return; +} +bool FfmpegThumbnail::SavePicture(const std::string &fileName, const void *data, const size_t &size) +{ + FILE *file = nullptr; + if (!data) { + LogError("SavePicture:%s failed, data is nullptr.\n", fileName.c_str()); + return false; + } + LogInfo("SavePicture:%s, size = %u\n", fileName.c_str(), size); + file = fopen(fileName.c_str(), "a+"); + if (!file) { + LogError("fopen failed.\n"); + return false; + } + fwrite(data, 1, size, file); + fflush(file); + fclose(file); + // system("sync"); + return true; +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegThumbnail.h b/utils/MediaBase/src/FfmpegThumbnail.h new file mode 100644 index 0000000..e5b34e7 --- /dev/null +++ b/utils/MediaBase/src/FfmpegThumbnail.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FFMPEG_THUMBNAIL_H +#define FFMPEG_THUMBNAIL_H +#include "FfmpegDecoder.h" +#include "FfmpegEncoder.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +typedef struct thumbnail_info +{ + thumbnail_info(const int &srouceWidth, const int &srouceHeight, const int &targetWidth, const int &targetHeight); + const int mSrouceWidth; + const int mSrouceHeight; + const int mTargetWidth; + const int mTargetHeight; +} ThumbnailInfo; +class FfmpegThumbnail +{ +public: + FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &decodecId); + virtual ~FfmpegThumbnail() = default; + void Init(const ThumbnailInfo &thumbnailInfo); + void UnInit(void); + bool CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size); + +private: + void GetDecodeDataCallback(AVFrame *frame); + void GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName); + +protected: + virtual void EncodeDataToPicture(AVFrame *frame); + +protected: + static bool SavePicture(const std::string &fileName, const void *data, const size_t &size); + +private: + std::shared_ptr mEncoder; + std::shared_ptr mDecoder; + std::function mDecodeCallback; + std::function mEncodeCallback; + +protected: + AVFormatContext *mOutputFormat; + AVStream *mStream; + struct SwsContext *mSwsCtx; + int mSrouceWidth; + int mSrouceHeight; + int mTargetWidth; + int mTargetHeight; + const AVCodecID mEncodecId; + const AVCodecID mDecodecId; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegThumbnailV2.cpp b/utils/MediaBase/src/FfmpegThumbnailV2.cpp new file mode 100644 index 0000000..d930e37 --- /dev/null +++ b/utils/MediaBase/src/FfmpegThumbnailV2.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FfmpegThumbnailV2.h" +#include "FfmpegDecoderV2.h" +#include "FfmpegEncoderV2.h" +#include "ILog.h" +#include "LinuxApi.h" +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +#include +#include +thumbnail_info_v2::thumbnail_info_v2(const int &sourceWidth, const int &sourceHeight, const int &targetWidth, + const int &targetHeight) + : mSourceWidth(sourceWidth), mSourceHeight(sourceHeight), mTargetWidth(targetWidth), mTargetHeight(targetHeight) +{ +} +FfmpegThumbnailV2::FfmpegThumbnailV2(const AVCodecID &encodecId, const AVCodecID &decodecId) + : mOutputFormat(nullptr), mStream(nullptr), mSwsCtx(nullptr), mEncodecId(encodecId), mDecodecId(decodecId), + mDecodePixelFormat(AV_PIX_FMT_RGB4) +{ +} +void FfmpegThumbnailV2::Init(const ThumbnailInfoV2 &thumbnailInfo) +{ + LogInfo("FfmpegThumbnailV2 Init\n"); + mSourceWidth = thumbnailInfo.mSourceWidth; + mSourceHeight = thumbnailInfo.mSourceHeight; + mTargetWidth = thumbnailInfo.mTargetWidth; + mTargetHeight = thumbnailInfo.mTargetHeight; + mDecoder = std::make_shared(mDecodecId, mDecodePixelFormat, mSourceWidth, mSourceHeight); + if (!mDecoder) { + LogError("mDecoder = nullptr.\n"); + } + mEncoder = std::make_shared(mEncodecId, mDecodePixelFormat, mTargetWidth, mTargetHeight); + if (!mEncoder) { + LogError("mEncoder = nullptr.\n"); + } +} +void FfmpegThumbnailV2::UnInit(void) +{ + if (mOutputFormat && mOutputFormat->pb) { + av_write_trailer(mOutputFormat); + } + if (mEncoder) { + mEncoder->UnInit(); + mEncoder.reset(); + } + if (mDecoder) { + mDecoder->UnInit(); + mDecoder.reset(); + } + if (nullptr == mOutputFormat) { + return; + } + if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { + /* Close the output file. */ + avio_closep(&mOutputFormat->pb); + } + avformat_free_context(mOutputFormat); + fx_system_v2("sync"); + if (mSwsCtx) { + sws_freeContext(mSwsCtx); + mSwsCtx = nullptr; + } +} +bool FfmpegThumbnailV2::CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size) +{ + if (!mDecoder) { + LogError("CreateThumbnail mDecoder && mDecodeCallback\n"); + return true; + } + mDecodeCallback = std::bind(&FfmpegThumbnailV2::GetDecodeDataCallback, this, std::placeholders::_1); + mEncodeCallback = std::bind(&FfmpegThumbnailV2::GetEncodeDataCallback, this, std::placeholders::_1, outputFile); + /* allocate the output media context */ + avformat_alloc_output_context2(&mOutputFormat, nullptr, "image2", outputFile.c_str()); + if (!mOutputFormat) { + LogError("Could not deduce output format from file.\n"); + return false; + } + /* Add the audio and video streams using the default format codecs + * and initialize the codecs. */ + if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { + /** + * @brief Maybe there is no need to use avformat_alloc_output_context2 function to create ffmpeg container. + * TODO: if mOutputFormat can be deleted here? + */ + mStream = avformat_new_stream(mOutputFormat, nullptr); + if (!mStream) { + LogError("Could not allocate stream\n"); + return false; + } + mStream->id = mOutputFormat->nb_streams - 1; + LogInfo("Create video stream\n"); + } + mDecoder->Init(); + constexpr int NO_FLAGS = 0; + mEncoder->Init(NO_FLAGS); + mStream->time_base = mEncoder->GetTimeBase(); + mEncoder->OpenEncoder(nullptr, mStream); + LogInfo("Start to decode data\n"); + mDecoder->DecodeData(data, size, AV_NOPTS_VALUE, mDecodeCallback); + LogInfo("Decode data end\n"); + return false; +} +void FfmpegThumbnailV2::GetDecodeDataCallback(AVFrame *frame) +{ + EncodeDataToPicture(frame); +} +void FfmpegThumbnailV2::GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName) +{ + SavePicture(fileName, pkt->data, pkt->size); +} +void FfmpegThumbnailV2::EncodeDataToPicture(AVFrame *frame) +{ + LogInfo("Decode frame->width = %d, frame->height=%d\n", frame->width, frame->height); + AVFrame *thumbnailFrame = av_frame_alloc(); + if (!thumbnailFrame) { + LogError("thumbnailFrame = nullptr.\n"); + return; + } + thumbnailFrame->format = mDecodePixelFormat; + thumbnailFrame->width = mTargetWidth; + thumbnailFrame->height = mTargetHeight; + + int jpegBufSize = av_image_get_buffer_size(mDecodePixelFormat, mSourceWidth, mSourceHeight, 1); + LogInfo("jpegBufSize: %d\n", jpegBufSize); + uint8_t *jpegBuf = (uint8_t *)av_malloc(jpegBufSize); + if (!jpegBuf) { + LogError("jpegBuf = nullptr.\n"); + goto END; + } + av_image_fill_arrays( + thumbnailFrame->data, thumbnailFrame->linesize, jpegBuf, mDecodePixelFormat, frame->width, frame->height, 1); + + mSwsCtx = sws_getContext(mSourceWidth, + mSourceHeight, + static_cast(frame->format), + thumbnailFrame->width, + thumbnailFrame->height, + mDecodePixelFormat, + SWS_BILINEAR, + nullptr, + nullptr, + nullptr); + if (!mSwsCtx) { + LogError("mSwsCtx = nullptr.\n"); + goto END; + } + fx_system_v2("echo 1 > /proc/sys/vm/drop_caches"); + /** + * @brief Perform pixel format conversion. + * NOTE: This function will crash when the chip does not have enough memory. + */ + sws_scale(mSwsCtx, frame->data, frame->linesize, 0, frame->height, thumbnailFrame->data, thumbnailFrame->linesize); + + if (mEncoder) { + mEncoder->EncodeData(thumbnailFrame, mStream, mEncodeCallback); + } +END: + if (thumbnailFrame) { + av_frame_free(&thumbnailFrame); + } + if (jpegBuf) { + av_free(jpegBuf); + } + return; +} +bool FfmpegThumbnailV2::SavePicture(const std::string &fileName, const void *data, const size_t &size) +{ + FILE *file = nullptr; + if (!data) { + LogError("SavePicture:%s failed, data is nullptr.\n", fileName.c_str()); + return false; + } + LogInfo("SavePicture:%s, size = %u\n", fileName.c_str(), size); + file = fopen(fileName.c_str(), "a+"); + if (!file) { + LogError("fopen failed.\n"); + return false; + } + fwrite(data, 1, size, file); + fflush(file); + fclose(file); + // system("sync"); + return true; +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegThumbnailV2.h b/utils/MediaBase/src/FfmpegThumbnailV2.h new file mode 100644 index 0000000..e5f6dfc --- /dev/null +++ b/utils/MediaBase/src/FfmpegThumbnailV2.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FFMPEG_THUMBNAIL_V2_H +#define FFMPEG_THUMBNAIL_V2_H +#include "FfmpegDecoderV2.h" +#include "FfmpegEncoderV2.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +typedef struct thumbnail_info_v2 +{ + thumbnail_info_v2(const int &sourceWidth, const int &sourceHeight, const int &targetWidth, const int &targetHeight); + const int mSourceWidth; + const int mSourceHeight; + const int mTargetWidth; + const int mTargetHeight; +} ThumbnailInfoV2; +class FfmpegThumbnailV2 +{ +public: + FfmpegThumbnailV2(const AVCodecID &encodecId, const AVCodecID &decodecId); + virtual ~FfmpegThumbnailV2() = default; + void Init(const ThumbnailInfoV2 &thumbnailInfo); + void UnInit(void); + bool CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size); + +private: + void GetDecodeDataCallback(AVFrame *frame); + void GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName); + +protected: + virtual void EncodeDataToPicture(AVFrame *frame); + +public: + static bool SavePicture(const std::string &fileName, const void *data, const size_t &size); + +private: + std::shared_ptr mEncoder; + std::shared_ptr mDecoder; + std::function mDecodeCallback; + std::function mEncodeCallback; + +protected: + AVFormatContext *mOutputFormat; + AVStream *mStream; + struct SwsContext *mSwsCtx; + int mSourceWidth; + int mSourceHeight; + int mTargetWidth; + int mTargetHeight; + const AVCodecID mEncodecId; + const AVCodecID mDecodecId; + AVPixelFormat mDecodePixelFormat; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/IMediaBase.cpp b/utils/MediaBase/src/IMediaBase.cpp index ee6303e..50f9e42 100644 --- a/utils/MediaBase/src/IMediaBase.cpp +++ b/utils/MediaBase/src/IMediaBase.cpp @@ -41,7 +41,7 @@ StatusCode IMediaBase::SetReadAudioCallback(ReadVideoFileCallback callback, void LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } -StatusCode IMediaBase::OpenOutputFile(const std::string &fileName) +StatusCode IMediaBase::OpenOutputFile(const OutputFileInfo &fileInfo) { LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); @@ -54,6 +54,27 @@ StatusCode IMediaBase::CloseOutputFile(void) void IMediaBase::GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) { } +OutputFileInfo IMediaBase::GetOutputFileInfo(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + OutputFileInfo info = {.mFileName = {0}, .mDuration_ms = 0, .mFinished = nullptr}; + return info; +} +StatusCode IMediaBase::OpenJpegFile(const OutputFileInfo &fileInfo) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IMediaBase::CloseJpegFile(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IMediaBase::WriteJpegData(const void *data, const size_t &size, const StreamInfo &streamInfo) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} static const char *MEDIA_BASE_NAME = "media_adapter"; const char *GetMediaBaseModuleName(void) { diff --git a/utils/MediaBase/src/IMediaBase.h b/utils/MediaBase/src/IMediaBase.h index b327ec5..efe5a2c 100644 --- a/utils/MediaBase/src/IMediaBase.h +++ b/utils/MediaBase/src/IMediaBase.h @@ -29,9 +29,15 @@ public: // About read media file. virtual StatusCode SetReadAudioCallback(ReadVideoFileCallback callback, void *context); public: // About combine file. - virtual StatusCode OpenOutputFile(const std::string &fileName); + virtual StatusCode OpenOutputFile(const OutputFileInfo &fileInfo); virtual StatusCode CloseOutputFile(void); virtual void GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo); + virtual OutputFileInfo GetOutputFileInfo(void); + +public: // About take picture. + virtual StatusCode OpenJpegFile(const OutputFileInfo &fileInfo); + virtual StatusCode CloseJpegFile(void); + virtual StatusCode WriteJpegData(const void *data, const size_t &size, const StreamInfo &streamInfo); }; typedef struct media_base_header { diff --git a/utils/MediaBase/src/MediaBase.cpp b/utils/MediaBase/src/MediaBase.cpp index 3083fda..5a7ef77 100644 --- a/utils/MediaBase/src/MediaBase.cpp +++ b/utils/MediaBase/src/MediaBase.cpp @@ -41,42 +41,42 @@ StatusCode ISetReadVideoCallback(void *object, ReadVideoFileCallback callback, v if (ObjectCheck(object) == true) { return (*(std::shared_ptr *)object)->SetReadVideoCallback(callback, context); } - return CreateStatusCode(STATUS_CODE_OK); + return CreateStatusCode(STATUS_CODE_NOT_OK); } StatusCode ISetReadAudioCallback(void *object, ReadVideoFileCallback callback, void *context) { if (ObjectCheck(object) == true) { return (*(std::shared_ptr *)object)->SetReadAudioCallback(callback, context); } - return CreateStatusCode(STATUS_CODE_OK); + return CreateStatusCode(STATUS_CODE_NOT_OK); } StatusCode IStartReadFile(void *object, const char *path) { if (ObjectCheck(object) == true) { return (*(std::shared_ptr *)object)->StartReadFile(path); } - return CreateStatusCode(STATUS_CODE_OK); + return CreateStatusCode(STATUS_CODE_NOT_OK); } StatusCode IStopReadFile(void *object) { if (ObjectCheck(object) == true) { return (*(std::shared_ptr *)object)->StopReadFile(); } - return CreateStatusCode(STATUS_CODE_OK); + return CreateStatusCode(STATUS_CODE_NOT_OK); } -StatusCode IOpenOutputFile(void *object, const char *fileName) +StatusCode IOpenOutputFile(void *object, const OutputFileInfo *info) { if (ObjectCheck(object) == true) { - return (*(std::shared_ptr *)object)->OpenOutputFile(fileName); + return (*(std::shared_ptr *)object)->OpenOutputFile(*info); } - return CreateStatusCode(STATUS_CODE_OK); + return CreateStatusCode(STATUS_CODE_NOT_OK); } StatusCode ICloseOutputFile(void *object) { if (ObjectCheck(object) == true) { return (*(std::shared_ptr *)object)->CloseOutputFile(); } - return CreateStatusCode(STATUS_CODE_OK); + return CreateStatusCode(STATUS_CODE_NOT_OK); } void IGetStreamData(void *object, const void *data, const size_t size, const StreamInfo streamInfo) { @@ -84,6 +84,35 @@ void IGetStreamData(void *object, const void *data, const size_t size, const Str (*(std::shared_ptr *)object)->GetStreamData(data, size, streamInfo); } } +OutputFileInfo IGetOutputFileInfo(void *object) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->GetOutputFileInfo(); + } + OutputFileInfo info = {.mFileName = {0}, .mDuration_ms = 0, .mFinished = nullptr}; + return info; +} +StatusCode IOpenJpegFile(void *object, const OutputFileInfo *info) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->OpenJpegFile(*info); + } + return CreateStatusCode(STATUS_CODE_NOT_OK); +} +StatusCode ICloseJpegFile(void *object) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->CloseJpegFile(); + } + return CreateStatusCode(STATUS_CODE_NOT_OK); +} +StatusCode IWriteJpegData(void *object, const void *data, const size_t size, const StreamInfo streamInfo) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->WriteJpegData(data, size, streamInfo); + } + return CreateStatusCode(STATUS_CODE_NOT_OK); +} void IMediaBaseFree(void *object) { if (ObjectCheck(object) == true) { diff --git a/utils/MediaBase/src/MediaBaseImpl.h b/utils/MediaBase/src/MediaBaseImpl.h index c9b68d0..46ab694 100644 --- a/utils/MediaBase/src/MediaBaseImpl.h +++ b/utils/MediaBase/src/MediaBaseImpl.h @@ -17,9 +17,10 @@ #include "FfmpegBase.h" #include "FfmpegMuxStreamV2.h" #include "FfmpegReadFile.h" +#include "FfmpegTakePicture.h" #include "IMediaBase.h" #include -class MediaBaseImpl : public FfmpegReadFile, public FfmpegMuxStreamV2 +class MediaBaseImpl : public FfmpegReadFile, public FfmpegMuxStreamV2, public FfmpegTakePicture { public: MediaBaseImpl(const MediaHandleType &type); diff --git a/utils/SharedData/src/SharedMemory.cpp b/utils/SharedData/src/SharedMemory.cpp index d2295f5..3a0386a 100644 --- a/utils/SharedData/src/SharedMemory.cpp +++ b/utils/SharedData/src/SharedMemory.cpp @@ -36,7 +36,7 @@ StatusCode SharedMemory::MakeSharedMemory(const int &size) char touchPath[128] = {0}; if (access(mPath, F_OK) != 0) { sprintf(touchPath, "%s %s", "touch", mPath); - fx_system(touchPath); + fx_system_v2(touchPath); } key_t key = ftok(mPath, mProjectId); if (key < 0) { diff --git a/utils/UpgradeTool/src/UpgradeTool.cpp b/utils/UpgradeTool/src/UpgradeTool.cpp index fb5bcbe..fab4a44 100644 --- a/utils/UpgradeTool/src/UpgradeTool.cpp +++ b/utils/UpgradeTool/src/UpgradeTool.cpp @@ -124,12 +124,12 @@ void UpgradeTool::PackFile(const std::string &fileName, const std::string &outpu LogError("write file failed, remove output file.\n"); char cmd[1024]; snprintf(cmd, sizeof(cmd), "rm -f %s", outputFile.c_str()); - fx_system(cmd); + fx_system_v2(cmd); } else { LogInfo("write file success.\n"); } fclose(input_file); fclose(output_file); - fx_system("sync"); + fx_system_v2("sync"); } \ No newline at end of file diff --git a/utils/WebServer/include/WebServer.h b/utils/WebServer/include/WebServer.h index 8e779a5..b36cf2a 100644 --- a/utils/WebServer/include/WebServer.h +++ b/utils/WebServer/include/WebServer.h @@ -29,6 +29,12 @@ typedef struct web_server_param StatusCode WebServerInit(const WebServerParam webParam); StatusCode WebServerExit(void); StatusCode WebServerUnInit(void); +/** + * @brief The document root directory configured by the web server. Files under the directory can be accessed over the + * network. + * @return const char* + */ +const char *GetWebServerDocumentRoot(void); #ifdef __cplusplus } #endif diff --git a/utils/WebServer/src/WebServer.cpp b/utils/WebServer/src/WebServer.cpp index 1342332..64dce94 100644 --- a/utils/WebServer/src/WebServer.cpp +++ b/utils/WebServer/src/WebServer.cpp @@ -21,6 +21,7 @@ #include #include #include +// #include #include #include @@ -103,9 +104,14 @@ static void get_thumbnail_handle(const char *thumbnailUrl, void *context) unsigned char *buffer = nullptr; size_t bytesRead = 0; long int fsize = 0; - file = fopen(thumbnailUrl, "rb"); + constexpr int BUFFER_SIZE = 256; + char filePath[BUFFER_SIZE] = {0}; + // const std::string url = thumbnailUrl; + // const std::string thumbnailFile = GOAHEAD_DOCUMENTS_PATH + url; + snprintf(filePath, BUFFER_SIZE - 1, GOAHEAD_DOCUMENTS_PATH "%s", thumbnailUrl); + file = fopen(filePath, "rb"); if (file == nullptr) { - LogError("Open picture failed[%s].\n", thumbnailUrl); + LogError("Open picture failed[%s].\n", filePath); goto END; } fseek(file, 0, SEEK_END); @@ -237,4 +243,8 @@ StatusCode WebServerUnInit(void) { LogInfo("WebServerUnInit.\n"); return CreateStatusCode(STATUS_CODE_OK); +} +const char *GetWebServerDocumentRoot(void) +{ + return GOAHEAD_DOCUMENTS_PATH; } \ No newline at end of file