diff --git a/application/MissionManager/src/DataProcessing.h b/application/MissionManager/src/DataProcessing.h index 29c0804..1b9ecb5 100644 --- a/application/MissionManager/src/DataProcessing.h +++ b/application/MissionManager/src/DataProcessing.h @@ -31,6 +31,7 @@ enum class InternalStateEvent KEY_EVENT_HANDLE, RESET_KEY_MEDIA_TASK, MEDIA_HANDLE_STATE_TASK_TIME_OUT, + MEDIA_HANDLE_STATE_TASK_FINISHED, END }; typedef struct key_event_data diff --git a/application/MissionManager/src/IdleState.cpp b/application/MissionManager/src/IdleState.cpp index e4269bb..74bd9ef 100644 --- a/application/MissionManager/src/IdleState.cpp +++ b/application/MissionManager/src/IdleState.cpp @@ -16,11 +16,14 @@ #include "DataProcessing.h" #include "ILog.h" #include "IStateMachine.h" +#include "MissionStateMachine.h" +#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); } void IdleState::GoInState() { @@ -33,4 +36,10 @@ void IdleState::GoOutState() 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; } \ No newline at end of file diff --git a/application/MissionManager/src/IdleState.h b/application/MissionManager/src/IdleState.h index f680ba0..0f35b69 100644 --- a/application/MissionManager/src/IdleState.h +++ b/application/MissionManager/src/IdleState.h @@ -25,6 +25,9 @@ public: void GoOutState() override; bool ExecuteStateMsg(VStateMachineData *msg) override; +private: + bool ResetKeyMediaTaskHandle(VStateMachineData *msg); + private: }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/MediaHandleState.cpp b/application/MissionManager/src/MediaHandleState.cpp index 307bb34..df03864 100644 --- a/application/MissionManager/src/MediaHandleState.cpp +++ b/application/MissionManager/src/MediaHandleState.cpp @@ -14,9 +14,12 @@ */ #include "MediaHandleState.h" #include "DataProcessing.h" +#include "IFilesManager.h" #include "ILog.h" #include "IMediaManager.h" +#include "IMissionManager.h" #include "IStateMachine.h" +#include "MediaTask.h" #include "MediaTaskHandle.h" #include "MissionStateMachine.h" #include @@ -26,6 +29,8 @@ MediaHandleState::MediaHandleState() : State("MediaHandleState") { mEventHandle[InternalStateEvent::RESET_KEY_MEDIA_TASK] = std::bind(&MediaHandleState::ResetKeyMediaTaskHandle, this, _1); + mEventHandle[InternalStateEvent::MEDIA_HANDLE_STATE_TASK_FINISHED] = + std::bind(&MediaHandleState::MediaTaskFinishedHandle, this, _1); } void MediaHandleState::GoInState() { @@ -42,11 +47,28 @@ bool MediaHandleState::ExecuteStateMsg(VStateMachineData *msg) { return DataProcessing::EventHandle(msg); } -void MediaHandleState::TaskResponse(const std::vector &response) +void MediaHandleState::TaskResponse(const MediaTaskInfo &taskinfo) { + std::shared_ptr message = std::make_shared>( + static_cast(InternalStateEvent::MEDIA_HANDLE_STATE_TASK_FINISHED), taskinfo); + MissionStateMachine::GetInstance()->SendStateMessage(message); } bool MediaHandleState::ResetKeyMediaTaskHandle(VStateMachineData *msg) { - MakeSingleTask(InternalStateEvent::RESET_KEY_MEDIA_TASK, shared_from_this()); + MediaTaskHandle::MakeSingleTask(InternalStateEvent::RESET_KEY_MEDIA_TASK, shared_from_this()); + return EXECUTED; +} +bool MediaHandleState::MediaTaskFinishedHandle(VStateMachineData *msg) +{ + std::shared_ptr message = std::dynamic_pointer_cast(msg->GetMessageObj()); + std::shared_ptr> data = + std::dynamic_pointer_cast>(message->mMissionData); + 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); + files.push_back(file); + } + IFilesManager::GetInstance()->SaveFiles(files); return EXECUTED; } \ No newline at end of file diff --git a/application/MissionManager/src/MediaHandleState.h b/application/MissionManager/src/MediaHandleState.h index 4a1e594..e68349a 100644 --- a/application/MissionManager/src/MediaHandleState.h +++ b/application/MissionManager/src/MediaHandleState.h @@ -31,7 +31,8 @@ public: bool ExecuteStateMsg(VStateMachineData *msg) override; private: - void TaskResponse(const std::vector &response) override; + void TaskResponse(const MediaTaskInfo &taskinfo) override; bool ResetKeyMediaTaskHandle(VStateMachineData *msg); + bool MediaTaskFinishedHandle(VStateMachineData *msg); }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/MediaTask.cpp b/application/MissionManager/src/MediaTask.cpp index 24071fa..76cf51f 100644 --- a/application/MissionManager/src/MediaTask.cpp +++ b/application/MissionManager/src/MediaTask.cpp @@ -14,15 +14,55 @@ */ #include "MediaTask.h" #include "DataProcessing.h" +#include "ILog.h" #include "IMediaManager.h" +#include +#include +#include +#include +#include +#include #include +#include +#include +#include MediaTask::MediaTask(const MediaTaskType &type, const InternalStateEvent &bindEvent, - const std::weak_ptr &iniator) - : mType(type), mBindEvent(bindEvent), mIniator(iniator) + const std::weak_ptr &iniator, const unsigned long &serialNumber, + const std::string &savePath) + : mType(type), mBindEvent(bindEvent), mIniator(iniator), mSerialNumber(serialNumber), mSavePath(savePath) { mResponseData.reset(); } unsigned int MediaTask::GetTaskTimeOutMs(void) { return MEDIA_TASK_TIMEOUT_MS; +} +std::string MediaTask::GetTargetNameForSaving(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); + + 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(); +} +void MediaTask::Response(const std::vector &response) +{ + LogInfo("Response handle.\n"); + auto iniator = mIniator.lock(); + if (mIniator.expired()) { + LogWarning("mIniator is expired.\n"); + return; + } + MediaTaskInfo info = { + .mResponse = response, + .mSerialNumber = mSerialNumber, + }; + iniator->TaskResponse(info); } \ No newline at end of file diff --git a/application/MissionManager/src/MediaTask.h b/application/MissionManager/src/MediaTask.h index 5eb77ae..ca6381a 100644 --- a/application/MissionManager/src/MediaTask.h +++ b/application/MissionManager/src/MediaTask.h @@ -16,26 +16,37 @@ #define MEDIA_TASK_H #include "DataProcessing.h" #include "IMediaManager.h" +#include constexpr unsigned int MEDIA_TASK_TIMEOUT_MS = 1000 * 60; +typedef struct media_task_info +{ + const std::vector mResponse; + const unsigned long mSerialNumber; +} MediaTaskInfo; class VMediaTaskIniator { public: VMediaTaskIniator() = default; virtual ~VMediaTaskIniator() = default; - virtual void TaskResponse(const std::vector &response) = 0; + virtual void TaskResponse(const MediaTaskInfo &taskinfo) = 0; }; class MediaTask : public VMediaTask { public: MediaTask(const MediaTaskType &type, const InternalStateEvent &bindEvent, - const std::weak_ptr &iniator); + const std::weak_ptr &iniator, const unsigned long &serialNumber, + const std::string &savePath); virtual ~MediaTask() = default; virtual unsigned int GetTaskTimeOutMs(void); + std::string GetTargetNameForSaving(void) override; + void Response(const std::vector &response) override; private: const MediaTaskType mType; const InternalStateEvent mBindEvent; const std::weak_ptr mIniator; + const unsigned long mSerialNumber; + const std::string mSavePath; bool mFinished = false; std::shared_ptr mResponseData; }; diff --git a/application/MissionManager/src/MediaTaskHandle.cpp b/application/MissionManager/src/MediaTaskHandle.cpp index 1658490..dd6da5f 100644 --- a/application/MissionManager/src/MediaTaskHandle.cpp +++ b/application/MissionManager/src/MediaTaskHandle.cpp @@ -14,6 +14,7 @@ */ #include "MediaTaskHandle.h" #include "DataProcessing.h" +#include "IFilesManager.h" #include "ILog.h" #include "IMediaManager.h" #include "IMissionManager.h" @@ -38,14 +39,16 @@ void MediaTaskHandle::UnInit(void) void MediaTaskHandle::MakeSingleTask(const InternalStateEvent &bindEvent, const std::shared_ptr &iniator) { - std::shared_ptr task = std::make_shared(MediaTaskType::END, bindEvent, iniator); + InfoToBeSaved info = IFilesManager::GetInstance()->GetInfoForSavingFiles(1); + std::shared_ptr task = + std::make_shared(MediaTaskType::END, bindEvent, iniator, info.mSerialNumber, info.mSavingPath); if (!mMediaHandle) { LogError("MediaHandle is null"); return; } auto code = mMediaHandle->ExecuteTask(task); if (IsCodeOK(code)) { - mRuningTask = task; + mRuningTask = task; // task should be saved here. long long timeOut = std::dynamic_pointer_cast(task)->GetTaskTimeOutMs(); std::shared_ptr message = std::make_shared( static_cast(InternalStateEvent::MEDIA_HANDLE_STATE_TASK_TIME_OUT)); diff --git a/application/MissionManager/src/MissionStateMachine.cpp b/application/MissionManager/src/MissionStateMachine.cpp index cc3aa68..e79ad38 100644 --- a/application/MissionManager/src/MissionStateMachine.cpp +++ b/application/MissionManager/src/MissionStateMachine.cpp @@ -21,6 +21,7 @@ #include "McuAskBase.h" #include "MissionManagerMakePtr.h" #include "StatusCode.h" +#include "VStateBase.h" #include // #include "TopState.h" std::shared_ptr &MissionStateMachine::GetInstance(std::shared_ptr *impl) @@ -57,6 +58,7 @@ void MissionStateMachine::Init(void) void MissionStateMachine::UnInit(void) { mStateMachine->StopHandlerThread(); + UnInitAllState(); mStateTree.clear(); } StatusCode MissionStateMachine::SendStateMessage(const std::shared_ptr &message) @@ -119,11 +121,30 @@ 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); + // /** + // * @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) +{ + for (auto &state : mStateTree) { + std::shared_ptr stateBase = std::dynamic_pointer_cast(state.second); + if (stateBase) { + stateBase->StateInit(); + } + } +} +void MissionStateMachine::UnInitAllState(void) +{ + for (auto &state : mStateTree) { + std::shared_ptr stateBase = std::dynamic_pointer_cast(state.second); + if (stateBase) { + stateBase->StateUnInit(); + } + } } diff --git a/application/MissionManager/src/MissionStateMachine.h b/application/MissionManager/src/MissionStateMachine.h index dc039bd..12fd0b2 100644 --- a/application/MissionManager/src/MissionStateMachine.h +++ b/application/MissionManager/src/MissionStateMachine.h @@ -22,6 +22,8 @@ #include const bool NOT_EXECUTED = false; const bool EXECUTED = true; +constexpr bool FATHER_HANDLE_MESSAGE = NOT_EXECUTED; +constexpr bool HANDLE_HELPESS = EXECUTED; enum class SystemState { TOP_STATE = 0, @@ -49,6 +51,8 @@ public: private: IpcMission GetStartMission(void); void RunStateMachine(const IpcMission &mission); + void InitAllState(void); + void UnInitAllState(void); private: std::shared_ptr mStateMachine; diff --git a/application/MissionManager/src/SdCardHandleState.cpp b/application/MissionManager/src/SdCardHandleState.cpp index a86a24e..2ef6b08 100644 --- a/application/MissionManager/src/SdCardHandleState.cpp +++ b/application/MissionManager/src/SdCardHandleState.cpp @@ -30,6 +30,8 @@ SdCardHandleState::SdCardHandleState() : State("SdCardHandleState"), mSdCardStat mEventHandle[InternalStateEvent::MEDIA_REPORT_EVENT] = std::bind(&SdCardHandleState::MediaReportHandle, this, _1); mEventHandle[InternalStateEvent::SD_CARD_HANDLE_STATE_SD_STATUS_REPORTED] = std::bind(&SdCardHandleState::SdCardEventHandle, this, _1); + mEventHandle[InternalStateEvent::RESET_KEY_MEDIA_TASK] = + std::bind(&SdCardHandleState::ResetKeyMediaTaskHandle, this, _1); } void SdCardHandleState::GoInState() { @@ -43,6 +45,13 @@ bool SdCardHandleState::ExecuteStateMsg(VStateMachineData *msg) { return DataProcessing::EventHandle(msg); } +void SdCardHandleState::StateInit(void) +{ +} +void SdCardHandleState::StateUnInit(void) +{ + IFilesManager::GetInstance()->UnInit(); +} bool SdCardHandleState::MediaReportHandle(VStateMachineData *msg) { LogInfo(" MediaReportHandle.\n"); @@ -86,9 +95,18 @@ bool SdCardHandleState::SdCardEventHandle(VStateMachineData *msg) } mSdCardStatus = data->mData; 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); } return EXECUTED; +} +bool SdCardHandleState::ResetKeyMediaTaskHandle(VStateMachineData *msg) +{ + if (StorageEvent::SD_CARD_INSERT == mSdCardStatus) { + return FATHER_HANDLE_MESSAGE; + } + LogWarning("Sd card is not inserted, ignore reset key media task.\n"); + return HANDLE_HELPESS; } \ No newline at end of file diff --git a/application/MissionManager/src/SdCardHandleState.h b/application/MissionManager/src/SdCardHandleState.h index c8eb7ef..9b350b9 100644 --- a/application/MissionManager/src/SdCardHandleState.h +++ b/application/MissionManager/src/SdCardHandleState.h @@ -19,7 +19,11 @@ #include "IMediaManager.h" #include "IStateMachine.h" #include "IStorageManager.h" -class SdCardHandleState : public State, public DataProcessing, public std::enable_shared_from_this +#include "VStateBase.h" +class SdCardHandleState : public State, + public DataProcessing, + public VStateBase, + public std::enable_shared_from_this { public: SdCardHandleState(); @@ -29,8 +33,11 @@ public: bool ExecuteStateMsg(VStateMachineData *msg) override; protected: + void StateInit(void) override; + void StateUnInit(void) override; bool MediaReportHandle(VStateMachineData *msg); bool SdCardEventHandle(VStateMachineData *msg); + bool ResetKeyMediaTaskHandle(VStateMachineData *msg); private: StorageEvent mSdCardStatus; diff --git a/application/MissionManager/src/StorageHandleState.cpp b/application/MissionManager/src/StorageHandleState.cpp index ba67bd0..2169907 100644 --- a/application/MissionManager/src/StorageHandleState.cpp +++ b/application/MissionManager/src/StorageHandleState.cpp @@ -19,12 +19,11 @@ #include "IStateMachine.h" #include "IStorageManager.h" #include "MissionStateMachine.h" -#include #include StorageHandleState::StorageHandleState() : State("StorageHandleState") { - mEventHandle[InternalStateEvent::STORAGE_HANDLE_STATE_INIT] = - std::bind(&StorageHandleState::StorageStartInitHandle, this, _1); + // mEventHandle[InternalStateEvent::STORAGE_HANDLE_STATE_INIT] = + // std::bind(&StorageHandleState::StorageStartInitHandle, this, _1); } void StorageHandleState::GoInState() { @@ -57,8 +56,10 @@ void StorageHandleState::ReportEvent(const StorageEvent &event) static_cast(InternalStateEvent::ANY_STATE_SD_STATUS_PERORIED), event); MissionStateMachine::GetInstance()->SendStateMessage(message2); } -bool StorageHandleState::StorageStartInitHandle(VStateMachineData *msg) +void StorageHandleState::StateInit(void) { Init(); - return EXECUTED; +} +void StorageHandleState::StateUnInit(void) +{ } \ No newline at end of file diff --git a/application/MissionManager/src/StorageHandleState.h b/application/MissionManager/src/StorageHandleState.h index 0c98175..508c74f 100644 --- a/application/MissionManager/src/StorageHandleState.h +++ b/application/MissionManager/src/StorageHandleState.h @@ -18,9 +18,11 @@ #include "IMediaManager.h" #include "IStateMachine.h" #include "IStorageManager.h" +#include "VStateBase.h" class StorageHandleState : public State, public DataProcessing, public VStorageMoniter, + public VStateBase, public std::enable_shared_from_this { public: @@ -34,6 +36,7 @@ public: private: // About VStorageMoniter void ReportEvent(const StorageEvent &event) override; - bool StorageStartInitHandle(VStateMachineData *msg); + void StateInit(void) override; + void StateUnInit(void) override; }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/TopState.cpp b/application/MissionManager/src/TopState.cpp index f88dbec..df07de1 100644 --- a/application/MissionManager/src/TopState.cpp +++ b/application/MissionManager/src/TopState.cpp @@ -24,12 +24,11 @@ #include "McuMonitor.h" #include "MissionStateMachine.h" #include "StatusCode.h" -#include #include TopState::TopState() : State("TopState") { - mEventHandle[InternalStateEvent::STORAGE_HANDLE_STATE_INIT] = - std::bind(&TopState::StorageStartInitHandle, this, _1); + // mEventHandle[InternalStateEvent::STORAGE_HANDLE_STATE_INIT] = + // std::bind(&TopState::StorageStartInitHandle, this, _1); } void TopState::GoInState() { @@ -68,10 +67,4 @@ void TopState::KeyEventReport(const std::string &keyName, const VirtualKeyEvent std::shared_ptr message = std::make_shared>( static_cast(InternalStateEvent::KEY_EVENT_HANDLE), data); MissionStateMachine::GetInstance()->SendStateMessage(message); -} -bool TopState::StorageStartInitHandle(VStateMachineData *msg) -{ - MissionStateMachine::GetInstance()->DelayMessage(msg); - MissionStateMachine::GetInstance()->SwitchState(SystemState::STORAGE_HANDLE_STATE); - return EXECUTED; } \ No newline at end of file diff --git a/application/MissionManager/src/TopState.h b/application/MissionManager/src/TopState.h index e55e6e7..0fd6f89 100644 --- a/application/MissionManager/src/TopState.h +++ b/application/MissionManager/src/TopState.h @@ -38,8 +38,5 @@ private: // About VMediaMonitor private: // About KeyMonitor void KeyEventReport(const std::string &keyName, const VirtualKeyEvent &event, const unsigned int &timeMs) override; - -private: - bool StorageStartInitHandle(VStateMachineData *msg); }; #endif \ No newline at end of file diff --git a/application/MissionManager/src/VStateBase.cpp b/application/MissionManager/src/VStateBase.cpp new file mode 100644 index 0000000..f9a8a82 --- /dev/null +++ b/application/MissionManager/src/VStateBase.cpp @@ -0,0 +1,21 @@ +/* + * 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 "VStateBase.h" +void VStateBase::StateInit(void) +{ +} +void VStateBase::StateUnInit(void) +{ +} \ No newline at end of file diff --git a/application/MissionManager/src/VStateBase.h b/application/MissionManager/src/VStateBase.h new file mode 100644 index 0000000..a5e489b --- /dev/null +++ b/application/MissionManager/src/VStateBase.h @@ -0,0 +1,25 @@ +/* + * 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 V_STATE_BASE_H +#define V_STATE_BASE_H +class VStateBase +{ +public: + VStateBase() = default; + virtual ~VStateBase() = default; + virtual void StateInit(void); + virtual void StateUnInit(void); +}; +#endif \ No newline at end of file diff --git a/middleware/FilesManager/README.md b/middleware/FilesManager/README.md index eda0373..801d5a2 100644 --- a/middleware/FilesManager/README.md +++ b/middleware/FilesManager/README.md @@ -10,8 +10,27 @@ ### 1.2.1. 数据库表 +**文件保存表** + +| key | 路径 | 时间 | 类型 | 大小 | 状态 | +| ---- | ---- | ---- | ---- | ---- | ---- | +| 数据库键值 | 文件保存的相对路径 | 录制的时间
可能不准确 | 录制的类型:
PIR
手动
手机app
定时 | 文件大小 | 枚举:
正在拍摄
已完成拍摄
待上传
正在上传
上传完成 | + +**如何应对未知的数据表临加** + +解决基于一个已存在的数据库文件,临加数据元素。 + +拍摄流程: + +1. 拍摄前先创建数据,获取唯一自增键值; +2. 拍摄结束后,更新数据状态为已完成; + +**状态使用TEXT数据类型,如果使用枚举,可能会存在枚举值变化导致的bug。** + ## 1.3. 文件夹管理 +**目录树** + ``` DCIM/ // 根目录 ├── picture // 图片目录 @@ -25,10 +44,18 @@ DCIM/ // 根目录 └── xxx.MP4 ``` +**野数据** + +  插入一些非软件保存文件的sd卡时,忽略这些数据(不做任何处理)。 + ## 1.4. 文件命名规则 **文件类型** 1. PIR抓拍; 2. 定时抓拍; -3. 手动抓拍; \ No newline at end of file +3. 手动抓拍; + +**文件名命名规则** + +基于文件保存表的数据元素:类型+时间+key diff --git a/middleware/FilesManager/include/IFilesManager.h b/middleware/FilesManager/include/IFilesManager.h index 1e086bd..8bca437 100644 --- a/middleware/FilesManager/include/IFilesManager.h +++ b/middleware/FilesManager/include/IFilesManager.h @@ -16,11 +16,51 @@ #define I_FILES_MANAGER_H #include "StatusCode.h" #include +#include +#include +#include +enum class FileCreateType +{ + PIR = 0, + MANUAL_TEST, + MANUAL_PHONE, + TIMED, + END +}; +enum class FileStatus +{ + RECORDING = 0, + FINISHED_RECORD, + SHOULD_BE_UPLOAD, + UPLOADING, + UPLOADED, + END +}; +constexpr unsigned long UNDEFINE_SERIAL_NUMBER = -1; +constexpr unsigned long UNDEFINE_FILE_SIZE = 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); + const unsigned long mSerialNumber; + const std::string mFileName; + const unsigned long mFileSize; + const time_t mCreateTime_s; + const FileCreateType mType; + const FileStatus mStatus; +} SyncFileInfo; typedef struct save_file_info { save_file_info(const std::string &fileName); const std::string mFileName; } SaveFileInfo; +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; +} InfoToBeSaved; bool CreateFilesManagerModule(void); bool DestroyFilesManagerModule(void); class IFilesManager @@ -32,5 +72,7 @@ public: virtual StatusCode Init(void); virtual StatusCode UnInit(void); virtual StatusCode SaveFile(const SaveFileInfo &fileInfo); + virtual InfoToBeSaved GetInfoForSavingFiles(const unsigned int &count); + virtual StatusCode SaveFiles(const 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 55b91c7..c70a03f 100644 --- a/middleware/FilesManager/src/FilesDatabase.h +++ b/middleware/FilesManager/src/FilesDatabase.h @@ -24,6 +24,8 @@ public: virtual ~FilesDatabase() = default; void Init(void); void UnInit(void); - StatusCode DatabaseSaveFile(const SaveFileInfo &fileInfo); + StatusCode DbSaveFile(const SaveFileInfo &fileInfo); + InfoToBeSaved CreateInfoForSavingFiles(const unsigned int &count); + StatusCode DbSaveFiles(const 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 d459663..e3b10f2 100644 --- a/middleware/FilesManager/src/FilesManagerImpl.cpp +++ b/middleware/FilesManager/src/FilesManagerImpl.cpp @@ -17,6 +17,7 @@ // #include "IStorageManager.h" #include "FilesDatabase.h" #include "StatusCode.h" +#include StatusCode FilesManagerImpl::Init(void) { FilesDatabase::Init(); @@ -24,9 +25,18 @@ StatusCode FilesManagerImpl::Init(void) } StatusCode FilesManagerImpl::UnInit(void) { + FilesDatabase::UnInit(); return CreateStatusCode(STATUS_CODE_OK); } StatusCode FilesManagerImpl::SaveFile(const SaveFileInfo &fileInfo) { - return FilesDatabase::DatabaseSaveFile(fileInfo); + return FilesDatabase::DbSaveFile(fileInfo); +} +InfoToBeSaved FilesManagerImpl::GetInfoForSavingFiles(const unsigned int &count) +{ + return FilesDatabase::CreateInfoForSavingFiles(count); +} +StatusCode FilesManagerImpl::SaveFiles(const std::vector &info) +{ + return FilesDatabase::DbSaveFiles(info); } \ No newline at end of file diff --git a/middleware/FilesManager/src/FilesManagerImpl.h b/middleware/FilesManager/src/FilesManagerImpl.h index 114bee7..d73989f 100644 --- a/middleware/FilesManager/src/FilesManagerImpl.h +++ b/middleware/FilesManager/src/FilesManagerImpl.h @@ -27,5 +27,7 @@ protected: StatusCode Init(void) override; StatusCode UnInit(void) override; StatusCode SaveFile(const SaveFileInfo &fileInfo) override; + InfoToBeSaved GetInfoForSavingFiles(const unsigned int &count) override; + StatusCode SaveFiles(const 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 1fa17a4..1381134 100644 --- a/middleware/FilesManager/src/IFilesManager.cpp +++ b/middleware/FilesManager/src/IFilesManager.cpp @@ -17,9 +17,22 @@ #include "StatusCode.h" #include #include +#include +#include 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) +{ +} +info_to_be_saved::info_to_be_saved(const unsigned long &serialNumber, const std::string mSavingPath) + : mSerialNumber(serialNumber), mSavingPath(mSavingPath) +{ +} std::shared_ptr &IFilesManager::GetInstance(std::shared_ptr *impl) { static auto instance = std::make_shared(); @@ -36,13 +49,27 @@ std::shared_ptr &IFilesManager::GetInstance(std::shared_ptr &info) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } \ No newline at end of file diff --git a/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp b/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp index 17c7c74..a6bf21b 100644 --- a/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp +++ b/middleware/FilesManager/src/sqlite3/FilesDatabase.cpp @@ -20,15 +20,19 @@ #include "SqliteHandle.h" #include "StatusCode.h" #include +#include +#define DATABASE_FILE_NAME "/files.db" void FilesDatabase::Init(void) { - SqliteHandle::GetInstance()->Init(); + std::string dbPath = IStorageManager::GetInstance()->GetFilesDatabasePath(); + LogInfo("Database path:%s\n", (dbPath + DATABASE_FILE_NAME).c_str()); + SqliteHandle::GetInstance()->Init(dbPath + DATABASE_FILE_NAME); } void FilesDatabase::UnInit(void) { SqliteHandle::GetInstance()->UnInit(); } -StatusCode FilesDatabase::DatabaseSaveFile(const SaveFileInfo &fileInfo) +StatusCode FilesDatabase::DbSaveFile(const SaveFileInfo &fileInfo) { std::string saveFile = FilesHandle::CreateFilePathName(fileInfo.mFileName); LogInfo("Save file:%s\n", saveFile.c_str()); @@ -42,4 +46,22 @@ StatusCode FilesDatabase::DatabaseSaveFile(const SaveFileInfo &fileInfo) return CreateStatusCode(STATUS_CODE_OK); } return CreateStatusCode(STATUS_CODE_NOT_OK); +} +InfoToBeSaved FilesDatabase::CreateInfoForSavingFiles(const unsigned int &count) +{ + const unsigned long key = SqliteHandle::GetInstance()->CreateFiles(count); + std::string savingPath = IStorageManager::GetInstance()->GetFilesSavingPath(); + InfoToBeSaved info(key, savingPath); + return info; +} +StatusCode FilesDatabase::DbSaveFiles(const std::vector &info) +{ + for (auto &each : info) { + bool result = SqliteHandle::GetInstance()->SyncFile(each); + if (!result) { + LogError("Save file 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 3d184c5..be814ec 100644 --- a/middleware/FilesManager/src/sqlite3/SqliteHandle.cpp +++ b/middleware/FilesManager/src/sqlite3/SqliteHandle.cpp @@ -13,10 +13,35 @@ * limitations under the License. */ #include "SqliteHandle.h" +#include "IFilesManager.h" #include "ILog.h" #include "sqlite3.h" +#include +#include #include -#include +#include +#include +#include +#define FILES_TABLE "files" +#define TABLE_KEY "key" +#define FILE_PATH "path" +#define CREATE_TIME "time" +#define FILE_TYPE "type" +#define FILE_SIZE "size" +#define FILE_STATUS "status" +#define FILE_STATUS_BE_RECORDING "recording" +#define FILE_STATUS_COMPLETE_RECORD "complete_record" +#define FILE_STATUS_SHOULD_BE_UPLOADED "should_be_uploaded" +#define FILE_STATUS_UPLOADING "uploading" +#define FILE_STATUS_UPLOADED "uploaded" +#define FIEL_TYPE_PIR "pir" +#define FIEL_TYPE_MANUAL_TEST "manual_test" +#define FIEL_TYPE_MANUAL_PHONE "manual_phone" +#define FIEL_TYPE_TIMED "timed" +#define FILE_TYPE_UNDEFINE "undefine" +SqliteHandle::SqliteHandle() : mDb(nullptr) +{ +} std::shared_ptr &SqliteHandle::GetInstance(std::shared_ptr *impl) { static auto instance = std::make_shared(); @@ -31,17 +56,244 @@ std::shared_ptr &SqliteHandle::GetInstance(std::shared_ptr int { + if (argc > 0) { + LogInfo("last_rowid = %s\n", argv[0]); + *(unsigned long int *)last_rowid = strtoul(argv[0], nullptr, 10); + } + return 0; + }; + unsigned long int last_rowid = 0; + const char *sql2 = "SELECT last_insert_rowid();"; + rc = sqlite3_exec(mDb, sql2, last_rowid_lambda, &last_rowid, &err_msg); + if (SQLITE_OK != rc) { + LogError("SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + return 0; + } + return last_rowid - count; +} +bool SqliteHandle::SyncFile(const SyncFileInfo &info) +{ + 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) { + return false; + } + return true; +} +void SqliteHandle::DbInit(sqlite3 *db) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return; + } + 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);"; + rc = sqlite3_exec(db, sql, nullptr, nullptr, &errMsg); + if (SQLITE_OK != rc) { + LogError("Sql: %s, errMsg: %s\n", sql, errMsg); + sqlite3_free(errMsg); + } + else { + LogInfo("Sqlite init success. table = %s\n", FILES_TABLE); + } +} +bool SqliteHandle::UpdateCreateTime(sqlite3 *db, const unsigned long &key, const time_t &createTime) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return false; + } + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + if (UNDEFINE_CREATE_TIME != createTime) { + std::stringstream sqlStream; + sqlStream << "UPDATE " FILES_TABLE " SET " CREATE_TIME " = '" << createTime << "' 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::UpdateFileName(sqlite3 *db, const unsigned long &key, const std::string &fileName) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return false; + } + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + if (fileName.empty() == false) { + std::stringstream sqlStream; + sqlStream << "UPDATE " FILES_TABLE " SET " FILE_PATH " = '" << fileName << "' 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::UpdateFileSize(sqlite3 *db, const unsigned long &key, const unsigned long &fileSize) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return false; + } + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + if (UNDEFINE_FILE_SIZE != fileSize) { + std::stringstream sqlStream; + sqlStream << "UPDATE " FILES_TABLE " SET " FILE_SIZE " = '" << fileSize << "' 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::UpdateFileType(sqlite3 *db, const unsigned long &key, const FileCreateType &type) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return false; + } + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + if (FileCreateType::END != type) { + std::stringstream sqlStream; + sqlStream << "UPDATE " FILES_TABLE " SET " FILE_TYPE " = '" << ConvertFileType(type) + << "' 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; +} +std::string SqliteHandle::ConvertFileType(const FileCreateType &type) +{ + switch (type) { + case FileCreateType::PIR: + return "PIR"; + case FileCreateType::MANUAL_TEST: + return "MANUAL_TEST"; + case FileCreateType::MANUAL_PHONE: + return "MANUAL_PHONE"; + case FileCreateType::TIMED: + return "TIMED"; + case FileCreateType::END: + default: + return "undefine"; + } +} +bool SqliteHandle::UpdateFileStatus(sqlite3 *db, const unsigned long &key, const FileStatus &status) +{ + if (nullptr == db) { + LogError("db is null.\n"); + return false; + } + char *errMsg = nullptr; + int rc = SQLITE_UNDEFINE; + if (FileStatus::END != status) { + std::stringstream sqlStream; + sqlStream << "UPDATE " FILES_TABLE " SET " FILE_STATUS " = '" << ConvertFileStatus(status) + << "' 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; +} +std::string SqliteHandle::ConvertFileStatus(const FileStatus &status) +{ + switch (status) { + case FileStatus::RECORDING: + return "RECORDING"; + case FileStatus::FINISHED_RECORD: + return "FINISHED_RECORD"; + case FileStatus::SHOULD_BE_UPLOAD: + return "SHOULD_BE_UPLOAD"; + case FileStatus::UPLOADING: + return "UPLOADING"; + case FileStatus::UPLOADED: + return "UPLOADED"; + case FileStatus::END: + default: + return "undefine"; + } } \ No newline at end of file diff --git a/middleware/FilesManager/src/sqlite3/SqliteHandle.h b/middleware/FilesManager/src/sqlite3/SqliteHandle.h index 2aae728..b3df206 100644 --- a/middleware/FilesManager/src/sqlite3/SqliteHandle.h +++ b/middleware/FilesManager/src/sqlite3/SqliteHandle.h @@ -14,14 +14,37 @@ */ #ifndef SQLITE_HANDLE_H #define SQLITE_HANDLE_H +#include "IFilesManager.h" +#include "sqlite3.h" #include +#include +#include +constexpr int SQLITE_UNDEFINE = -1; class SqliteHandle { public: - SqliteHandle() = default; + SqliteHandle(); virtual ~SqliteHandle() = default; static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); - void Init(void); + void Init(const std::string &dbFileName); void UnInit(void); + unsigned long int CreateFiles(const unsigned int &count); + bool SyncFile(const SyncFileInfo &info); + bool Remove(); + bool Modified(); + bool Serach(); + +private: + static void DbInit(sqlite3 *db); + static bool UpdateCreateTime(sqlite3 *db, const unsigned long &key, const time_t &createTime); + 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 UpdateFileStatus(sqlite3 *db, const unsigned long &key, const FileStatus &status); + static std::string ConvertFileStatus(const FileStatus &status); + +private: + sqlite3 *mDb; }; #endif \ No newline at end of file diff --git a/middleware/MediaManager/include/IMediaManager.h b/middleware/MediaManager/include/IMediaManager.h index 097c8c8..fb39bcb 100644 --- a/middleware/MediaManager/include/IMediaManager.h +++ b/middleware/MediaManager/include/IMediaManager.h @@ -56,6 +56,7 @@ public: VMediaTask() = default; virtual ~VMediaTask() = default; virtual const MediaTaskType GetTaskType(void); + virtual std::string GetTargetNameForSaving(void); virtual void Response(const std::vector &response); virtual bool IsTaskFinished(void); virtual const signed int GetIsNight(void); @@ -77,10 +78,6 @@ public: virtual StatusCode StopTask(void); virtual StatusCode ClearTask(void); virtual StatusCode SetSpontaneousTaskMonitor(std::shared_ptr &task); - // virtual StatusCode BeReadyForLive(void); - // virtual StatusCode SetMediaMonitor(std::shared_ptr &monitor); - // virtual StatusCode StartMedia(void); - // virtual StatusCode StopMedia(void); }; class IMediaManager { diff --git a/middleware/MediaManager/src/IMediaManager.cpp b/middleware/MediaManager/src/IMediaManager.cpp index 68ee030..62fcd00 100644 --- a/middleware/MediaManager/src/IMediaManager.cpp +++ b/middleware/MediaManager/src/IMediaManager.cpp @@ -29,6 +29,11 @@ const MediaTaskType VMediaTask::GetTaskType(void) { return MediaTaskType::END; } +std::string VMediaTask::GetTargetNameForSaving(void) +{ + const std::string fileName = "Undefined"; + return fileName; +} void VMediaTask::Response(const std::vector &response) { } diff --git a/middleware/MediaManager/src/MediaHandle.cpp b/middleware/MediaManager/src/MediaHandle.cpp index 7a83562..7214edb 100644 --- a/middleware/MediaManager/src/MediaHandle.cpp +++ b/middleware/MediaManager/src/MediaHandle.cpp @@ -66,12 +66,17 @@ StatusCode MediaHandle::ExecuteTask(std::shared_ptr &task) return CreateStatusCode(STATUS_CODE_NOT_OK); } } - mStreamHandle = std::make_shared(); + mStreamHandle = std::make_shared(task); if (nullptr == mStreamHandle) { LogError("Create stream handle failed.\n"); + mStreamHandle.reset(); return CreateStatusCode(STATUS_CODE_NOT_OK); } - mStreamHandle->Init(); + auto code2 = mStreamHandle->Init(); + if (!IsCodeOK(code2)) { + LogError("StreamHandle init failed.\n"); + return code2; + } CameraTaskType taskType = TaskTypeConvert(task->GetTaskType()); CameraTaskParam data(taskType); auto code = mCameraHal->StartSingleTask(data); @@ -115,19 +120,22 @@ void MediaHandle::TaskTimer(void) */ mTaskRuning = false; } + 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("SdCardHal: monitor is expired.\n"); + LogWarning("mCurrentTask is expired.\n"); return; } LogInfo("Task finished response to application.\n"); - std::vector responses; - runingTask->Response(responses); + runingTask->Response(files); mCurrentTask.reset(); mMutex.unlock(); } diff --git a/middleware/MediaManager/src/RecordMp4.cpp b/middleware/MediaManager/src/RecordMp4.cpp index 9b3d117..bd06f1d 100644 --- a/middleware/MediaManager/src/RecordMp4.cpp +++ b/middleware/MediaManager/src/RecordMp4.cpp @@ -14,32 +14,55 @@ */ #include "RecordMp4.h" #include "ILog.h" +#include "IMediaManager.h" #include "MediaBase.h" +#include "StatusCode.h" #include #include #include -RecordMp4::RecordMp4() : mRecordMp4Object(nullptr) +#include +#include +#include +RecordMp4::RecordMp4(std::shared_ptr &recordTask) : mRecordMp4Object(nullptr), mRecordTask(recordTask) { } -void RecordMp4::Init(void) +StatusCode RecordMp4::Init(void) { mRecordMp4Object = ICreateMediaBase(MEDIA_HANDLE_TYPE_COMBINE_MP4); if (nullptr == mRecordMp4Object) { LogError("mRecordMp4Object is null.\n"); - return; + return CreateStatusCode(STATUS_CODE_NOT_OK); } - IOpenOutputFile(mRecordMp4Object, "./record.mp4"); + std::string videoPath = mRecordTask->GetTargetNameForSaving(); + StatusCode code = IOpenOutputFile(mRecordMp4Object, videoPath.c_str()); + if (!IsCodeOK(code)) { + LogError("OpenOutputFile failed.\n"); + ICloseOutputFile(mRecordMp4Object); + IMediaBaseFree(mRecordMp4Object); + mRecordMp4Object = nullptr; + } + return code; } -void RecordMp4::UnInit(void) +StatusCode RecordMp4::UnInit(void) { + StopHandleStream(); + return CreateStatusCode(STATUS_CODE_OK); +} +void RecordMp4::StopHandleStream(void) +{ + std::lock_guard locker(mMutex); if (mRecordMp4Object) { ICloseOutputFile(mRecordMp4Object); IMediaBaseFree(mRecordMp4Object); mRecordMp4Object = nullptr; } + std::string videoPath = mRecordTask->GetTargetNameForSaving(); + MediaTaskResponse response(videoPath.c_str()); + mTaskResponse.push_back(response); } 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}; IGetStreamData(mRecordMp4Object, stream, length, info); @@ -47,8 +70,15 @@ void RecordMp4::GetVideoStream(const void *stream, const unsigned int &length, c } void RecordMp4::GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { + std::lock_guard locker(mMutex); if (mRecordMp4Object) { StreamInfo info = {.mType = STREAM_TYPE_AUDIO_G711A}; IGetStreamData(mRecordMp4Object, stream, length, info); } +} +StatusCode RecordMp4::GetAllFiles(std::vector &files) +{ + files = std::move(mTaskResponse); + mTaskResponse.clear(); + return CreateStatusCode(STATUS_CODE_OK); } \ No newline at end of file diff --git a/middleware/MediaManager/src/RecordMp4.h b/middleware/MediaManager/src/RecordMp4.h index b8118ee..87e6968 100644 --- a/middleware/MediaManager/src/RecordMp4.h +++ b/middleware/MediaManager/src/RecordMp4.h @@ -14,19 +14,27 @@ */ #ifndef RECORD_MP4_H #define RECORD_MP4_H +#include "IMediaManager.h" #include "VStreamHandle.h" #include +#include +#include class RecordMp4 : public VStreamHandle { public: - RecordMp4(); + RecordMp4(std::shared_ptr &recordTask); virtual ~RecordMp4() = default; - void Init(void) override; - void UnInit(void) override; + 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 GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) override; + StatusCode GetAllFiles(std::vector &files) override; private: + std::mutex mMutex; void *mRecordMp4Object; + std::shared_ptr mRecordTask; + std::vector mTaskResponse; }; #endif \ No newline at end of file diff --git a/middleware/MediaManager/src/SaveStream.cpp b/middleware/MediaManager/src/SaveStream.cpp index 0d79f94..64bcce7 100644 --- a/middleware/MediaManager/src/SaveStream.cpp +++ b/middleware/MediaManager/src/SaveStream.cpp @@ -14,18 +14,20 @@ */ #include "SaveStream.h" #include "ILog.h" +#include "StatusCode.h" #include #include #include SaveStream::SaveStream() : mFileAudio(nullptr), mFileVideo(nullptr) { } -void SaveStream::Init(void) +StatusCode SaveStream::Init(void) { mFileAudio = fopen("./audio.g711", "a+"); // TODO: mFileVideo = fopen("./video.h264", "a+"); // TODO: + return CreateStatusCode(STATUS_CODE_OK); } -void SaveStream::UnInit(void) +StatusCode SaveStream::UnInit(void) { if (mFileAudio) { fclose(mFileAudio); @@ -35,6 +37,7 @@ void SaveStream::UnInit(void) fclose(mFileVideo); mFileVideo = nullptr; } + return CreateStatusCode(STATUS_CODE_OK); } void SaveStream::GetVideoStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { diff --git a/middleware/MediaManager/src/SaveStream.h b/middleware/MediaManager/src/SaveStream.h index 7cf5ba2..358abc6 100644 --- a/middleware/MediaManager/src/SaveStream.h +++ b/middleware/MediaManager/src/SaveStream.h @@ -21,8 +21,8 @@ class SaveStream : public VStreamHandle public: SaveStream(); virtual ~SaveStream() = default; - void Init(void) override; - void UnInit(void) override; + StatusCode Init(void) override; + StatusCode UnInit(void) override; 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; diff --git a/middleware/MediaManager/src/VStreamHandle.cpp b/middleware/MediaManager/src/VStreamHandle.cpp index c34beca..0efed77 100644 --- a/middleware/MediaManager/src/VStreamHandle.cpp +++ b/middleware/MediaManager/src/VStreamHandle.cpp @@ -13,15 +13,33 @@ * limitations under the License. */ #include "VStreamHandle.h" -void VStreamHandle::Init(void) +#include "ILog.h" +#include "StatusCode.h" +#include +StatusCode VStreamHandle::Init(void) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } -void VStreamHandle::UnInit(void) +StatusCode VStreamHandle::UnInit(void) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +void VStreamHandle::StopHandleStream(void) +{ + LogInfo("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"); } void VStreamHandle::GetAudioStream(const void *stream, const unsigned int &length, const unsigned long long &timeStamp) { + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); +} +StatusCode VStreamHandle::GetAllFiles(std::vector &files) +{ + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } \ No newline at end of file diff --git a/middleware/MediaManager/src/VStreamHandle.h b/middleware/MediaManager/src/VStreamHandle.h index d7e23d8..8bee00c 100644 --- a/middleware/MediaManager/src/VStreamHandle.h +++ b/middleware/MediaManager/src/VStreamHandle.h @@ -14,14 +14,19 @@ */ #ifndef V_STREAM_HANDLE_H #define V_STREAM_HANDLE_H +#include "IMediaManager.h" +#include "StatusCode.h" +#include class VStreamHandle { public: VStreamHandle() = default; virtual ~VStreamHandle() = default; - virtual void Init(void); - virtual void UnInit(void); + virtual StatusCode Init(void); + virtual StatusCode UnInit(void); + 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 StatusCode GetAllFiles(std::vector &files); }; #endif \ No newline at end of file diff --git a/middleware/StorageManager/include/IStorageManager.h b/middleware/StorageManager/include/IStorageManager.h index 126df99..69a3918 100644 --- a/middleware/StorageManager/include/IStorageManager.h +++ b/middleware/StorageManager/include/IStorageManager.h @@ -16,6 +16,7 @@ #define I_STORAGE_MANAGER_H #include "StatusCode.h" #include +#include bool CreateStorageManagerModule(void); bool DestroyStorageManagerModule(void); enum class StorageEvent @@ -53,5 +54,7 @@ public: virtual StatusCode SaveFile(const std::string &sourceFile, const std::string &savePaht); virtual const char *PrintStringStorageEvent(const StorageEvent &event); virtual StatusCode GetSdCardInfo(SdCardInfo &info); + virtual std::string GetFilesDatabasePath(void); + virtual std::string GetFilesSavingPath(void); }; #endif \ No newline at end of file diff --git a/middleware/StorageManager/src/IStorageManager.cpp b/middleware/StorageManager/src/IStorageManager.cpp index 867ccbc..8673d31 100644 --- a/middleware/StorageManager/src/IStorageManager.cpp +++ b/middleware/StorageManager/src/IStorageManager.cpp @@ -43,21 +43,38 @@ StatusCode IStorageManager::Init(void) } StatusCode IStorageManager::UnInit(void) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode IStorageManager::SetMonitor(std::shared_ptr &monitor) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } StatusCode IStorageManager::SaveFile(const std::string &sourceFile, const std::string &savePaht) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); } const char *IStorageManager::PrintStringStorageEvent(const StorageEvent &event) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return "STATUS_CODE_VIRTUAL_FUNCTION"; } StatusCode IStorageManager::GetSdCardInfo(SdCardInfo &info) { + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +std::string IStorageManager::GetFilesDatabasePath(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + std::string path = "undefined"; + return path; +} +std::string IStorageManager::GetFilesSavingPath(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION\n"); + std::string path = "undefined"; + return path; } \ No newline at end of file diff --git a/middleware/StorageManager/src/StorageBase.cpp b/middleware/StorageManager/src/StorageBase.cpp index 2320181..76ca77f 100644 --- a/middleware/StorageManager/src/StorageBase.cpp +++ b/middleware/StorageManager/src/StorageBase.cpp @@ -15,10 +15,18 @@ #include "StorageBase.h" #include "ILog.h" #include "IStorageManager.h" +#include +#include +#include +#include #include +#include +#include +#include #include #include #include +#include #include bool StorageBase::CheckDirectory(const char *filepath) { @@ -41,6 +49,7 @@ bool StorageBase::CheckDirectory(const char *filepath) } if (stat(path, &st) == -1) { if (errno == ENOENT) { + // LogInfo("mkdir:%s\n", path); if (mkdir(path, 0755) == -1) { LogError("mkdir path failed:%s\n", path); free(path); @@ -65,28 +74,38 @@ const char *StorageBase::PrintStringStorageEvent(const StorageEvent &event) switch (event) { case StorageEvent::SD_CARD_INSERT: { return "SD_CARD_INSERT"; - break; } case StorageEvent::SD_CARD_REMOVE: { return "SD_CARD_REMOVE"; - break; } case StorageEvent::SD_ABNORMAL: { return "SD_ABNORMAL"; - break; } case StorageEvent::EMMC_NORMAL: { return "EMMC_NORMAL"; - break; } case StorageEvent::END: { return "END"; - break; } default: { return "UNDEFINE"; - break; } } +} +std::string StorageBase::CreateFilesSavingPath(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); + + int year = tm_now.tm_year + 1900; + int month = tm_now.tm_mon + 1; + int day = tm_now.tm_mday; + + std::ostringstream pathStream; + pathStream << SD_CARD_MOUNT_PATH << "/DCIM/" << std::setw(4) << std::setfill('0') << year << "/" << std::setw(2) + << std::setfill('0') << month << "/" << std::setw(2) << std::setfill('0') << day << "/"; + CheckDirectory((pathStream.str() + "anyfile").c_str()); + return pathStream.str(); } \ No newline at end of file diff --git a/middleware/StorageManager/src/StorageBase.h b/middleware/StorageManager/src/StorageBase.h index 8d6e19f..90e1d6a 100644 --- a/middleware/StorageManager/src/StorageBase.h +++ b/middleware/StorageManager/src/StorageBase.h @@ -25,6 +25,7 @@ public: protected: const char *PrintStringStorageEvent(const StorageEvent &event); + std::string CreateFilesSavingPath(void); protected: std::weak_ptr mStorageMonitor; diff --git a/middleware/StorageManager/src/StorageManagerImpl.cpp b/middleware/StorageManager/src/StorageManagerImpl.cpp index b1e3a9b..f24b4e9 100644 --- a/middleware/StorageManager/src/StorageManagerImpl.cpp +++ b/middleware/StorageManager/src/StorageManagerImpl.cpp @@ -13,13 +13,14 @@ * limitations under the License. */ #include "StorageManagerImpl.h" -// #include "ILog.h" +#include "ILog.h" #include "IStorageManager.h" #include "SdCardHandle.h" #include "StatusCode.h" #include "StorageBase.h" #include #include +#define SYSTEM_FILE_PATH "/system" StatusCode StorageManagerImpl::Init(void) { SdCardHandle::SdCardInit(); @@ -43,4 +44,19 @@ StatusCode StorageManagerImpl::SaveFile(const std::string &sourceFile, const std const char *StorageManagerImpl::PrintStringStorageEvent(const StorageEvent &event) { return StorageBase::PrintStringStorageEvent(event); +} +std::string StorageManagerImpl::GetFilesDatabasePath(void) +{ + std::string path = SD_CARD_MOUNT_PATH SYSTEM_FILE_PATH "/anyfile"; + bool directoryExist = StorageBase::CheckDirectory(path.c_str()); + if (false == directoryExist) { + LogError("Directory not exist.\n"); + path = "Unavailable"; + } + path = SD_CARD_MOUNT_PATH SYSTEM_FILE_PATH; + return path; +} +std::string StorageManagerImpl::GetFilesSavingPath(void) +{ + return StorageBase::CreateFilesSavingPath(); } \ No newline at end of file diff --git a/middleware/StorageManager/src/StorageManagerImpl.h b/middleware/StorageManager/src/StorageManagerImpl.h index 689c617..420c8a8 100644 --- a/middleware/StorageManager/src/StorageManagerImpl.h +++ b/middleware/StorageManager/src/StorageManagerImpl.h @@ -23,10 +23,14 @@ class StorageManagerImpl : public SdCardHandle, public EmmcHandle public: StorageManagerImpl() = default; virtual ~StorageManagerImpl() = default; + +protected: StatusCode Init(void) override; StatusCode UnInit(void) override; StatusCode SetMonitor(std::shared_ptr &monitor) override; StatusCode SaveFile(const std::string &sourceFile, const std::string &savePaht) override; const char *PrintStringStorageEvent(const StorageEvent &event) override; + std::string GetFilesDatabasePath(void) override; + std::string GetFilesSavingPath(void) override; }; #endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegDecoder.cpp b/utils/MediaBase/src/FfmpegDecoder.cpp new file mode 100644 index 0000000..045cd77 --- /dev/null +++ b/utils/MediaBase/src/FfmpegDecoder.cpp @@ -0,0 +1,193 @@ +/* + * 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 "FfmpegDecoder.h" +#include "ILog.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +#include +FfmpegDecoder::FfmpegDecoder(const enum AVCodecID &codecId) + : mCodecId(codecId), mCodec(nullptr), mCodecCtx(nullptr), mFrame(nullptr) +{ +} +bool FfmpegDecoder::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("Codec not found\n"); + 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("Encoder 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); + ret = select_channel_layout(mCodec, &(mCodecCtx->ch_layout)); + if (ret < 0) { + LogError("Could not set channel layout\n"); + return false; + } + } + 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; + } + 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; + } + } + return true; +} +bool FfmpegDecoder::UnInit(void) +{ + if (mFrame) { + av_frame_free(&mFrame); + mFrame = nullptr; + } + if (mCodecCtx) { + avcodec_free_context(&mCodecCtx); + mCodecCtx = nullptr; + } + return true; +} +void FfmpegDecoder::DecodeData(const void *data, const size_t &size, std::function callback) +{ + AVPacket *packet = nullptr; + packet = av_packet_alloc(); + packet->data = (unsigned char *)data; + packet->size = size; + int ret = avcodec_send_packet(mCodecCtx, packet); + if (ret < 0) { + LogError("Error sending a packet for decoding\n"); + av_packet_unref(packet); + av_packet_free(&packet); + 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) { + callback(mFrame); + } + // mFrame->pts = mAudioSt.next_pts; + // mAudioSt.next_pts += mFrame->nb_samples; + // ConvertAudioFrame(mFrame, mAudioSt.enc, &mAudioSt); + // write_frame(mOc, mAudioSt.enc, mAudioSt.st, mAudioSt.frame, mAudioSt.tmp_pkt); + break; + } + av_packet_unref(packet); + av_packet_free(&packet); +} +/* just pick the highest supported samplerate */ +int FfmpegDecoder::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 FfmpegDecoder::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 FfmpegDecoder::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/FfmpegDecoder.h b/utils/MediaBase/src/FfmpegDecoder.h new file mode 100644 index 0000000..ccf3666 --- /dev/null +++ b/utils/MediaBase/src/FfmpegDecoder.h @@ -0,0 +1,56 @@ +/* + * 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_H +#define FFMPEG_DECODER_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +class FfmpegDecoder +{ +public: + FfmpegDecoder(const enum AVCodecID &codecId); + virtual ~FfmpegDecoder() = default; + bool Init(void); + bool UnInit(void); + void DecodeData(const void *data, const size_t &size, 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; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegEncoder.cpp b/utils/MediaBase/src/FfmpegEncoder.cpp new file mode 100644 index 0000000..e34a1ff --- /dev/null +++ b/utils/MediaBase/src/FfmpegEncoder.cpp @@ -0,0 +1,386 @@ +/* + * 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 "FfmpegEncoder.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 +#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) +{ +} +bool FfmpegEncoder::Init(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_STEREO; + 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->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 = 8000; + av_channel_layout_copy(&mCodecCtx->ch_layout, &src); + // st->time_base = (AVRational){1, mCodecCtx->sample_rate}; + break; + + case AVMEDIA_TYPE_VIDEO: + mCodecCtx->codec_id = mCodecId; + + mCodecCtx->bit_rate = 400000; + /* Resolution must be a multiple of two. */ + mCodecCtx->width = 1920; + mCodecCtx->height = 2160; + /* 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. */ + // st->time_base = (AVRational){1, STREAM_FRAME_RATE}; + 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; + 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 FfmpegEncoder::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 FfmpegEncoder::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 FfmpegEncoder::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; + } +} +int FfmpegEncoder::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); + mTmpPkt->stream_index = stream->index; + + /* Write the compressed frame to the media file. */ + // log_packet(fmt_ctx, pkt); + // ret = av_interleaved_write_frame(fmt_ctx, pkt); + if (callback) { + callback(mTmpPkt); + } + /* pkt is now blank (av_interleaved_write_frame() takes ownership of + * its contents and resets pkt), so that no unreferencing is necessary. + * This would be different if one used av_write_frame(). */ + // if (ret < 0) { + // fprintf(stderr, "Error while writing output packet: %s\n", av_err2str(ret)); + // return AVERROR_EXIT; + // } + } + + return ret == AVERROR_EOF ? 1 : 0; +} +bool FfmpegEncoder::OpenVideo(AVDictionary *optArg, AVStream *stream) +{ + int ret = 0; + AVDictionary *opt = nullptr; + av_dict_copy(&opt, optArg, 0); + /* 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; + } + return true; +} +bool FfmpegEncoder::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; + } + /* 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_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 *FfmpegEncoder::ConvertAudioFrame(AVFrame *decodeFrame, struct SwrContext *swr_ctx) +{ + if (nullptr == decodeFrame) { + LogError("decodeFrame is null\n"); + return nullptr; + } + 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, + 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; + } + + mFrame->pts = av_rescale_q(mSamplesCount, (AVRational){1, mCodecCtx->sample_rate}, mCodecCtx->time_base); + mSamplesCount += dst_nb_samples; + return mFrame; +} +AVFrame *FfmpegEncoder::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 *FfmpegEncoder::alloc_audio_frame(enum AVSampleFormat sample_fmt, const AVChannelLayout *channel_layout, + int sample_rate, int nb_samples) +{ + AVFrame *frame = av_frame_alloc(); + if (!frame) { + LogInfo("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) { + LogInfo("Error allocating an audio buffer\n"); + return nullptr; + } + } + + return frame; +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegEncoder.h b/utils/MediaBase/src/FfmpegEncoder.h new file mode 100644 index 0000000..9686cf4 --- /dev/null +++ b/utils/MediaBase/src/FfmpegEncoder.h @@ -0,0 +1,68 @@ +/* + * 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_H +#define FFMPEG_ENCODER_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +class FfmpegEncoder +{ +public: + FfmpegEncoder(const enum AVCodecID &codecId); + virtual ~FfmpegEncoder() = default; + bool Init(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; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegMuxStream.cpp b/utils/MediaBase/src/FfmpegMuxStream.cpp index e99f3f2..3d10f94 100644 --- a/utils/MediaBase/src/FfmpegMuxStream.cpp +++ b/utils/MediaBase/src/FfmpegMuxStream.cpp @@ -18,6 +18,7 @@ #include "StatusCode.h" #include #include +#include #include #include #include @@ -30,11 +31,13 @@ extern "C" { #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -106,9 +109,9 @@ StatusCode FfmpegMuxStream::OpenOutputFile(const std::string &fileName) ret = avio_open(&oc->pb, fileName.c_str(), AVIO_FLAG_WRITE); if (ret < 0) { char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; - LogInfo("Could not open '%s': %s\n", - fileName.c_str(), - av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + LogError("Could not open '%s': %s\n", + fileName.c_str(), + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); return CreateStatusCode(STATUS_CODE_NOT_OK); } } @@ -116,15 +119,17 @@ StatusCode FfmpegMuxStream::OpenOutputFile(const std::string &fileName) ret = avformat_write_header(oc, &opt); if (ret < 0) { char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; - LogInfo("Error occurred when opening output file: %s\n", - av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + LogError("Error occurred when opening output file: %s\n", + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); return CreateStatusCode(STATUS_CODE_NOT_OK); } return CreateStatusCode(STATUS_CODE_OK); } StatusCode FfmpegMuxStream::CloseOutputFile(void) { - av_write_trailer(mOc); + if (mOc && mOc->pb) { + av_write_trailer(mOc); + } if (mFrameVideo) { av_frame_free(&mFrameVideo); mFrameVideo = nullptr; @@ -141,6 +146,9 @@ StatusCode FfmpegMuxStream::CloseOutputFile(void) avcodec_free_context(&mCodecAudioContext); mCodecAudioContext = nullptr; } + if (nullptr == mOc) { + return CreateStatusCode(STATUS_CODE_OK); + } close_stream(mOc, &mVideoSt); close_stream(mOc, &mAudioSt); memset(&mVideoSt, 0, sizeof(mVideoSt)); @@ -189,12 +197,26 @@ void FfmpegMuxStream::GetVideoStream(const void *data, const size_t &size, const av_packet_unref(packet); av_packet_free(&packet); } +void save_code_stream_file(const void *data, const unsigned int &size) +{ + FILE *file = NULL; + file = fopen("./test.g711a", "a+"); + + if (file) { + fwrite(data, 1, size, file); + fflush(file); + } + + if (file) + fclose(file); +} void FfmpegMuxStream::GetAudioStream(const void *data, const size_t &size, const StreamInfo &streamInfo) { AVPacket *packet = nullptr; packet = av_packet_alloc(); packet->data = (unsigned char *)data; packet->size = size; + // save_code_stream_file(data, size); int ret = avcodec_send_packet(mCodecAudioContext, packet); if (ret < 0) { LogInfo("Error sending a packet for decoding\n"); @@ -213,7 +235,7 @@ void FfmpegMuxStream::GetAudioStream(const void *data, const size_t &size, const } mFrameAudio->pts = mAudioSt.next_pts; mAudioSt.next_pts += mFrameAudio->nb_samples; - ConvertAudioFrame(mFrameVideo, mAudioSt.enc, &mAudioSt); + ConvertAudioFrame(mFrameAudio, mAudioSt.enc, &mAudioSt); write_frame(mOc, mAudioSt.enc, mAudioSt.st, mAudioSt.frame, mAudioSt.tmp_pkt); break; } @@ -224,7 +246,7 @@ bool FfmpegMuxStream::add_stream(OutputStream *ost, AVFormatContext *oc, const A { AVCodecContext *c; int i; - + LogInfo("Encoder: %s\n", avcodec_get_name(codec_id)); /* find the encoder */ *codec = avcodec_find_encoder(codec_id); if (!(*codec)) { @@ -264,6 +286,7 @@ bool FfmpegMuxStream::add_stream(OutputStream *ost, AVFormatContext *oc, const A c->sample_rate = 44100; } } + c->sample_rate = 8000; av_channel_layout_copy(&c->ch_layout, &src); ost->st->time_base = (AVRational){1, c->sample_rate}; break; @@ -273,8 +296,8 @@ bool FfmpegMuxStream::add_stream(OutputStream *ost, AVFormatContext *oc, const A c->bit_rate = 400000; /* Resolution must be a multiple of two. */ - c->width = 352; - c->height = 288; + c->width = 1920; + c->height = 2160; /* 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 @@ -498,6 +521,7 @@ bool FfmpegMuxStream::InitCodecAudio(enum AVCodecID codecId, AVCodec **codec, AV { int ret = 0; *codec = (AVCodec *)avcodec_find_decoder(codecId); + // *codec = (AVCodec *)avcodec_find_decoder_by_name("libfdk_aac"); if (!(*codec)) { LogError("Codec not found\n"); return false; @@ -509,6 +533,8 @@ bool FfmpegMuxStream::InitCodecAudio(enum AVCodecID codecId, AVCodec **codec, AV } /* put sample parameters */ (*codec_ctx)->bit_rate = 64000; + // (*codec_ctx)->bit_rate = 352800; + // (*codec_ctx)->sample_rate = 8000; /* check that the encoder supports s16 pcm input */ (*codec_ctx)->sample_fmt = AV_SAMPLE_FMT_S16; @@ -529,6 +555,8 @@ bool FfmpegMuxStream::InitCodecAudio(enum AVCodecID codecId, AVCodec **codec, AV LogError("Could not open codec:%s\n", av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); return false; } + /* Set the packet timebase for the decoder. */ + // (*codec_ctx)->pkt_timebase = {.num = 1, .den = 44100}; *frame = av_frame_alloc(); if (!(*frame)) { LogError("Could not allocate video frame\n"); @@ -572,7 +600,7 @@ int FfmpegMuxStream::write_frame(AVFormatContext *fmt_ctx, AVCodecContext *c, AV pkt->stream_index = st->index; /* Write the compressed frame to the media file. */ - log_packet(fmt_ctx, pkt); + // log_packet(fmt_ctx, pkt); ret = av_interleaved_write_frame(fmt_ctx, pkt); /* pkt is now blank (av_interleaved_write_frame() takes ownership of * its contents and resets pkt), so that no unreferencing is necessary. diff --git a/utils/MediaBase/src/FfmpegMuxStreamV2.cpp b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp new file mode 100644 index 0000000..ea69a05 --- /dev/null +++ b/utils/MediaBase/src/FfmpegMuxStreamV2.cpp @@ -0,0 +1,137 @@ +/* + * 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 "FfmpegMuxStreamV2.h" +#include "FfmpegOutputStream.h" +#include "ILog.h" +#include "MediaBase.h" +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +// #include +#include +FfmpegMuxStreamV2::FfmpegMuxStreamV2() : mOutputFormat(nullptr), mOptions(nullptr) +{ +} +StatusCode FfmpegMuxStreamV2::OpenOutputFile(const std::string &fileName) +{ + return OpenMuxOutputFile(fileName); +} +StatusCode FfmpegMuxStreamV2::CloseOutputFile(void) +{ + if (mOutputFormat && mOutputFormat->pb) { + av_write_trailer(mOutputFormat); + } + if (mVideoStream) { + mVideoStream->UnInit(); + } + if (mAudioStream) { + mAudioStream->UnInit(); + } + if (nullptr == mOutputFormat) { + return CreateStatusCode(STATUS_CODE_OK); + } + if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { + /* Close the output file. */ + avio_closep(&mOutputFormat->pb); + } + avformat_free_context(mOutputFormat); + return CreateStatusCode(STATUS_CODE_OK); +} +void FfmpegMuxStreamV2::GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) +{ + if (streamInfo.mType == STREAM_TYPE_VIDEO_H264 && mVideoStream) { + mVideoStream->WriteSourceData(data, size); + } + if (streamInfo.mType == STREAM_TYPE_AUDIO_G711A && mAudioStream) { + mAudioStream->WriteSourceData(data, size); + } +} +StatusCode FfmpegMuxStreamV2::OpenMuxOutputFile(const std::string &fileName) +{ + AVDictionary *opt = nullptr; + int ret = 0; + /* allocate the output media context */ + avformat_alloc_output_context2(&mOutputFormat, nullptr, "mp4", fileName.c_str()); + if (!mOutputFormat) { + LogError("Could not deduce output format from file.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + /* 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, mOutputFormat->oformat->video_codec, AV_CODEC_ID_H264); + mVideoStream->SetWriteSourceDataCallback( + std::bind(&FfmpegMuxStreamV2::GetAVPacketDataCallback, this, std::placeholders::_1)); + } + if (mOutputFormat->oformat->audio_codec != AV_CODEC_ID_NONE) { + mAudioStream = AddStream(mOutputFormat, mOutputFormat->oformat->audio_codec, AV_CODEC_ID_PCM_ALAW); + mAudioStream->SetWriteSourceDataCallback( + std::bind(&FfmpegMuxStreamV2::GetAVPacketDataCallback, this, std::placeholders::_1)); + } + av_dump_format(mOutputFormat, 0, fileName.c_str(), 1); + /* open the output file, if needed */ + if (!(mOutputFormat->oformat->flags & AVFMT_NOFILE)) { + ret = avio_open(&mOutputFormat->pb, fileName.c_str(), 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(), + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + } + } + /* Write the stream header, if any. */ + ret = avformat_write_header(mOutputFormat, &opt); + 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 CreateStatusCode(STATUS_CODE_NOT_OK); + } + return CreateStatusCode(STATUS_CODE_OK); +} +void FfmpegMuxStreamV2::GetAVPacketDataCallback(AVPacket *pkt) +{ + // std::lock_guard locker(mMutex); + int ret = 0; + ret = av_interleaved_write_frame(mOutputFormat, pkt); + /* pkt is now blank (av_interleaved_write_frame() takes ownership of + * its contents and resets pkt), so that no unreferencing is necessary. + * This would be different if one used av_write_frame(). */ + if (ret < 0) { + char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; + LogInfo("Error while writing output packet: %s\n", + av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); + } +} +std::shared_ptr FfmpegMuxStreamV2::AddStream(AVFormatContext *outputFormat, + enum AVCodecID encodecId, enum AVCodecID decodecId) +{ + auto stream = std::make_shared(encodecId, decodecId); + stream->Init(outputFormat); + return stream; +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegMuxStreamV2.h b/utils/MediaBase/src/FfmpegMuxStreamV2.h new file mode 100644 index 0000000..cc1ea90 --- /dev/null +++ b/utils/MediaBase/src/FfmpegMuxStreamV2.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_MUX_STREAM_V2_H +#define FFMPEG_MUX_STREAM_V2_H +#include "FfmpegBase.h" +#include "FfmpegOutputStream.h" +#include "MediaBase.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +class FfmpegMuxStreamV2 : virtual public FfmpegBase +{ +public: + FfmpegMuxStreamV2(); + virtual ~FfmpegMuxStreamV2() = default; + +public: // About combine file. + StatusCode OpenOutputFile(const std::string &fileName) override; + StatusCode CloseOutputFile(void) override; + void GetStreamData(const void *data, const size_t &size, const StreamInfo &streamInfo) override; + +private: + StatusCode OpenMuxOutputFile(const std::string &fileName); + void GetAVPacketDataCallback(AVPacket *pkt); + +private: + static std::shared_ptr AddStream(AVFormatContext *outputFormat, enum AVCodecID encodecId, + enum AVCodecID decodecId); + +private: + std::mutex mMutex; + AVFormatContext *mOutputFormat; + std::shared_ptr mVideoStream; + std::shared_ptr mAudioStream; + AVDictionary *mOptions; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOutputStream.cpp b/utils/MediaBase/src/FfmpegOutputStream.cpp new file mode 100644 index 0000000..b4e8004 --- /dev/null +++ b/utils/MediaBase/src/FfmpegOutputStream.cpp @@ -0,0 +1,78 @@ +/* + * 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 "FfmpegOutputStream.h" +#include "FfmpegDecoder.h" +#include "FfmpegEncoder.h" +#include "ILog.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif +#include +#include +#include +FfmpegOutputStream::FfmpegOutputStream(const AVCodecID &encodecId, const AVCodecID &dncodecId) + : mEncodecId(encodecId), mDeccodecId(dncodecId), mTmpPkt(nullptr), mStream(nullptr) +{ +} +bool FfmpegOutputStream::Init(AVFormatContext *outputFormat) +{ + mDecodeCallback = std::bind(&FfmpegOutputStream::GetDecodeDataCallback, this, std::placeholders::_1); + mTmpPkt = av_packet_alloc(); + if (!mTmpPkt) { + LogError("Could not allocate AVPacket\n"); + return false; + } + mStream = avformat_new_stream(outputFormat, nullptr); + if (!mStream) { + LogError("Could not allocate stream\n"); + return false; + } + mDecoder = std::make_shared(mDeccodecId); + mDecoder->Init(); + mStream->id = outputFormat->nb_streams - 1; + mEncoder = std::make_shared(mEncodecId); + mEncoder->Init(outputFormat->flags); + mStream->time_base = mEncoder->GetTimeBase(); + mEncoder->OpenEncoder(nullptr, mStream); + return true; +} +void FfmpegOutputStream::UnInit(void) +{ + mEncoder->UnInit(); + mDecoder->UnInit(); + av_packet_free(&mTmpPkt); +} +void FfmpegOutputStream::WriteSourceData(const void *data, const size_t &size) +{ + mDecoder->DecodeData(data, size, mDecodeCallback); +} +void FfmpegOutputStream::SetWriteSourceDataCallback(std::function callback) +{ + mEncodeCallback = callback; +} +void FfmpegOutputStream::GetDecodeDataCallback(AVFrame *frame) +{ + mEncoder->EncodeData(frame, mStream, mEncodeCallback); +} +void FfmpegOutputStream::GetEncodeDataCallback(AVPacket *pkt) +{ +} \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegOutputStream.h b/utils/MediaBase/src/FfmpegOutputStream.h new file mode 100644 index 0000000..6dd68b4 --- /dev/null +++ b/utils/MediaBase/src/FfmpegOutputStream.h @@ -0,0 +1,63 @@ +/* + * 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_OUTPUT_STREAM_H +#define FFMPEG_OUTPUT_STREAM_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 +class FfmpegOutputStream +{ +public: + FfmpegOutputStream(const AVCodecID &encodecId, const AVCodecID &dncodecId); + virtual ~FfmpegOutputStream() = default; + bool Init(AVFormatContext *outputFormat); + void UnInit(void); + void WriteSourceData(const void *data, const size_t &size); + void SetWriteSourceDataCallback(std::function callback); + +private: + void GetDecodeDataCallback(AVFrame *frame); + void GetEncodeDataCallback(AVPacket *pkt); + +private: + const AVCodecID mEncodecId; + const AVCodecID mDeccodecId; + AVPacket *mTmpPkt; + std::shared_ptr mEncoder; + std::shared_ptr mDecoder; + AVStream *mStream; + std::function mDecodeCallback; + std::function mEncodeCallback; +}; +#endif \ No newline at end of file diff --git a/utils/MediaBase/src/FfmpegReadFile.cpp b/utils/MediaBase/src/FfmpegReadFile.cpp index aa750e9..f4142f9 100644 --- a/utils/MediaBase/src/FfmpegReadFile.cpp +++ b/utils/MediaBase/src/FfmpegReadFile.cpp @@ -123,6 +123,10 @@ void FfmpegReadFile::ReadFileThread(AVFormatContext *pFormatCtx, int mediaStream pFormatCtx->streams[mediaStreamIndex]->time_base.den; // 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", + // pFormatCtx->streams[mediaStreamIndex]->time_base.num, + // pFormatCtx->streams[mediaStreamIndex]->time_base.den); + // LogInfo("pFormatCtx->bit_rate = %ld\n", pFormatCtx->bit_rate); ReadFrame(&packet); std::this_thread::sleep_for(std::chrono::milliseconds(playTimeMs)); } diff --git a/utils/MediaBase/src/MediaBaseImpl.h b/utils/MediaBase/src/MediaBaseImpl.h index 060c9e2..c9b68d0 100644 --- a/utils/MediaBase/src/MediaBaseImpl.h +++ b/utils/MediaBase/src/MediaBaseImpl.h @@ -15,11 +15,11 @@ #ifndef MEDIA_BASE_IMPL_H #define MEDIA_BASE_IMPL_H #include "FfmpegBase.h" -#include "FfmpegMuxStream.h" +#include "FfmpegMuxStreamV2.h" #include "FfmpegReadFile.h" #include "IMediaBase.h" #include -class MediaBaseImpl : public FfmpegReadFile, public FfmpegMuxStream +class MediaBaseImpl : public FfmpegReadFile, public FfmpegMuxStreamV2 { public: MediaBaseImpl(const MediaHandleType &type);