Commit fce4580d65558172deb944f08e65d0640546360c
1 parent
b00d3d35
更新最新代码:修改解码器实现方式并修改一些bug;优化代码
Showing
8 changed files
with
945 additions
and
1089 deletions
src/ai_platform/MultiSourceProcess.cpp
@@ -207,8 +207,7 @@ bool CMultiSourceProcess::AddTask(task_param _cur_task_param){ | @@ -207,8 +207,7 @@ bool CMultiSourceProcess::AddTask(task_param _cur_task_param){ | ||
207 | } | 207 | } |
208 | 208 | ||
209 | AbstractDecoder* dec = pDecManager->createDecoder(config); | 209 | AbstractDecoder* dec = pDecManager->createDecoder(config); |
210 | - if (!dec) | ||
211 | - { | 210 | + if (!dec){ |
212 | return false; | 211 | return false; |
213 | } | 212 | } |
214 | 213 | ||
@@ -417,6 +416,10 @@ bool CMultiSourceProcess::finish_task(const string taskID, const bool delete_sna | @@ -417,6 +416,10 @@ bool CMultiSourceProcess::finish_task(const string taskID, const bool delete_sna | ||
417 | mq_manager_->publish(mq_type_t::HEART_BEAT_MQ, json_str.c_str(), true); | 416 | mq_manager_->publish(mq_type_t::HEART_BEAT_MQ, json_str.c_str(), true); |
418 | #endif | 417 | #endif |
419 | 418 | ||
419 | +#ifdef WITH_SECOND_PROCESS | ||
420 | + pedestrian_vehicle_retrograde_.force_release_result(taskID); //221024 byzsh | ||
421 | +#endif | ||
422 | + | ||
420 | m_task_param_manager->delete_task_param(taskID); | 423 | m_task_param_manager->delete_task_param(taskID); |
421 | 424 | ||
422 | return true; | 425 | return true; |
@@ -496,6 +499,7 @@ int CMultiSourceProcess::algorthim_vpt(vector<DeviceMemory*> vec_gpuMem){ | @@ -496,6 +499,7 @@ int CMultiSourceProcess::algorthim_vpt(vector<DeviceMemory*> vec_gpuMem){ | ||
496 | sy_img img; | 499 | sy_img img; |
497 | img.w_ = mem->getWidth(); | 500 | img.w_ = mem->getWidth(); |
498 | img.h_ = mem->getHeight(); | 501 | img.h_ = mem->getHeight(); |
502 | + img.c_ = mem->getChannel(); | ||
499 | img.data_ = mem->getMem(); | 503 | img.data_ = mem->getMem(); |
500 | vpt_interest_imgs.push_back(img); | 504 | vpt_interest_imgs.push_back(img); |
501 | vpt_interest_task_id.push_back(mem->getId()); | 505 | vpt_interest_task_id.push_back(mem->getId()); |
@@ -517,7 +521,6 @@ int CMultiSourceProcess::algorthim_vpt(vector<DeviceMemory*> vec_gpuMem){ | @@ -517,7 +521,6 @@ int CMultiSourceProcess::algorthim_vpt(vector<DeviceMemory*> vec_gpuMem){ | ||
517 | #ifndef VEHICLE_MULTI_BOXES | 521 | #ifndef VEHICLE_MULTI_BOXES |
518 | /* 快照优选(内部可实现不同的快照优选策略) */ | 522 | /* 快照优选(内部可实现不同的快照优选策略) */ |
519 | m_snapshot_reprocessing->update_bestsnapshot(vec_vptMem, vptResult, deleteObjectID); | 523 | m_snapshot_reprocessing->update_bestsnapshot(vec_vptMem, vptResult, deleteObjectID); |
520 | - | ||
521 | /* for snapshot algorithm. 轨迹结束目标 做最后的结果返回(当前返回算法结果+快照保存路径)*/ | 524 | /* for snapshot algorithm. 轨迹结束目标 做最后的结果返回(当前返回算法结果+快照保存路径)*/ |
522 | vehicle_snapshot(vpt_interest_task_id, deleteObjectID); | 525 | vehicle_snapshot(vpt_interest_task_id, deleteObjectID); |
523 | #else | 526 | #else |
@@ -530,8 +533,12 @@ int CMultiSourceProcess::algorthim_vpt(vector<DeviceMemory*> vec_gpuMem){ | @@ -530,8 +533,12 @@ int CMultiSourceProcess::algorthim_vpt(vector<DeviceMemory*> vec_gpuMem){ | ||
530 | /* for pedestrian safety det. 行人安全分析算法模块 */ | 533 | /* for pedestrian safety det. 行人安全分析算法模块 */ |
531 | // algorthim_pedestrian_safety(vpt_interest_task_id, vpt_interest_imgs,vptResult); | 534 | // algorthim_pedestrian_safety(vpt_interest_task_id, vpt_interest_imgs,vptResult); |
532 | 535 | ||
533 | - /* for retrograde & trespass algor 逆行&非法闯入算法模块 */ | ||
534 | - algorthim_retrograde_trespass(vpt_interest_task_id, vec_vptMem, vptResult, deleteObjectID); | 536 | + // 逆行 |
537 | + algorthim_retrograde(vpt_interest_task_id, vec_vptMem, vptResult); | ||
538 | + retrograde_snapshot(vpt_interest_task_id, deleteObjectID); | ||
539 | + // 闯入 | ||
540 | + algorthim_trespass(vpt_interest_task_id, vec_vptMem, vptResult, deleteObjectID); | ||
541 | + trespass_snapshot(vpt_interest_task_id, deleteObjectID); | ||
535 | 542 | ||
536 | // #endif | 543 | // #endif |
537 | 544 | ||
@@ -662,21 +669,12 @@ void CMultiSourceProcess::vehicle_snapshot(vector<string>& vpt_interest_task_id, | @@ -662,21 +669,12 @@ void CMultiSourceProcess::vehicle_snapshot(vector<string>& vpt_interest_task_id, | ||
662 | for (int &j : deleteObjectID[i]) // loop algor type. | 669 | for (int &j : deleteObjectID[i]) // loop algor type. |
663 | { | 670 | { |
664 | OBJ_KEY obj_key = {*task_iter, j}; | 671 | OBJ_KEY obj_key = {*task_iter, j}; |
665 | - endframe_obj_process(obj_key, algorithm_type_t::PLACEHOLDER); | 672 | + // 该路任务开启了抓拍功能 开始抓拍保存;若未开启抓拍,清空显存资源 |
673 | + vehicle_locus_finished(obj_key); | ||
666 | } | 674 | } |
667 | } | 675 | } |
668 | } | 676 | } |
669 | 677 | ||
670 | -/* 轨迹结束帧需要做的算法模块 */ | ||
671 | -int CMultiSourceProcess::endframe_obj_process(const OBJ_KEY &obj_key, algorithm_type_t algor_type) { | ||
672 | - | ||
673 | - // 该路任务开启了抓拍功能 开始抓拍保存;若未开启抓拍,清空显存资源 | ||
674 | - vehicle_locus_finished(obj_key); | ||
675 | - | ||
676 | - /* 开启行人&机动车逆行算法模块,获取该目标的算法分析结果 返回结果+快照 最后释放资源 */ | ||
677 | - endframe_retrograde_trespass(obj_key); | ||
678 | -} | ||
679 | - | ||
680 | void CMultiSourceProcess::vehicle_locus_finished(const OBJ_KEY obj_key) { | 678 | void CMultiSourceProcess::vehicle_locus_finished(const OBJ_KEY obj_key) { |
681 | auto task_param_ptr = m_task_param_manager->get_task_algor_param(obj_key.video_id); | 679 | auto task_param_ptr = m_task_param_manager->get_task_algor_param(obj_key.video_id); |
682 | auto task_other_param_ptr = m_task_param_manager->get_task_other_param(obj_key.video_id); | 680 | auto task_other_param_ptr = m_task_param_manager->get_task_other_param(obj_key.video_id); |
@@ -809,16 +807,12 @@ void CMultiSourceProcess::timing_snapshot_thread(){ | @@ -809,16 +807,12 @@ void CMultiSourceProcess::timing_snapshot_thread(){ | ||
809 | LOG_INFO("timing_snapshot_thread end."); | 807 | LOG_INFO("timing_snapshot_thread end."); |
810 | } | 808 | } |
811 | 809 | ||
812 | -void CMultiSourceProcess::algorthim_retrograde_trespass(vector<string>& vpt_interest_task_id, vector<DeviceMemory*> vpt_interest_imgs, | ||
813 | - vector<onelevel_det_result>& vptResult ,vector<vector<int>>& deleteObjectID){ | 810 | +// 逆行 |
811 | +void CMultiSourceProcess::algorthim_retrograde(vector<string>& vpt_interest_task_id, vector<DeviceMemory*> vpt_interest_imgs, | ||
812 | + vector<onelevel_det_result>& vptResult){ | ||
814 | vector<string> interest_task_id; | 813 | vector<string> interest_task_id; |
815 | vector<onelevel_det_result> interest_vpt_result; | 814 | vector<onelevel_det_result> interest_vpt_result; |
816 | - vector<DeviceMemory*> interest_imgs; | ||
817 | - | ||
818 | - vector<string> trespass_interest_task_id; | ||
819 | - vector<onelevel_det_result> trespass_interest_vpt_result; | ||
820 | - vector<vector<int>> trespass_interest_deleteobjs; | ||
821 | - vector<DeviceMemory*> trespass_interest_imgs; | 815 | + vector<DeviceMemory*> interest_imgs; |
822 | 816 | ||
823 | int _idx = 0; | 817 | int _idx = 0; |
824 | for (auto _task_id_iter = vpt_interest_task_id.begin(); _task_id_iter != vpt_interest_task_id.end(); | 818 | for (auto _task_id_iter = vpt_interest_task_id.begin(); _task_id_iter != vpt_interest_task_id.end(); |
@@ -833,6 +827,30 @@ void CMultiSourceProcess::algorthim_retrograde_trespass(vector<string>& vpt_inte | @@ -833,6 +827,30 @@ void CMultiSourceProcess::algorthim_retrograde_trespass(vector<string>& vpt_inte | ||
833 | interest_imgs.emplace_back(vpt_interest_imgs[_idx]); | 827 | interest_imgs.emplace_back(vpt_interest_imgs[_idx]); |
834 | interest_vpt_result.emplace_back(vptResult[_idx]); | 828 | interest_vpt_result.emplace_back(vptResult[_idx]); |
835 | } | 829 | } |
830 | + } | ||
831 | + | ||
832 | + LOG_DEBUG("retrograde interest_vpt_result size: {}", interest_vpt_result.size()); | ||
833 | + | ||
834 | + if (!interest_imgs.empty()){ | ||
835 | + pedestrian_vehicle_retrograde_.update_mstreams(interest_task_id, interest_imgs, interest_vpt_result); | ||
836 | + } | ||
837 | +} | ||
838 | + | ||
839 | +// 闯入 | ||
840 | +void CMultiSourceProcess::algorthim_trespass(vector<string>& vpt_interest_task_id, vector<DeviceMemory*> vpt_interest_imgs, | ||
841 | + vector<onelevel_det_result>& vptResult ,vector<vector<int>>& deleteObjectID){ | ||
842 | + | ||
843 | + vector<string> trespass_interest_task_id; | ||
844 | + vector<onelevel_det_result> trespass_interest_vpt_result; | ||
845 | + vector<vector<int>> trespass_interest_deleteobjs; | ||
846 | + vector<DeviceMemory*> trespass_interest_imgs; | ||
847 | + | ||
848 | + int _idx = 0; | ||
849 | + for (auto _task_id_iter = vpt_interest_task_id.begin(); _task_id_iter != vpt_interest_task_id.end(); | ||
850 | + ++_task_id_iter, ++_idx) // loop task_id; | ||
851 | + { | ||
852 | + auto task_id = *_task_id_iter; | ||
853 | + auto algor_map = m_task_param_manager->get_task_other_param(task_id); | ||
836 | 854 | ||
837 | if (algor_map->find(algorithm_type_t::PEDESTRIAN_TRESPASS) != algor_map->end() || | 855 | if (algor_map->find(algorithm_type_t::PEDESTRIAN_TRESPASS) != algor_map->end() || |
838 | algor_map->find(algorithm_type_t::VEHICLE_TRESPASS) != algor_map->end()) { | 856 | algor_map->find(algorithm_type_t::VEHICLE_TRESPASS) != algor_map->end()) { |
@@ -843,36 +861,58 @@ void CMultiSourceProcess::algorthim_retrograde_trespass(vector<string>& vpt_inte | @@ -843,36 +861,58 @@ void CMultiSourceProcess::algorthim_retrograde_trespass(vector<string>& vpt_inte | ||
843 | } | 861 | } |
844 | } | 862 | } |
845 | 863 | ||
846 | - LOG_DEBUG("trespass_interest_vpt_result size: {}", trespass_interest_vpt_result.size()); | ||
847 | - | ||
848 | - if (!interest_imgs.empty()) | ||
849 | - pedestrian_vehicle_retrograde_.update_mstreams(interest_task_id, interest_imgs, | ||
850 | - interest_vpt_result); | 864 | + LOG_DEBUG("trespass interest_vpt_result size: {}", trespass_interest_vpt_result.size()); |
851 | 865 | ||
852 | if (!trespass_interest_imgs.empty()) { | 866 | if (!trespass_interest_imgs.empty()) { |
853 | - pedestrian_vehicle_trespass_.update_mstreams( | ||
854 | - trespass_interest_task_id, trespass_interest_imgs, trespass_interest_vpt_result, | ||
855 | - trespass_interest_deleteobjs); | 867 | + pedestrian_vehicle_trespass_.update_mstreams( trespass_interest_task_id, trespass_interest_imgs, |
868 | + trespass_interest_vpt_result, trespass_interest_deleteobjs); | ||
856 | } | 869 | } |
857 | } | 870 | } |
858 | 871 | ||
859 | -int CMultiSourceProcess::endframe_retrograde_trespass(const OBJ_KEY &obj_key) { | ||
860 | - auto task_param_ptr = m_task_param_manager->get_task_algor_param(obj_key.video_id); | ||
861 | 872 | ||
862 | - /* 开启行人&机动车逆行算法模块,获取该目标的算法分析结果 返回结果+快照 最后释放资源 */ | ||
863 | - if (task_param_ptr->human_algors.find(algorithm_type_t::PEDESTRIAN_RETROGRADE) != | ||
864 | - task_param_ptr->human_algors.end()) | ||
865 | - retrograde_trespass_alarm(obj_key, algorithm_type_t::PEDESTRIAN_RETROGRADE); | 873 | +// for snapshot algorithm. 轨迹结束目标 做最后的结果返回(当前返回算法结果+快照保存路径) |
874 | +void CMultiSourceProcess::retrograde_snapshot(vector<string>& vpt_interest_task_id, vector<vector<int>> deleteObjectID) { | ||
875 | + auto task_iter = vpt_interest_task_id.begin(); | ||
876 | + | ||
877 | + for (int i = 0; i < deleteObjectID.size(); i++, ++task_iter) // loop taskId. | ||
878 | + { | ||
879 | + for (int &j : deleteObjectID[i]) // loop algor type. | ||
880 | + { | ||
881 | + OBJ_KEY obj_key = {*task_iter, j}; | ||
882 | + auto task_param_ptr = m_task_param_manager->get_task_algor_param(obj_key.video_id); | ||
883 | + | ||
884 | + /* 开启行人&机动车逆行算法模块,获取该目标的算法分析结果 返回结果+快照 最后释放资源 */ | ||
885 | + if (task_param_ptr->human_algors.find(algorithm_type_t::PEDESTRIAN_RETROGRADE) != task_param_ptr->human_algors.end()){ | ||
886 | + retrograde_trespass_alarm(obj_key, algorithm_type_t::PEDESTRIAN_RETROGRADE); | ||
887 | + } | ||
888 | + | ||
889 | + if (task_param_ptr->vehicle_algors.find(algorithm_type_t::VEHICLE_RETROGRADE) != task_param_ptr->vehicle_algors.end()){ | ||
890 | + retrograde_trespass_alarm(obj_key, algorithm_type_t::VEHICLE_RETROGRADE); | ||
891 | + } | ||
892 | + } | ||
893 | + } | ||
894 | +} | ||
866 | 895 | ||
867 | - if (task_param_ptr->vehicle_algors.find(algorithm_type_t::VEHICLE_RETROGRADE) != | ||
868 | - task_param_ptr->vehicle_algors.end()) | ||
869 | - retrograde_trespass_alarm(obj_key, algorithm_type_t::VEHICLE_RETROGRADE); | 896 | +// for snapshot algorithm. 轨迹结束目标 做最后的结果返回(当前返回算法结果+快照保存路径) |
897 | +void CMultiSourceProcess::trespass_snapshot(vector<string>& vpt_interest_task_id, vector<vector<int>> deleteObjectID) { | ||
898 | + auto task_iter = vpt_interest_task_id.begin(); | ||
870 | 899 | ||
871 | - if (task_param_ptr->human_algors.find(algorithm_type_t::PEDESTRIAN_TRESPASS) != task_param_ptr->human_algors.end()) | ||
872 | - retrograde_trespass_alarm(obj_key, algorithm_type_t::PEDESTRIAN_TRESPASS); | 900 | + for (int i = 0; i < deleteObjectID.size(); i++, ++task_iter) // loop taskId. |
901 | + { | ||
902 | + for (int &j : deleteObjectID[i]) // loop algor type. | ||
903 | + { | ||
904 | + OBJ_KEY obj_key = {*task_iter, j}; | ||
905 | + auto task_param_ptr = m_task_param_manager->get_task_algor_param(obj_key.video_id); | ||
873 | 906 | ||
874 | - if (task_param_ptr->vehicle_algors.find(algorithm_type_t::VEHICLE_TRESPASS) != task_param_ptr->vehicle_algors.end()) | ||
875 | - retrograde_trespass_alarm(obj_key, algorithm_type_t::VEHICLE_TRESPASS); | 907 | + if (task_param_ptr->human_algors.find(algorithm_type_t::PEDESTRIAN_TRESPASS) != task_param_ptr->human_algors.end()){ |
908 | + retrograde_trespass_alarm(obj_key, algorithm_type_t::PEDESTRIAN_TRESPASS); | ||
909 | + } | ||
910 | + | ||
911 | + if (task_param_ptr->vehicle_algors.find(algorithm_type_t::VEHICLE_TRESPASS) != task_param_ptr->vehicle_algors.end()){ | ||
912 | + retrograde_trespass_alarm(obj_key, algorithm_type_t::VEHICLE_TRESPASS); | ||
913 | + } | ||
914 | + } | ||
915 | + } | ||
876 | } | 916 | } |
877 | 917 | ||
878 | void CMultiSourceProcess::retrograde_trespass_alarm(const OBJ_KEY &obj_key, const algorithm_type_t &algor_type) { | 918 | void CMultiSourceProcess::retrograde_trespass_alarm(const OBJ_KEY &obj_key, const algorithm_type_t &algor_type) { |
@@ -946,7 +986,6 @@ bool CMultiSourceProcess::save_snapshot_process(const OBJ_KEY &obj_key, const al | @@ -946,7 +986,6 @@ bool CMultiSourceProcess::save_snapshot_process(const OBJ_KEY &obj_key, const al | ||
946 | obj_save_info.json_str = json_str; | 986 | obj_save_info.json_str = json_str; |
947 | m_save_snapshot_reprocessing->reprocessing_process_wo_locus_async(obj_save_info); | 987 | m_save_snapshot_reprocessing->reprocessing_process_wo_locus_async(obj_save_info); |
948 | } | 988 | } |
949 | - | ||
950 | 989 | ||
951 | return true; | 990 | return true; |
952 | } | 991 | } |
953 | \ No newline at end of file | 992 | \ No newline at end of file |
src/ai_platform/MultiSourceProcess.h
@@ -49,9 +49,11 @@ public: | @@ -49,9 +49,11 @@ public: | ||
49 | private: | 49 | private: |
50 | // 算法相关 | 50 | // 算法相关 |
51 | int algorthim_vpt(vector<DeviceMemory*> vec_gpuMem); | 51 | int algorthim_vpt(vector<DeviceMemory*> vec_gpuMem); |
52 | - // 逆行&非法闯入算法模块 | ||
53 | - void algorthim_retrograde_trespass(vector<string>& vpt_interest_task_id, vector<DeviceMemory*> vpt_interest_imgs, | ||
54 | - vector<onelevel_det_result>& vptResult ,vector<vector<int>>& deleteObjectID); | 52 | + // 逆行 |
53 | + void algorthim_retrograde(vector<string>& vpt_interest_task_id, vector<DeviceMemory*> vpt_interest_imgs, vector<onelevel_det_result>& vptResult); | ||
54 | + // 闯入 | ||
55 | + void algorthim_trespass(vector<string>& vpt_interest_task_id, vector<DeviceMemory*> vpt_interest_imgs, | ||
56 | + vector<onelevel_det_result>& vptResult ,vector<vector<int>>& deleteObjectID); | ||
55 | 57 | ||
56 | private: | 58 | private: |
57 | // 工具处理函数 | 59 | // 工具处理函数 |
@@ -63,12 +65,11 @@ private: | @@ -63,12 +65,11 @@ private: | ||
63 | void vehicle_snapshot(vector<string>& vpt_interest_task_id, vector<vector<int>> deleteObjectID); | 65 | void vehicle_snapshot(vector<string>& vpt_interest_task_id, vector<vector<int>> deleteObjectID); |
64 | void vehicle_locus_finished(const OBJ_KEY obj_key); | 66 | void vehicle_locus_finished(const OBJ_KEY obj_key); |
65 | 67 | ||
66 | - int endframe_obj_process(const OBJ_KEY &obj_key, algorithm_type_t algor_type); | ||
67 | - | ||
68 | bool save_snapshot_process(const OBJ_KEY &obj_key, const algorithm_type_t &algorithm_type, vpc_img_info img_info, vpc_img_info roi_img, | 68 | bool save_snapshot_process(const OBJ_KEY &obj_key, const algorithm_type_t &algorithm_type, vpc_img_info img_info, vpc_img_info roi_img, |
69 | const long long id,const std::string &json_str); | 69 | const long long id,const std::string &json_str); |
70 | 70 | ||
71 | - int endframe_retrograde_trespass(const OBJ_KEY &obj_key); | 71 | + void retrograde_snapshot(vector<string>& vpt_interest_task_id, vector<vector<int>> deleteObjectID); |
72 | + void trespass_snapshot(vector<string>& vpt_interest_task_id, vector<vector<int>> deleteObjectID); | ||
72 | void retrograde_trespass_alarm(const OBJ_KEY &obj_key, const algorithm_type_t &algor_type) ; | 73 | void retrograde_trespass_alarm(const OBJ_KEY &obj_key, const algorithm_type_t &algor_type) ; |
73 | 74 | ||
74 | private: | 75 | private: |
src/decoder/dvpp/DvppDec.cpp deleted
1 | -#include "DvppDec.h" | ||
2 | -#include "DvppSourceManager.h" | ||
3 | - | ||
4 | -#include "../../util/vpc_util.h" | ||
5 | - | ||
6 | -struct Vdec_CallBack_UserData { | ||
7 | - uint64_t frameId; | ||
8 | - long startTime; | ||
9 | - long sendTime; | ||
10 | - // void* vdecOutputBuf; | ||
11 | - DvppDec* self; | ||
12 | - shared_ptr<MemNode> inBufNode; | ||
13 | - Vdec_CallBack_UserData() { | ||
14 | - frameId = 0; | ||
15 | - } | ||
16 | -}; | ||
17 | - | ||
18 | -static const int g_pkt_size = 1024 * 1024; | ||
19 | - | ||
20 | - DvppDec::DvppDec(){ | ||
21 | - m_decode_thread = 0; | ||
22 | - m_cached_mem = nullptr; | ||
23 | - } | ||
24 | - | ||
25 | - DvppDec::~DvppDec(){ | ||
26 | - releaseResource(); | ||
27 | - } | ||
28 | - | ||
29 | - bool DvppDec::init_vdpp(DvppDecConfig cfg){ | ||
30 | - | ||
31 | - m_dec_name = cfg.dec_name; | ||
32 | - | ||
33 | - LOG_INFO("[{}]- Init device start...", m_dec_name); | ||
34 | - | ||
35 | - m_dvpp_deviceId = atoi(cfg.dev_id.c_str()); | ||
36 | - | ||
37 | - if(cfg.codec_id == 0){ | ||
38 | - // 66:Baseline,77:Main,>=100:High | ||
39 | - if(cfg.profile == 77){ | ||
40 | - enType = H264_MAIN_LEVEL; | ||
41 | - }else if(cfg.profile < 77){ | ||
42 | - enType = H264_BASELINE_LEVEL; | ||
43 | - }else{ | ||
44 | - enType = H264_HIGH_LEVEL; | ||
45 | - } | ||
46 | - }else if(cfg.codec_id == 1){ | ||
47 | - // h265只有main | ||
48 | - enType = H265_MAIN_LEVEL; | ||
49 | - }else { | ||
50 | - LOG_ERROR("[{}]- codec_id is not supported!", m_dec_name); | ||
51 | - return false; | ||
52 | - } | ||
53 | - | ||
54 | - post_decoded_cbk = cfg.post_decoded_cbk; | ||
55 | - m_pktQueueptr = cfg.pktQueueptr; | ||
56 | - | ||
57 | - // DvppSourceManager 创建时包含 aclInit,析构时包含 aclFinalize | ||
58 | - DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); | ||
59 | - m_context = pSrcMgr->getContext(m_dvpp_deviceId); | ||
60 | - m_dvpp_channel = pSrcMgr->getChannel(m_dvpp_deviceId); | ||
61 | - if(m_dvpp_channel < 0){ | ||
62 | - LOG_ERROR("[{}]-该设备channel已经用完了!", m_dec_name); | ||
63 | - return false; | ||
64 | - } | ||
65 | - | ||
66 | - do | ||
67 | - { | ||
68 | - CHECK_AND_BREAK(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed !"); | ||
69 | - | ||
70 | - int ret = 0; | ||
71 | - // int ret = picConverter.init(m_context, m_dec_name); | ||
72 | - // if(ret != ACL_ERROR_NONE){ | ||
73 | - // LOG_ERROR("[{}]- acldvppMalloc failed!, ret:{}", m_dec_name, ret); | ||
74 | - // break; | ||
75 | - // } | ||
76 | - | ||
77 | - // queue_size 最小应大于16,否则关键帧之间距离太远的时候会导致回调函数与循环队列卡死 | ||
78 | - for (size_t i = 0; i < 20; i++){ | ||
79 | - void *vdecInputbuf = nullptr; | ||
80 | - ret = acldvppMalloc((void **)&vdecInputbuf, g_pkt_size); | ||
81 | - if(ret != ACL_ERROR_NONE){ | ||
82 | - LOG_ERROR("[{}]- acldvppMalloc failed!, ret:{}", m_dec_name, ret); | ||
83 | - // 析构函数中有对channel 的补救性释放,所以这里可以直接return | ||
84 | - return false;; | ||
85 | - } | ||
86 | - m_vec_vdec.push_back(vdecInputbuf); | ||
87 | - } | ||
88 | - | ||
89 | - if(!m_vdecQueue.init(m_vec_vdec)){ | ||
90 | - break; | ||
91 | - } | ||
92 | - | ||
93 | - m_vdec_out_size = cfg.width * cfg.height * 3 / 2; | ||
94 | - | ||
95 | - LOG_INFO("[{}]- init vdpp success! device:{} channel:{}", m_dec_name, m_dvpp_deviceId, m_dvpp_channel); | ||
96 | - return true; | ||
97 | - | ||
98 | - } while (0); | ||
99 | - | ||
100 | - LOG_INFO("[{}]- init vdpp failed!", m_dec_name); | ||
101 | - // 初始化失败,释放channel | ||
102 | - pSrcMgr->releaseChannel(m_dvpp_deviceId, m_dvpp_channel); | ||
103 | - return false; | ||
104 | -} | ||
105 | - | ||
106 | -bool DvppDec::start(){ | ||
107 | - m_bRunning = true; | ||
108 | - | ||
109 | - pthread_create(&m_decode_thread,0, | ||
110 | - [](void* arg) | ||
111 | - { | ||
112 | - DvppDec* a=(DvppDec*)arg; | ||
113 | - a->decode_thread(); | ||
114 | - return (void*)0; | ||
115 | - } | ||
116 | - ,this); | ||
117 | - | ||
118 | - return true; | ||
119 | -} | ||
120 | - | ||
121 | -static void *ReportThd(void *arg) | ||
122 | -{ | ||
123 | - DvppDec *self = (DvppDec *)arg; | ||
124 | - if(nullptr != self){ | ||
125 | - self->doProcessReport(); | ||
126 | - } | ||
127 | - return (void *)0; | ||
128 | -} | ||
129 | - | ||
130 | -void DvppDec::doProcessReport(){ | ||
131 | - | ||
132 | - aclError ret = aclrtSetDevice(m_dvpp_deviceId); | ||
133 | - if(ret != ACL_ERROR_NONE){ | ||
134 | - // cout << "aclrtSetDevice failed" << endl; | ||
135 | - LOG_ERROR("aclrtSetDevice failed !"); | ||
136 | - return ; | ||
137 | - } | ||
138 | - | ||
139 | - aclrtContext ctx; | ||
140 | - ret = aclrtCreateContext(&ctx, m_dvpp_deviceId); | ||
141 | - if (ret != ACL_ERROR_NONE) { | ||
142 | - // cout << "aclrtCreateContext failed " << endl; | ||
143 | - LOG_ERROR("aclrtCreateContext failed !"); | ||
144 | - return ; | ||
145 | - } | ||
146 | - | ||
147 | - CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(ctx), "aclrtSetCurrentContext failed"); | ||
148 | - // 阻塞等待vdec线程开始 | ||
149 | - | ||
150 | - while (!m_bExitReportThd) { | ||
151 | - aclrtProcessReport(1000); | ||
152 | - } | ||
153 | - | ||
154 | - ret = aclrtDestroyContext(ctx); | ||
155 | - if(ret != ACL_ERROR_NONE){ | ||
156 | - LOG_ERROR("aclrtDestroyContext failed !"); | ||
157 | - } | ||
158 | - LOG_INFO("doProcessReport exit."); | ||
159 | -} | ||
160 | - | ||
161 | -static void VdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *pUserData) | ||
162 | -{ | ||
163 | - Vdec_CallBack_UserData *userData = (Vdec_CallBack_UserData *) pUserData; | ||
164 | - if(nullptr != userData){ | ||
165 | - DvppDec* self = userData->self; | ||
166 | - if(self != nullptr){ | ||
167 | - | ||
168 | - self->doVdppVdecCallBack(input, output); | ||
169 | - } | ||
170 | - delete userData; | ||
171 | - userData = nullptr; | ||
172 | - } | ||
173 | -} | ||
174 | - | ||
175 | -void DvppDec::doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output){ | ||
176 | - | ||
177 | - // dvpp_crop(output); | ||
178 | - | ||
179 | - CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); | ||
180 | - | ||
181 | - void *inputDataDev = acldvppGetStreamDescData(input); | ||
182 | - void *outputDataDev = acldvppGetPicDescData(output); | ||
183 | - uint32_t outputSize = acldvppGetPicDescSize(output); | ||
184 | - uint32_t width = acldvppGetPicDescWidth(output); | ||
185 | - uint32_t width_stride = acldvppGetPicDescWidthStride(output); | ||
186 | - uint32_t height = acldvppGetPicDescHeight(output); | ||
187 | - uint32_t height_stride = acldvppGetPicDescHeightStride(output); | ||
188 | - | ||
189 | - | ||
190 | - DvppDataMemory* mem = new DvppDataMemory(width, width_stride, height, height_stride, outputSize, m_dec_name, to_string(m_dvpp_deviceId), false, (unsigned char *)outputDataDev); | ||
191 | - post_decoded_cbk(m_postDecArg, mem); | ||
192 | - | ||
193 | - if(m_bSnapShoting){ | ||
194 | - // 缓存snapshot | ||
195 | - std::unique_lock<std::mutex> locker(m_cached_mutex); | ||
196 | - | ||
197 | - m_cached_mem = new DvppDataMemory(-1, width, width_stride, height, height_stride, outputSize, m_dec_name, to_string(m_dvpp_deviceId), false); | ||
198 | - if(m_cached_mem != nullptr){ | ||
199 | - aclrtMemcpy(m_cached_mem->getMem(), outputSize, (unsigned char *)outputDataDev, outputSize, ACL_MEMCPY_DEVICE_TO_DEVICE); | ||
200 | - } | ||
201 | - | ||
202 | - locker.unlock(); | ||
203 | - m_cached_cond.notify_one(); | ||
204 | - m_bSnapShoting = false; | ||
205 | - } | ||
206 | - | ||
207 | - | ||
208 | - | ||
209 | - | ||
210 | -// DvppDataMemory* rgbMem = picConverter.convert2bgr(output, width, height, false); | ||
211 | -// if(rgbMem != nullptr){ | ||
212 | -// #ifdef TEST_DECODER | ||
213 | -// // D2H | ||
214 | -// if(vdecHostAddr == nullptr){ | ||
215 | -// CHECK_NOT_RETURN(aclrtMallocHost(&vdecHostAddr, width * height * 3), "aclrtMallocHost failed"); | ||
216 | -// } | ||
217 | -// uint32_t data_size = rgbMem->getSize(); | ||
218 | -// CHECK_AND_RETURN_NOVALUE(aclrtMemcpy(vdecHostAddr, data_size, rgbMem->getMem(), data_size, ACL_MEMCPY_DEVICE_TO_HOST), "D2H aclrtMemcpy failed"); | ||
219 | - | ||
220 | -// // 保存vdec结果 | ||
221 | -// if(count_frame > 45 && count_frame < 50) | ||
222 | -// { | ||
223 | -// string file_name = "./yuv_pic/vdec_out_"+ m_dec_name +".rgb" ; | ||
224 | -// FILE *outputFile = fopen(file_name.c_str(), "a"); | ||
225 | -// if(outputFile){ | ||
226 | -// fwrite(vdecHostAddr, data_size, sizeof(char), outputFile); | ||
227 | -// fclose(outputFile); | ||
228 | -// } | ||
229 | -// } | ||
230 | -// else if(count_frame > 50 && vdecHostAddr != nullptr){ | ||
231 | -// CHECK_NOT_RETURN(aclrtFreeHost(vdecHostAddr), "aclrtFreeHost failed"); | ||
232 | -// vdecHostAddr = nullptr; | ||
233 | -// } | ||
234 | -// count_frame++; | ||
235 | -// #endif | ||
236 | -// post_decoded_cbk(m_postDecArg, rgbMem); | ||
237 | -// }else{ | ||
238 | -// LOG_ERROR("[{}]- convert2bgr failed !", m_dec_name); | ||
239 | -// } | ||
240 | - | ||
241 | - // // 测试 | ||
242 | - // acldvppFree(outputDataDev); | ||
243 | - // outputDataDev = nullptr; | ||
244 | - | ||
245 | - m_vdecQueue.addHead(); | ||
246 | - | ||
247 | - CHECK_AND_RETURN_NOVALUE(acldvppDestroyStreamDesc(input), "acldvppDestroyStreamDesc failed"); | ||
248 | - CHECK_AND_RETURN_NOVALUE(acldvppDestroyPicDesc(output), "acldvppDestroyPicDesc failed"); | ||
249 | -} | ||
250 | - | ||
251 | -void DvppDec::close(){ | ||
252 | - m_bRunning=false; | ||
253 | - | ||
254 | - if(m_decode_thread != 0){ | ||
255 | - pthread_join(m_decode_thread,0); | ||
256 | - } | ||
257 | -} | ||
258 | - | ||
259 | -bool DvppDec::sendVdecEos(aclvdecChannelDesc *vdecChannelDesc){ | ||
260 | - // create stream desc | ||
261 | - acldvppStreamDesc *streamInputDesc = acldvppCreateStreamDesc(); | ||
262 | - if (streamInputDesc == nullptr) { | ||
263 | - LOG_ERROR("[{}]- fail to create input stream desc", m_dec_name); | ||
264 | - return false; | ||
265 | - } | ||
266 | - aclError ret = acldvppSetStreamDescEos(streamInputDesc, 1); | ||
267 | - if (ret != ACL_SUCCESS) { | ||
268 | - LOG_ERROR("[{}]- fail to set eos for stream desc, errorCode = {}", m_dec_name, static_cast<int32_t>(ret)); | ||
269 | - (void)acldvppDestroyStreamDesc(streamInputDesc); | ||
270 | - return false; | ||
271 | - } | ||
272 | - | ||
273 | - // send vdec eos frame. when all vdec callback are completed, aclvdecSendFrame can be returned. | ||
274 | - LOG_INFO("[{}]- send eos", m_dec_name); | ||
275 | - ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, nullptr, nullptr, nullptr); | ||
276 | - (void)acldvppDestroyStreamDesc(streamInputDesc); | ||
277 | - if (ret != ACL_SUCCESS) { | ||
278 | - LOG_ERROR("[{}]- fail to send eos frame, ret={}", m_dec_name, ret); | ||
279 | - return false; | ||
280 | - } | ||
281 | - | ||
282 | - return true; | ||
283 | -} | ||
284 | - | ||
285 | -void DvppDec::releaseResource(){ | ||
286 | - for(int i = 0; i < m_vec_vdec.size(); i++){ | ||
287 | - if(m_vec_vdec[i] != nullptr){ | ||
288 | - acldvppFree(m_vec_vdec[i]); | ||
289 | - m_vec_vdec[i] = nullptr; | ||
290 | - } | ||
291 | - } | ||
292 | - m_vec_vdec.clear(); | ||
293 | - | ||
294 | - DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); | ||
295 | - pSrcMgr->releaseChannel(m_dvpp_deviceId, m_dvpp_channel); | ||
296 | -} | ||
297 | - | ||
298 | -void DvppDec::decode_thread(){ | ||
299 | - | ||
300 | - long startTime = UtilTools::get_cur_time_ms(); | ||
301 | - | ||
302 | - int ret = -1; | ||
303 | - | ||
304 | - // // dvpp解码参数 | ||
305 | - // CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); | ||
306 | - | ||
307 | - m_bExitReportThd = false; | ||
308 | - pthread_t report_thread; | ||
309 | - ret = pthread_create(&report_thread, nullptr, ReportThd, (void *)this); | ||
310 | - if(ret != 0){ | ||
311 | - LOG_ERROR("[{}]- pthread_create failed", m_dec_name); | ||
312 | - return; | ||
313 | - } | ||
314 | - | ||
315 | - aclrtSetDevice(m_dvpp_deviceId); | ||
316 | - aclrtContext ctx; | ||
317 | - ret = aclrtCreateContext(&ctx, m_dvpp_deviceId); | ||
318 | - if (ret != ACL_ERROR_NONE) { | ||
319 | - // cout << "aclrtCreateContext failed " << endl; | ||
320 | - LOG_ERROR("aclrtCreateContext failed !"); | ||
321 | - return ; | ||
322 | - } | ||
323 | - | ||
324 | - // 创建aclvdecChannelDesc类型的数据 | ||
325 | - aclvdecChannelDesc *vdecChannelDesc = aclvdecCreateChannelDesc(); | ||
326 | - if (vdecChannelDesc == nullptr) { | ||
327 | - LOG_ERROR("[{}]- aclvdecCreateChannelDesc failed", m_dec_name); | ||
328 | - return; | ||
329 | - } | ||
330 | - do{ | ||
331 | - // 创建 channel dec结构体 | ||
332 | - // 通道ID在dvpp层面为0~31 | ||
333 | - CHECK_AND_BREAK(aclvdecSetChannelDescChannelId(vdecChannelDesc, m_dvpp_channel), "aclvdecSetChannelDescChannelId failed"); | ||
334 | - CHECK_AND_BREAK(aclvdecSetChannelDescThreadId(vdecChannelDesc, report_thread), "aclvdecSetChannelDescThreadId failed"); | ||
335 | - CHECK_AND_BREAK(aclvdecSetChannelDescCallback(vdecChannelDesc, VdecCallback), "aclvdecSetChannelDescCallback failed"); | ||
336 | - CHECK_AND_BREAK(aclvdecSetChannelDescEnType(vdecChannelDesc, enType), "aclvdecSetChannelDescEnType failed"); | ||
337 | - CHECK_AND_BREAK(aclvdecSetChannelDescOutPicFormat(vdecChannelDesc, PIXEL_FORMAT_YUV_SEMIPLANAR_420), "aclvdecSetChannelDescOutPicFormat failed"); | ||
338 | - CHECK_AND_BREAK(aclvdecCreateChannel(vdecChannelDesc), "aclvdecCreateChannel failed"); | ||
339 | - | ||
340 | - uint64_t frame_count = 0; | ||
341 | - bool bBreak = false; | ||
342 | - while (m_bRunning) | ||
343 | - { | ||
344 | - if (m_bPause){ | ||
345 | - std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
346 | - continue; | ||
347 | - } | ||
348 | - int ret = sentFrame(vdecChannelDesc, frame_count); | ||
349 | - if(ret == 2){ | ||
350 | - break; | ||
351 | - bBreak = true; | ||
352 | - }else if(ret == 1){ | ||
353 | - continue; | ||
354 | - } | ||
355 | - | ||
356 | - frame_count++; | ||
357 | - } | ||
358 | - | ||
359 | - // 尽量保证数据全部解码完成 | ||
360 | - int sum = 0; | ||
361 | - if(!bBreak){ | ||
362 | - aclrtSetDevice(m_dvpp_deviceId); | ||
363 | - aclrtSetCurrentContext(ctx); | ||
364 | - while(!m_pktQueueptr->isEmpty()){ | ||
365 | - int ret = sentFrame(vdecChannelDesc, frame_count); | ||
366 | - if(ret == 2){ | ||
367 | - break; | ||
368 | - } | ||
369 | - std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
370 | - sum++; | ||
371 | - if(sum > 40){ | ||
372 | - // 避免卡死 | ||
373 | - break; | ||
374 | - } | ||
375 | - } | ||
376 | - } | ||
377 | - | ||
378 | - sendVdecEos(vdecChannelDesc); | ||
379 | - | ||
380 | - CHECK_NOT_RETURN(aclvdecDestroyChannel(vdecChannelDesc), "aclvdecDestroyChannel failed"); | ||
381 | - }while(0); | ||
382 | - | ||
383 | - CHECK_NOT_RETURN(aclvdecDestroyChannelDesc(vdecChannelDesc), "aclvdecDestroyChannelDesc failed"); | ||
384 | - | ||
385 | - // report_thread 需后于destroy退出 | ||
386 | - m_bRunning = false; | ||
387 | - m_bExitReportThd = true; | ||
388 | - CHECK_NOT_RETURN(pthread_join(report_thread, nullptr), "pthread_join failed"); | ||
389 | - | ||
390 | - releaseResource(); | ||
391 | - LOG_INFO("[{}]- decode thread exit.", m_dec_name); | ||
392 | -} | ||
393 | - | ||
394 | -int DvppDec::sentFrame(aclvdecChannelDesc *vdecChannelDesc, uint64_t frame_count){ | ||
395 | - | ||
396 | - AVPacket * pkt = m_pktQueueptr->getHead(); | ||
397 | - if(pkt == nullptr){ | ||
398 | - std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
399 | - return 1; | ||
400 | - } | ||
401 | - // 解码 | ||
402 | - void *vdecInputbuf = m_vdecQueue.getTail(); | ||
403 | - if(vdecInputbuf == nullptr){ | ||
404 | - std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
405 | - return 1; | ||
406 | - } | ||
407 | - | ||
408 | - int ret = aclrtMemcpy(vdecInputbuf, pkt->size, pkt->data, pkt->size, ACL_MEMCPY_HOST_TO_DEVICE); | ||
409 | - if(ACL_ERROR_NONE != ret){ | ||
410 | - LOG_ERROR("[{}]- aclrtMemcpy failed", m_dec_name); | ||
411 | - return 2; | ||
412 | - } | ||
413 | - | ||
414 | - void *vdecOutputBuf = nullptr; | ||
415 | - ret = acldvppMalloc((void **)&vdecOutputBuf, m_vdec_out_size); | ||
416 | - if(ret != ACL_ERROR_NONE){ | ||
417 | - LOG_ERROR("[{}]- acldvppMalloc failed", m_dec_name); | ||
418 | - return 2; | ||
419 | - } | ||
420 | - | ||
421 | - acldvppStreamDesc *input_stream_desc = nullptr; | ||
422 | - acldvppPicDesc *output_pic_desc = nullptr; | ||
423 | - do{ | ||
424 | - input_stream_desc = acldvppCreateStreamDesc(); | ||
425 | - if (input_stream_desc == nullptr) { | ||
426 | - LOG_ERROR("[{}]- acldvppCreateStreamDesc failed", m_dec_name); | ||
427 | - break; | ||
428 | - } | ||
429 | - output_pic_desc = acldvppCreatePicDesc(); | ||
430 | - if (output_pic_desc == nullptr) { | ||
431 | - LOG_ERROR("[{}]- acldvppCreatePicDesc failed", m_dec_name); | ||
432 | - break; | ||
433 | - } | ||
434 | - CHECK_AND_BREAK(acldvppSetStreamDescData(input_stream_desc, vdecInputbuf), "acldvppSetStreamDescData failed"); | ||
435 | - CHECK_AND_BREAK(acldvppSetStreamDescSize(input_stream_desc, pkt->size), "acldvppSetStreamDescSize failed"); | ||
436 | - CHECK_AND_BREAK(acldvppSetPicDescData(output_pic_desc, vdecOutputBuf), "acldvppSetPicDescData failed"); | ||
437 | - CHECK_AND_BREAK(acldvppSetPicDescSize(output_pic_desc, m_vdec_out_size), "acldvppSetPicDescSize failed"); | ||
438 | - | ||
439 | - Vdec_CallBack_UserData *user_data = NULL; | ||
440 | - user_data = new Vdec_CallBack_UserData; | ||
441 | - user_data->frameId = frame_count; | ||
442 | - // user_data->startTime = startTime; | ||
443 | - user_data->sendTime = UtilTools::get_cur_time_ms(); | ||
444 | - user_data->self = this; | ||
445 | - ret = aclvdecSendFrame(vdecChannelDesc, input_stream_desc, output_pic_desc, nullptr, reinterpret_cast<void *>(user_data)); | ||
446 | - av_packet_unref(pkt); | ||
447 | - m_pktQueueptr->addHead(); | ||
448 | - if(ret != ACL_ERROR_NONE){ | ||
449 | - delete user_data; | ||
450 | - user_data = nullptr; | ||
451 | - LOG_ERROR("[{}]- aclvdecSendFrame failed", m_dec_name); | ||
452 | - break; | ||
453 | - } | ||
454 | - | ||
455 | - m_vdecQueue.addTail(); | ||
456 | - | ||
457 | - return 0; | ||
458 | - }while (0); | ||
459 | - | ||
460 | - // 报错情形 | ||
461 | - if(input_stream_desc){ | ||
462 | - CHECK_NOT_RETURN(acldvppDestroyStreamDesc(input_stream_desc), "acldvppDestroyStreamDesc failed"); | ||
463 | - } | ||
464 | - if(output_pic_desc){ | ||
465 | - CHECK_NOT_RETURN(acldvppDestroyPicDesc(output_pic_desc), "acldvppDestroyPicDesc failed"); | ||
466 | - } | ||
467 | - | ||
468 | - if (vdecOutputBuf){ | ||
469 | - acldvppFree(vdecOutputBuf); | ||
470 | - vdecOutputBuf = nullptr; | ||
471 | - } | ||
472 | - | ||
473 | - return 1; | ||
474 | -} | ||
475 | - | ||
476 | - | ||
477 | -void DvppDec::setPostDecArg(const void* postDecArg){ | ||
478 | - m_postDecArg = postDecArg; | ||
479 | -} | ||
480 | - | ||
481 | -void DvppDec::pause(){ | ||
482 | - m_bPause = true; | ||
483 | -} | ||
484 | - | ||
485 | -void DvppDec::resume(){ | ||
486 | - m_bPause = false; | ||
487 | -} | ||
488 | - | ||
489 | -DeviceMemory* DvppDec::snapshot(){ | ||
490 | - // 开始抓拍 | ||
491 | - m_bSnapShoting = true; | ||
492 | - | ||
493 | - std::unique_lock<std::mutex> locker(m_cached_mutex); | ||
494 | - while (m_cached_mem == nullptr) | ||
495 | - m_cached_cond.wait(locker); // Unlock mutex and wait to be notified | ||
496 | - locker.unlock(); | ||
497 | - | ||
498 | - DeviceMemory* mem = m_cached_mem; | ||
499 | - m_cached_mem = nullptr; | ||
500 | - | ||
501 | - return mem; | ||
502 | -} | ||
503 | \ No newline at end of file | 0 | \ No newline at end of file |
src/decoder/dvpp/DvppDec.h deleted
1 | -#include<string> | ||
2 | -#include <pthread.h> | ||
3 | - | ||
4 | -#include "dvpp_headers.h" | ||
5 | -#include "depend_headers.h" | ||
6 | -#include "user_mem.h" | ||
7 | -#include "CircularQueue.hpp" | ||
8 | -#include "VpcPicConverter.h" | ||
9 | -#include "FFReceiver.h" | ||
10 | - | ||
11 | -#include <queue> | ||
12 | -#include <mutex> | ||
13 | -#include <condition_variable> | ||
14 | - | ||
15 | -using namespace std; | ||
16 | - | ||
17 | -// #define TEST_DECODER | ||
18 | - | ||
19 | -struct DvppDecConfig{ | ||
20 | - string dec_name; | ||
21 | - POST_DECODE_CALLBACK post_decoded_cbk; // 解码数据回调接口 | ||
22 | - string dev_id; // gpu id | ||
23 | - bool force_tcp{true}; // 是否指定使用tcp连接 | ||
24 | - int skip_frame{1}; // 跳帧数 | ||
25 | - int codec_id; // 0 : h264 1:h265 | ||
26 | - int profile; | ||
27 | - CircularQueue<AVPacket*> *pktQueueptr; | ||
28 | - | ||
29 | - int width; | ||
30 | - int height; | ||
31 | -}; | ||
32 | - | ||
33 | - | ||
34 | -class DvppDec { | ||
35 | -public: | ||
36 | - DvppDec(); | ||
37 | - ~DvppDec(); | ||
38 | - bool init_vdpp(DvppDecConfig cfg); | ||
39 | - void setPostDecArg(const void* postDecArg); | ||
40 | - bool start(); | ||
41 | - void close(); | ||
42 | - void pause(); | ||
43 | - void resume(); | ||
44 | - DeviceMemory* snapshot(); | ||
45 | - | ||
46 | -public: | ||
47 | - void doProcessReport(); | ||
48 | - void doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output); | ||
49 | - | ||
50 | -private: | ||
51 | - void decode_thread(); | ||
52 | - void releaseResource(); | ||
53 | - bool sendVdecEos(aclvdecChannelDesc *vdecChannelDesc); | ||
54 | - int sentFrame(aclvdecChannelDesc *vdecChannelDesc, uint64_t frame_count); | ||
55 | - | ||
56 | -private: | ||
57 | - | ||
58 | - bool m_bRunning{false}; | ||
59 | - bool m_bPause{false}; | ||
60 | - bool m_bExitReportThd{false}; | ||
61 | - | ||
62 | - int m_dvpp_deviceId {-1}; | ||
63 | - int m_dvpp_channel {-1}; | ||
64 | - aclrtContext m_context; | ||
65 | - acldvppStreamFormat enType; | ||
66 | - | ||
67 | - pthread_t m_decode_thread; | ||
68 | - | ||
69 | - DvppDecConfig m_cfg; | ||
70 | - string m_dec_name; | ||
71 | - | ||
72 | - vector<void*> m_vec_vdec; | ||
73 | - CircularQueue<void *> m_vdecQueue; | ||
74 | - CircularQueue<AVPacket *> *m_pktQueueptr; | ||
75 | - | ||
76 | - const void * m_postDecArg; | ||
77 | - POST_DECODE_CALLBACK post_decoded_cbk; | ||
78 | - | ||
79 | - VpcPicConverter picConverter; | ||
80 | - | ||
81 | - int m_vdec_out_size {-1}; | ||
82 | - | ||
83 | - bool m_bSnapShoting{false}; | ||
84 | - DvppDataMemory* m_cached_mem; | ||
85 | - mutex m_cached_mutex; | ||
86 | - condition_variable m_cached_cond; | ||
87 | - | ||
88 | -#ifdef TEST_DECODER | ||
89 | - void *vdecHostAddr = nullptr; | ||
90 | - int count_frame = 0; | ||
91 | -#endif | ||
92 | - | ||
93 | -}; | ||
94 | \ No newline at end of file | 0 | \ No newline at end of file |
src/decoder/dvpp/DvppDecoder.cpp
1 | #include "DvppDecoder.h" | 1 | #include "DvppDecoder.h" |
2 | +#include "DvppSourceManager.h" | ||
2 | 3 | ||
3 | -void receiver_finish_cbk(const void* userPtr){ | ||
4 | - if(userPtr != nullptr){ | ||
5 | - DvppDecoder* self = (DvppDecoder*)userPtr; | ||
6 | - self->taskFinishing(); | 4 | + |
5 | +struct Vdec_CallBack_UserData { | ||
6 | + uint64_t frameId; | ||
7 | + long startTime; | ||
8 | + long sendTime; | ||
9 | + // void* vdecOutputBuf; | ||
10 | + DvppDecoder* self; | ||
11 | + Vdec_CallBack_UserData() { | ||
12 | + frameId = 0; | ||
7 | } | 13 | } |
8 | -} | 14 | +}; |
9 | 15 | ||
10 | DvppDecoder::DvppDecoder(){ | 16 | DvppDecoder::DvppDecoder(){ |
11 | - m_pktQueueptr = new CircularQueue<AVPacket *>(); | 17 | + m_read_thread = 0; |
18 | + m_decode_thread = 0; | ||
19 | + m_cached_mem = nullptr; | ||
20 | + | ||
21 | + fmt_ctx = nullptr; | ||
22 | + m_bRunning = false; | ||
23 | + | ||
24 | + stream = nullptr; | ||
25 | + video_index = -1; | ||
26 | + pix_fmt = AV_PIX_FMT_NONE; | ||
27 | + m_dec_name = ""; | ||
28 | + | ||
29 | + m_bPause = false; | ||
30 | + m_bReal = true; | ||
31 | + | ||
32 | + m_bFinished = false; | ||
33 | + m_dec_keyframe = false; | ||
34 | + m_fps = 0.0; | ||
35 | + | ||
36 | + m_bSnapShoting = false; | ||
12 | } | 37 | } |
13 | 38 | ||
14 | DvppDecoder::~DvppDecoder(){ | 39 | DvppDecoder::~DvppDecoder(){ |
15 | - delete m_pktQueueptr; | ||
16 | - m_pktQueueptr = nullptr; | ||
17 | } | 40 | } |
18 | 41 | ||
19 | bool DvppDecoder::init(FFDecConfig cfg){ | 42 | bool DvppDecoder::init(FFDecConfig cfg){ |
20 | 43 | ||
21 | m_dec_name = cfg.dec_name; | 44 | m_dec_name = cfg.dec_name; |
22 | - | ||
23 | - ReceiverConfig receiver_config; | ||
24 | - receiver_config.uri = cfg.uri.c_str(); | ||
25 | - receiver_config.dec_name = cfg.dec_name; | ||
26 | - receiver_config.force_tcp = cfg.force_tcp; | ||
27 | - receiver_config.pktQueueptr = m_pktQueueptr; | ||
28 | - receiver_config.receiver_finished_cbk = receiver_finish_cbk; | ||
29 | - AVCodecContext* avctx = m_receiver.init_FFmpeg(receiver_config); | 45 | + |
46 | + AVCodecContext* avctx = init_FFmpeg(cfg); | ||
30 | if(avctx == nullptr){ | 47 | if(avctx == nullptr){ |
31 | return false; | 48 | return false; |
32 | } | 49 | } |
33 | - m_receiver.setFinishCbkArg(this); | ||
34 | 50 | ||
35 | - DvppDecConfig dec_cfg; | ||
36 | - if(avctx->codec_id == AV_CODEC_ID_H264){ | ||
37 | - dec_cfg.codec_id = 0; | ||
38 | - }else if(avctx->codec_id == AV_CODEC_ID_HEVC){ | ||
39 | - dec_cfg.codec_id = 1; | ||
40 | - }else { | ||
41 | - return false; | ||
42 | - } | ||
43 | - dec_cfg.dec_name = cfg.dec_name; | ||
44 | - dec_cfg.post_decoded_cbk = cfg.post_decoded_cbk; | ||
45 | - dec_cfg.dev_id = cfg.gpuid; | ||
46 | - dec_cfg.force_tcp = cfg.force_tcp; | ||
47 | - dec_cfg.skip_frame = cfg.skip_frame; | ||
48 | - dec_cfg.profile = avctx->profile; | ||
49 | - dec_cfg.pktQueueptr = m_pktQueueptr; | ||
50 | - dec_cfg.width = avctx->width; | ||
51 | - dec_cfg.height = avctx->height; | ||
52 | - bool bRet = m_decoder.init_vdpp(dec_cfg); | 51 | + bool bRet = init_vdpp(cfg, avctx); |
53 | if(!bRet){ | 52 | if(!bRet){ |
54 | return false; | 53 | return false; |
55 | } | 54 | } |
@@ -63,22 +62,189 @@ bool DvppDecoder::init(FFDecConfig cfg){ | @@ -63,22 +62,189 @@ bool DvppDecoder::init(FFDecConfig cfg){ | ||
63 | return true; | 62 | return true; |
64 | } | 63 | } |
65 | 64 | ||
65 | +AVCodecContext* DvppDecoder::init_FFmpeg(FFDecConfig config){ | ||
66 | + | ||
67 | +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) | ||
68 | + av_register_all(); | ||
69 | +#endif | ||
70 | +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100) | ||
71 | + avcodec_register_all(); | ||
72 | +#endif | ||
73 | + | ||
74 | + avformat_network_init(); | ||
75 | + | ||
76 | + const char* uri = config.uri.c_str(); | ||
77 | + fstream infile(uri); | ||
78 | + if (infile.is_open()){ | ||
79 | + m_bReal = false; | ||
80 | + infile.close(); | ||
81 | + } else { | ||
82 | + m_bReal = true; | ||
83 | + } | ||
84 | + | ||
85 | + // 打开输入视频文件 | ||
86 | + AVDictionary *options = nullptr; | ||
87 | + av_dict_set( &options, "bufsize", "655360", 0 ); | ||
88 | + av_dict_set( &options, "rtsp_transport", config.force_tcp ? "tcp" : "udp", 0 ); | ||
89 | + av_dict_set( &options, "stimeout", "30000000", 0 ); // 单位为 百万分之一秒 | ||
90 | + | ||
91 | + const char* input_file = uri; | ||
92 | + | ||
93 | + do{ | ||
94 | + fmt_ctx = avformat_alloc_context(); | ||
95 | + if (avformat_open_input(&fmt_ctx, input_file, nullptr, &options) != 0) { | ||
96 | + LOG_ERROR("[{}]- Cannot open input file: {}", m_dec_name, input_file); | ||
97 | + break; | ||
98 | + } | ||
99 | + av_dump_format(fmt_ctx, 0, input_file, 0); | ||
100 | + | ||
101 | + // 查找流信息 | ||
102 | + if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) { | ||
103 | + LOG_ERROR("[{}]- Cannot find input stream information!", m_dec_name); | ||
104 | + break; | ||
105 | + } | ||
106 | + | ||
107 | + // 查找视频流信息 | ||
108 | + AVCodec *decoder = nullptr; | ||
109 | + video_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); | ||
110 | + if (video_index < 0) { | ||
111 | + LOG_ERROR("[{}]- Cannot find a video stream in the input file!", m_dec_name); | ||
112 | + break; | ||
113 | + } | ||
114 | + AVCodec *vcodec = avcodec_find_decoder(decoder->id); | ||
115 | + | ||
116 | + avctx = avcodec_alloc_context3(vcodec); | ||
117 | + if(avctx == nullptr){ | ||
118 | + LOG_ERROR("[{}]- alloc AVCodecContext failed!", m_dec_name); | ||
119 | + break; | ||
120 | + } | ||
121 | + | ||
122 | + // 得到视频流对象 | ||
123 | + AVStream* stream = fmt_ctx->streams[video_index]; | ||
124 | + AVCodecParameters *codecpar = stream->codecpar; | ||
125 | + if (avcodec_parameters_to_context(avctx, codecpar) < 0) | ||
126 | + break; | ||
127 | + | ||
128 | + const AVBitStreamFilter * filter = nullptr; | ||
129 | + if(codecpar->codec_id == AV_CODEC_ID_H264){ | ||
130 | + filter = av_bsf_get_by_name("h264_mp4toannexb"); | ||
131 | + }else if(codecpar->codec_id == AV_CODEC_ID_HEVC){ | ||
132 | + filter = av_bsf_get_by_name("hevc_mp4toannexb"); | ||
133 | + }else { | ||
134 | + LOG_ERROR("[{}]- codec_id is not supported!", m_dec_name); | ||
135 | + break; | ||
136 | + } | ||
137 | + | ||
138 | + int ret = av_bsf_alloc(filter, &h264bsfc); | ||
139 | + if (ret < 0){ | ||
140 | + break; | ||
141 | + } | ||
142 | + | ||
143 | + avcodec_parameters_copy(h264bsfc->par_in, codecpar); | ||
144 | + av_bsf_init(h264bsfc); | ||
145 | + | ||
146 | + frame_width = codecpar->width; | ||
147 | + frame_height = codecpar->height; | ||
148 | + pix_fmt = (AVPixelFormat)codecpar->format; | ||
149 | + m_fps = av_q2d(stream ->avg_frame_rate); | ||
150 | + | ||
151 | + LOG_INFO("[{}]- init ffmpeg success! input:{} frame_width:{} frame_height:{} fps:{} ", m_dec_name, input_file, frame_width, frame_height, m_fps); | ||
152 | + | ||
153 | + return avctx; | ||
154 | + }while(0); | ||
155 | + | ||
156 | + release_ffmpeg(); | ||
157 | + | ||
158 | + LOG_ERROR("[{}]- init ffmpeg failed ! input:{} ", m_dec_name, input_file); | ||
159 | + | ||
160 | + return nullptr; | ||
161 | +} | ||
162 | + | ||
163 | + bool DvppDecoder::init_vdpp(FFDecConfig cfg, AVCodecContext* avctx) { | ||
164 | + | ||
165 | + LOG_INFO("[{}]- Init device start...", m_dec_name); | ||
166 | + | ||
167 | + m_dvpp_deviceId = atoi(cfg.gpuid.c_str()); | ||
168 | + | ||
169 | + if(avctx->codec_id == AV_CODEC_ID_H264){ | ||
170 | + // 66:Baseline,77:Main,>=100:High | ||
171 | + if(avctx->profile == 77){ | ||
172 | + enType = H264_MAIN_LEVEL; | ||
173 | + }else if(avctx->profile < 77){ | ||
174 | + enType = H264_BASELINE_LEVEL; | ||
175 | + }else{ | ||
176 | + enType = H264_HIGH_LEVEL; | ||
177 | + } | ||
178 | + }else if(avctx->codec_id == AV_CODEC_ID_HEVC){ | ||
179 | + // h265只有main | ||
180 | + enType = H265_MAIN_LEVEL; | ||
181 | + }else { | ||
182 | + LOG_ERROR("[{}]- codec_id is not supported!", m_dec_name); | ||
183 | + return false; | ||
184 | + } | ||
185 | + | ||
186 | + post_decoded_cbk = cfg.post_decoded_cbk; | ||
187 | + | ||
188 | + do{ | ||
189 | + aclError ret = aclrtSetDevice(m_dvpp_deviceId); | ||
190 | + if(ret != ACL_ERROR_NONE){ | ||
191 | + LOG_ERROR("[{}]-aclrtSetDevice failed !", m_dec_name); | ||
192 | + return false; | ||
193 | + } | ||
194 | + | ||
195 | + ret = aclrtCreateContext(&m_context, m_dvpp_deviceId); | ||
196 | + if (ret != ACL_ERROR_NONE) { | ||
197 | + LOG_ERROR("[{}]-aclrtCreateContext failed !", m_dec_name); | ||
198 | + return false; | ||
199 | + } | ||
200 | + | ||
201 | + // DvppSourceManager 创建时包含 aclInit,析构时包含 aclFinalize | ||
202 | + DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); | ||
203 | + m_dvpp_channel = pSrcMgr->getChannel(m_dvpp_deviceId); | ||
204 | + if(m_dvpp_channel < 0){ | ||
205 | + LOG_ERROR("[{}]-该设备channel已经用完了!", m_dec_name); | ||
206 | + return false; | ||
207 | + } | ||
208 | + m_vdec_out_size = avctx->width * avctx->height * 3 / 2; | ||
209 | + | ||
210 | + LOG_INFO("[{}]- init vdpp success! device:{} channel:{}", m_dec_name, m_dvpp_deviceId, m_dvpp_channel); | ||
211 | + return true; | ||
212 | + }while(0); | ||
213 | + | ||
214 | + release_dvpp(); | ||
215 | + | ||
216 | + return false; | ||
217 | +} | ||
218 | + | ||
66 | bool DvppDecoder::isSurport(FFDecConfig& cfg){ | 219 | bool DvppDecoder::isSurport(FFDecConfig& cfg){ |
67 | return true; | 220 | return true; |
68 | } | 221 | } |
69 | 222 | ||
70 | bool DvppDecoder::start(){ | 223 | bool DvppDecoder::start(){ |
71 | - m_receiver.start(); | ||
72 | - m_decoder.start(); | ||
73 | - return true; | 224 | + m_bRunning = true; |
225 | + | ||
226 | + pthread_create(&m_read_thread,0, | ||
227 | + [](void* arg) | ||
228 | + { | ||
229 | + DvppDecoder* a=(DvppDecoder*)arg; | ||
230 | + a->read_thread(); | ||
231 | + return (void*)0; | ||
232 | + } | ||
233 | + ,this); | ||
234 | + | ||
235 | + return true; | ||
74 | } | 236 | } |
75 | 237 | ||
76 | void DvppDecoder::close(){ | 238 | void DvppDecoder::close(){ |
77 | - m_receiver.close(); | 239 | + m_bRunning=false; |
240 | + | ||
241 | + if(m_read_thread != 0){ | ||
242 | + pthread_join(m_read_thread,0); | ||
243 | + } | ||
78 | } | 244 | } |
79 | 245 | ||
80 | void DvppDecoder::setPostDecArg(const void* postDecArg){ | 246 | void DvppDecoder::setPostDecArg(const void* postDecArg){ |
81 | - m_decoder.setPostDecArg(postDecArg); | 247 | + m_postDecArg = postDecArg; |
82 | } | 248 | } |
83 | 249 | ||
84 | void DvppDecoder::setFinishedDecArg(const void* finishedDecArg){ | 250 | void DvppDecoder::setFinishedDecArg(const void* finishedDecArg){ |
@@ -86,19 +252,19 @@ void DvppDecoder::setFinishedDecArg(const void* finishedDecArg){ | @@ -86,19 +252,19 @@ void DvppDecoder::setFinishedDecArg(const void* finishedDecArg){ | ||
86 | } | 252 | } |
87 | 253 | ||
88 | void DvppDecoder::pause(){ | 254 | void DvppDecoder::pause(){ |
89 | - m_receiver.pause(); | 255 | + m_bPause = true; |
90 | } | 256 | } |
91 | 257 | ||
92 | void DvppDecoder::resume(){ | 258 | void DvppDecoder::resume(){ |
93 | - m_receiver.resume(); | 259 | + m_bPause = false; |
94 | } | 260 | } |
95 | 261 | ||
96 | void DvppDecoder::setDecKeyframe(bool bKeyframe){ | 262 | void DvppDecoder::setDecKeyframe(bool bKeyframe){ |
97 | - m_receiver.setDecKeyframe(bKeyframe); | 263 | + m_dec_keyframe = bKeyframe; |
98 | } | 264 | } |
99 | 265 | ||
100 | bool DvppDecoder::isRunning(){ | 266 | bool DvppDecoder::isRunning(){ |
101 | - return m_receiver.isRunning(); | 267 | + return m_bRunning; |
102 | } | 268 | } |
103 | 269 | ||
104 | bool DvppDecoder::isFinished(){ | 270 | bool DvppDecoder::isFinished(){ |
@@ -106,32 +272,597 @@ bool DvppDecoder::isFinished(){ | @@ -106,32 +272,597 @@ bool DvppDecoder::isFinished(){ | ||
106 | } | 272 | } |
107 | 273 | ||
108 | bool DvppDecoder::isPausing(){ | 274 | bool DvppDecoder::isPausing(){ |
109 | - return m_receiver.isPausing(); | 275 | + return m_bPause; |
110 | } | 276 | } |
111 | 277 | ||
112 | bool DvppDecoder::getResolution(int &width, int &height){ | 278 | bool DvppDecoder::getResolution(int &width, int &height){ |
113 | - return m_receiver.getResolution(width, height); | 279 | + width = frame_width; |
280 | + height = frame_height; | ||
281 | + return true; | ||
114 | } | 282 | } |
115 | 283 | ||
116 | float DvppDecoder::fps(){ | 284 | float DvppDecoder::fps(){ |
117 | - return m_receiver.fps(); | 285 | + return m_fps; |
118 | } | 286 | } |
119 | 287 | ||
120 | DeviceMemory* DvppDecoder::snapshot(){ | 288 | DeviceMemory* DvppDecoder::snapshot(){ |
121 | // 注意内部有锁 | 289 | // 注意内部有锁 |
122 | - return m_decoder.snapshot(); | 290 | + // 开始抓拍 |
291 | + m_bSnapShoting = true; | ||
292 | + | ||
293 | + std::unique_lock<std::mutex> locker(m_cached_mutex); | ||
294 | + while (m_cached_mem == nullptr) | ||
295 | + m_cached_cond.wait_for(locker, std::chrono::milliseconds(400)); // Unlock mutex and wait to be notified | ||
296 | + locker.unlock(); | ||
297 | + | ||
298 | + DeviceMemory* mem = m_cached_mem; | ||
299 | + m_cached_mem = nullptr; | ||
300 | + | ||
301 | + return mem; | ||
123 | } | 302 | } |
124 | 303 | ||
125 | int DvppDecoder::getCachedQueueLength(){ | 304 | int DvppDecoder::getCachedQueueLength(){ |
126 | return 0; | 305 | return 0; |
127 | } | 306 | } |
128 | 307 | ||
129 | -void DvppDecoder::taskFinishing(){ | ||
130 | - // receiver 中读取线程结束时执行 | ||
131 | - m_decoder.close(); | 308 | +void DvppDecoder::release_ffmpeg() { |
309 | + m_dec_keyframe = false; | ||
310 | + if(h264bsfc){ | ||
311 | + av_bsf_free(&h264bsfc); | ||
312 | + h264bsfc = nullptr; | ||
313 | + } | ||
314 | + if (fmt_ctx){ | ||
315 | + avformat_close_input(&fmt_ctx); | ||
316 | + fmt_ctx = nullptr; | ||
317 | + } | ||
318 | + if(avctx){ | ||
319 | + avcodec_free_context(&avctx); | ||
320 | + avctx = nullptr; | ||
321 | + } | ||
322 | +} | ||
323 | + | ||
324 | +void DvppDecoder::read_thread() { | ||
325 | + | ||
326 | + int frame_count = 0; | ||
327 | + int ret = -1; | ||
328 | + | ||
329 | + pthread_create(&m_decode_thread,0, | ||
330 | + [](void* arg) | ||
331 | + { | ||
332 | + DvppDecoder* a=(DvppDecoder*)arg; | ||
333 | + a->decode_thread(); | ||
334 | + return (void*)0; | ||
335 | + } | ||
336 | + ,this); | ||
337 | + | ||
338 | + AVPacket* pkt = nullptr; | ||
339 | + while (m_bRunning){ | ||
340 | + | ||
341 | + if (!m_bReal){ | ||
342 | + if (m_bPause){ | ||
343 | + std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
344 | + continue; | ||
345 | + } | ||
346 | + } | ||
347 | + | ||
348 | + m_pktQueue_mutex.lock(); | ||
349 | + if(m_pktQueue.size() > 10){ | ||
350 | + m_pktQueue_mutex.unlock(); | ||
351 | + std::this_thread::sleep_for(std::chrono::milliseconds(5)); | ||
352 | + continue; | ||
353 | + } | ||
354 | + m_pktQueue_mutex.unlock(); | ||
355 | + | ||
356 | + pkt = av_packet_alloc(); | ||
357 | + av_init_packet( pkt ); | ||
358 | + | ||
359 | + int result = av_read_frame(fmt_ctx, pkt); | ||
360 | + if (result == AVERROR_EOF || result < 0){ | ||
361 | + av_packet_free(&pkt); | ||
362 | + pkt = nullptr; | ||
363 | + LOG_ERROR("[{}]- Failed to read frame!", m_dec_name); | ||
364 | + break; | ||
365 | + } | ||
366 | + | ||
367 | + if (m_dec_keyframe && !(pkt->flags & AV_PKT_FLAG_KEY)) { | ||
368 | + av_packet_free(&pkt); | ||
369 | + pkt = nullptr; | ||
370 | + continue; | ||
371 | + } | ||
372 | + | ||
373 | + if (video_index == pkt->stream_index){ | ||
374 | + | ||
375 | + ret = av_bsf_send_packet(h264bsfc, pkt); | ||
376 | + if(ret < 0) { | ||
377 | + LOG_ERROR("[{}]- av_bsf_send_packet error!", m_dec_name); | ||
378 | + av_packet_free(&pkt); | ||
379 | + pkt = nullptr; | ||
380 | + continue; | ||
381 | + } | ||
382 | + | ||
383 | + bool bPushed = false; | ||
384 | + while ((ret = av_bsf_receive_packet(h264bsfc, pkt)) == 0) { | ||
385 | + if(pkt->size > g_pkt_size){ | ||
386 | + LOG_ERROR("[{}]- pkt size 大于最大预设值!", m_dec_name); | ||
387 | + break; | ||
388 | + } | ||
389 | + | ||
390 | + if(!m_bRunning){ | ||
391 | + break; | ||
392 | + } | ||
393 | + | ||
394 | + m_pktQueue_mutex.lock(); | ||
395 | + m_pktQueue.push(pkt); | ||
396 | + m_pktQueue_mutex.unlock(); | ||
397 | + | ||
398 | + bPushed = true; | ||
399 | + frame_count++; | ||
400 | + } | ||
401 | + | ||
402 | + if(!bPushed){ | ||
403 | + av_packet_free(&pkt); | ||
404 | + pkt = nullptr; | ||
405 | + } | ||
406 | + } else { | ||
407 | + // 音频等其他分量的情形 | ||
408 | + av_packet_free(&pkt); | ||
409 | + pkt = nullptr; | ||
410 | + } | ||
411 | + } | ||
412 | + | ||
413 | + m_bRunning=false; | ||
414 | + | ||
415 | + if(m_decode_thread != 0){ | ||
416 | + pthread_join(m_decode_thread,0); | ||
417 | + } | ||
418 | + | ||
419 | + m_pktQueue_mutex.lock(); | ||
420 | + while(m_pktQueue.size() > 0){ | ||
421 | + pkt = m_pktQueue.front(); | ||
422 | + av_packet_free(&pkt); | ||
423 | + pkt = nullptr; | ||
424 | + m_pktQueue.pop(); | ||
425 | + } | ||
426 | + m_pktQueue_mutex.unlock(); | ||
427 | + | ||
132 | decode_finished_cbk(m_finishedDecArg); | 428 | decode_finished_cbk(m_finishedDecArg); |
133 | 429 | ||
134 | - m_bFinished = true; | 430 | + LOG_INFO("[{}]- read thread exit.", m_dec_name); |
431 | + m_bFinished = true; | ||
432 | +} | ||
433 | + | ||
434 | +static void *ReportThd(void *arg) | ||
435 | +{ | ||
436 | + DvppDecoder *self = (DvppDecoder *)arg; | ||
437 | + if(nullptr != self){ | ||
438 | + self->doProcessReport(); | ||
439 | + } | ||
440 | + return (void *)0; | ||
441 | +} | ||
442 | + | ||
443 | +void DvppDecoder::doProcessReport(){ | ||
444 | + | ||
445 | + aclError ret = aclrtSetDevice(m_dvpp_deviceId); | ||
446 | + if(ret != ACL_ERROR_NONE){ | ||
447 | + // cout << "aclrtSetDevice failed" << endl; | ||
448 | + LOG_ERROR("aclrtSetDevice failed !"); | ||
449 | + return ; | ||
450 | + } | ||
451 | + | ||
452 | + aclrtContext ctx; | ||
453 | + ret = aclrtCreateContext(&ctx, m_dvpp_deviceId); | ||
454 | + if (ret != ACL_ERROR_NONE) { | ||
455 | + // cout << "aclrtCreateContext failed " << endl; | ||
456 | + LOG_ERROR("aclrtCreateContext failed !"); | ||
457 | + return ; | ||
458 | + } | ||
459 | + | ||
460 | + CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(ctx), "aclrtSetCurrentContext failed"); | ||
461 | + // 阻塞等待vdec线程开始 | ||
462 | + | ||
463 | + while (!m_bExitReportThd) { | ||
464 | + aclrtProcessReport(1000); | ||
465 | + } | ||
466 | + | ||
467 | + ret = aclrtDestroyContext(ctx); | ||
468 | + if(ret != ACL_ERROR_NONE){ | ||
469 | + LOG_ERROR("aclrtDestroyContext failed !"); | ||
470 | + } | ||
471 | + LOG_INFO("doProcessReport exit."); | ||
472 | +} | ||
473 | + | ||
474 | +static void VdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *pUserData) | ||
475 | +{ | ||
476 | + Vdec_CallBack_UserData *userData = (Vdec_CallBack_UserData *) pUserData; | ||
477 | + if(nullptr != userData){ | ||
478 | + DvppDecoder* self = userData->self; | ||
479 | + if(self != nullptr){ | ||
480 | + | ||
481 | + self->doVdppVdecCallBack(input, output); | ||
482 | + } | ||
483 | + delete userData; | ||
484 | + userData = nullptr; | ||
485 | + } | ||
486 | +} | ||
487 | + | ||
488 | +void DvppDecoder::doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output){ | ||
489 | + | ||
490 | + m_vdecQueue_mutex.lock(); | ||
491 | + if(m_vdecQueue.size() > 0){ | ||
492 | + void* inputData = m_vdecQueue.front(); | ||
493 | + acldvppFree(inputData); | ||
494 | + inputData = nullptr; | ||
495 | + m_vdecQueue.pop(); | ||
496 | + } | ||
497 | + m_vdecQueue_mutex.unlock(); | ||
498 | + | ||
499 | + | ||
500 | + CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); | ||
501 | + | ||
502 | + void *outputDataDev = acldvppGetPicDescData(output); | ||
503 | + uint32_t outputSize = acldvppGetPicDescSize(output); | ||
504 | + uint32_t width = acldvppGetPicDescWidth(output); | ||
505 | + uint32_t width_stride = acldvppGetPicDescWidthStride(output); | ||
506 | + uint32_t height = acldvppGetPicDescHeight(output); | ||
507 | + uint32_t height_stride = acldvppGetPicDescHeightStride(output); | ||
508 | + | ||
509 | + do{ | ||
510 | + int ret = acldvppGetPicDescRetCode(output); | ||
511 | + if(ret != ACL_ERROR_NONE){ | ||
512 | + LOG_ERROR("[{}]- decode result error, retCode:{} ", m_dec_name, ret); | ||
513 | + acldvppFree(outputDataDev); | ||
514 | + outputDataDev = nullptr; | ||
515 | + break; | ||
516 | + } | ||
517 | + | ||
518 | + if(width > 0 && height > 0 && outputSize > 0){ | ||
519 | + DvppDataMemory* mem = new DvppDataMemory(width, width_stride, height, height_stride, outputSize, m_dec_name, to_string(m_dvpp_deviceId), false, (unsigned char *)outputDataDev); | ||
520 | + if(mem){ | ||
521 | + post_decoded_cbk(m_postDecArg, mem); | ||
522 | + | ||
523 | + if(m_bSnapShoting){ | ||
524 | + // 缓存snapshot | ||
525 | + std::unique_lock<std::mutex> locker(m_cached_mutex); | ||
526 | + | ||
527 | + m_cached_mem = new DvppDataMemory(-1, width, width_stride, height, height_stride, outputSize, m_dec_name, to_string(m_dvpp_deviceId), false); | ||
528 | + if(m_cached_mem != nullptr){ | ||
529 | + aclrtMemcpy(m_cached_mem->getMem(), outputSize, (unsigned char *)outputDataDev, outputSize, ACL_MEMCPY_DEVICE_TO_DEVICE); | ||
530 | + } | ||
531 | + | ||
532 | + locker.unlock(); | ||
533 | + m_cached_cond.notify_one(); | ||
534 | + m_bSnapShoting = false; | ||
535 | + } | ||
536 | + } else { | ||
537 | + LOG_ERROR("[{}]- DvppDataMemory 创建失败! ", m_dec_name, ret); | ||
538 | + acldvppFree(outputDataDev); | ||
539 | + outputDataDev = nullptr; | ||
540 | + } | ||
541 | + | ||
542 | + } else { | ||
543 | + LOG_WARN("[{}]- decode result error, width:{} width_stride:{} height:{} height_stride:{} size:{}", m_dec_name, width, width_stride, height, height_stride, outputSize); | ||
544 | + acldvppFree(outputDataDev); | ||
545 | + outputDataDev = nullptr; | ||
546 | + } | ||
547 | + | ||
548 | + // DvppDataMemory* rgbMem = picConverter.convert2bgr(output, width, height, false); | ||
549 | + // if(rgbMem != nullptr){ | ||
550 | + // #ifdef TEST_DECODER | ||
551 | + // // D2H | ||
552 | + // if(vdecHostAddr == nullptr){ | ||
553 | + // CHECK_NOT_RETURN(aclrtMallocHost(&vdecHostAddr, width * height * 3), "aclrtMallocHost failed"); | ||
554 | + // } | ||
555 | + // uint32_t data_size = rgbMem->getSize(); | ||
556 | + // CHECK_AND_RETURN_NOVALUE(aclrtMemcpy(vdecHostAddr, data_size, rgbMem->getMem(), data_size, ACL_MEMCPY_DEVICE_TO_HOST), "D2H aclrtMemcpy failed"); | ||
557 | + | ||
558 | + // // 保存vdec结果 | ||
559 | + // if(count_frame > 45 && count_frame < 50) | ||
560 | + // { | ||
561 | + // string file_name = "./yuv_pic/vdec_out_"+ m_dec_name +".rgb" ; | ||
562 | + // FILE *outputFile = fopen(file_name.c_str(), "a"); | ||
563 | + // if(outputFile){ | ||
564 | + // fwrite(vdecHostAddr, data_size, sizeof(char), outputFile); | ||
565 | + // fclose(outputFile); | ||
566 | + // } | ||
567 | + // } | ||
568 | + // else if(count_frame > 50 && vdecHostAddr != nullptr){ | ||
569 | + // CHECK_NOT_RETURN(aclrtFreeHost(vdecHostAddr), "aclrtFreeHost failed"); | ||
570 | + // vdecHostAddr = nullptr; | ||
571 | + // } | ||
572 | + // count_frame++; | ||
573 | + // #endif | ||
574 | + // post_decoded_cbk(m_postDecArg, rgbMem); | ||
575 | + // }else{ | ||
576 | + // LOG_ERROR("[{}]- convert2bgr failed !", m_dec_name); | ||
577 | + // } | ||
578 | + }while(0); | ||
579 | + | ||
580 | + CHECK_AND_RETURN_NOVALUE(acldvppDestroyStreamDesc(input), "acldvppDestroyStreamDesc failed"); | ||
581 | + CHECK_AND_RETURN_NOVALUE(acldvppDestroyPicDesc(output), "acldvppDestroyPicDesc failed"); | ||
582 | +} | ||
583 | + | ||
584 | +void DvppDecoder::decode_thread(){ | ||
585 | + | ||
586 | + long startTime = UtilTools::get_cur_time_ms(); | ||
587 | + | ||
588 | + int ret = -1; | ||
589 | + | ||
590 | + m_bExitReportThd = false; | ||
591 | + pthread_t report_thread; | ||
592 | + ret = pthread_create(&report_thread, nullptr, ReportThd, (void *)this); | ||
593 | + if(ret != 0){ | ||
594 | + LOG_ERROR("[{}]- pthread_create failed", m_dec_name); | ||
595 | + return; | ||
596 | + } | ||
597 | + | ||
598 | + aclrtSetDevice(m_dvpp_deviceId); | ||
599 | + aclrtContext ctx; | ||
600 | + ret = aclrtCreateContext(&ctx, m_dvpp_deviceId); | ||
601 | + if (ret != ACL_ERROR_NONE) { | ||
602 | + // cout << "aclrtCreateContext failed " << endl; | ||
603 | + LOG_ERROR("aclrtCreateContext failed !"); | ||
604 | + return ; | ||
605 | + } | ||
606 | + | ||
607 | + // 创建aclvdecChannelDesc类型的数据 | ||
608 | + aclvdecChannelDesc *vdecChannelDesc = aclvdecCreateChannelDesc(); | ||
609 | + if (vdecChannelDesc == nullptr) { | ||
610 | + LOG_ERROR("[{}]- aclvdecCreateChannelDesc failed", m_dec_name); | ||
611 | + return; | ||
612 | + } | ||
613 | + do{ | ||
614 | + // 创建 channel dec结构体 | ||
615 | + // 通道ID在dvpp层面为0~31 | ||
616 | + CHECK_AND_BREAK(aclvdecSetChannelDescChannelId(vdecChannelDesc, m_dvpp_channel), "aclvdecSetChannelDescChannelId failed"); | ||
617 | + CHECK_AND_BREAK(aclvdecSetChannelDescThreadId(vdecChannelDesc, report_thread), "aclvdecSetChannelDescThreadId failed"); | ||
618 | + CHECK_AND_BREAK(aclvdecSetChannelDescCallback(vdecChannelDesc, VdecCallback), "aclvdecSetChannelDescCallback failed"); | ||
619 | + CHECK_AND_BREAK(aclvdecSetChannelDescEnType(vdecChannelDesc, enType), "aclvdecSetChannelDescEnType failed"); | ||
620 | + CHECK_AND_BREAK(aclvdecSetChannelDescOutPicFormat(vdecChannelDesc, PIXEL_FORMAT_YUV_SEMIPLANAR_420), "aclvdecSetChannelDescOutPicFormat failed"); | ||
621 | + CHECK_AND_BREAK(aclvdecCreateChannel(vdecChannelDesc), "aclvdecCreateChannel failed"); | ||
622 | + | ||
623 | + uint64_t frame_count = 0; | ||
624 | + bool bBreak = false; | ||
625 | + while (m_bRunning) | ||
626 | + { | ||
627 | + if (m_bPause){ | ||
628 | + std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
629 | + continue; | ||
630 | + } | ||
631 | + int ret = sentFrame(vdecChannelDesc, frame_count); | ||
632 | + if(ret == 2){ | ||
633 | + bBreak = true; | ||
634 | + break; | ||
635 | + }else if(ret == 1){ | ||
636 | + continue; | ||
637 | + } | ||
638 | + | ||
639 | + frame_count++; | ||
640 | + } | ||
641 | + | ||
642 | + // 尽量保证数据全部解码完成 | ||
643 | + int sum = 0; | ||
644 | + if(!bBreak){ | ||
645 | + aclrtSetDevice(m_dvpp_deviceId); | ||
646 | + aclrtSetCurrentContext(ctx); | ||
647 | + while(m_pktQueue.size() > 0){ | ||
648 | + int ret = sentFrame(vdecChannelDesc, frame_count); | ||
649 | + if(ret == 2){ | ||
650 | + break; | ||
651 | + } | ||
652 | + std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
653 | + sum++; | ||
654 | + if(sum > 40){ | ||
655 | + // 避免卡死 | ||
656 | + break; | ||
657 | + } | ||
658 | + } | ||
659 | + } | ||
660 | + | ||
661 | + sendVdecEos(vdecChannelDesc); | ||
662 | + | ||
663 | + CHECK_NOT_RETURN(aclvdecDestroyChannel(vdecChannelDesc), "aclvdecDestroyChannel failed"); | ||
664 | + }while(0); | ||
665 | + | ||
666 | + CHECK_NOT_RETURN(aclvdecDestroyChannelDesc(vdecChannelDesc), "aclvdecDestroyChannelDesc failed"); | ||
667 | + | ||
668 | + // report_thread 需后于destroy退出 | ||
669 | + m_bRunning = false; | ||
670 | + m_bExitReportThd = true; | ||
671 | + CHECK_NOT_RETURN(pthread_join(report_thread, nullptr), "pthread_join failed"); | ||
672 | + | ||
673 | + // 最后清理一遍未解码的数据 | ||
674 | + m_vdecQueue_mutex.lock(); | ||
675 | + if(m_vdecQueue.size() > 0){ | ||
676 | + void* inputData = m_vdecQueue.front(); | ||
677 | + acldvppFree(inputData); | ||
678 | + inputData = nullptr; | ||
679 | + m_vdecQueue.pop(); | ||
680 | + } | ||
681 | + m_vdecQueue_mutex.unlock(); | ||
682 | + | ||
683 | + release_dvpp(); | ||
684 | + | ||
685 | + ret = aclrtDestroyContext(ctx); | ||
686 | + if(ret != ACL_ERROR_NONE){ | ||
687 | + LOG_ERROR("aclrtDestroyContext failed !"); | ||
688 | + } | ||
689 | + | ||
690 | + LOG_INFO("[{}]- decode thread exit.", m_dec_name); | ||
691 | +} | ||
692 | + | ||
693 | +#include <fstream> | ||
694 | +#include <iostream> | ||
695 | +#include <cstring> | ||
135 | 696 | ||
136 | - LOG_INFO("[{}]- task finished.", m_dec_name); | 697 | +static int nRecoder = 0; |
698 | + | ||
699 | + | ||
700 | + | ||
701 | +int DvppDecoder::sentFrame(aclvdecChannelDesc *vdecChannelDesc, uint64_t frame_count){ | ||
702 | + | ||
703 | + // 此处需要判断 m_vdecQueue 队列长度,避免占用过多显存 | ||
704 | + m_vdecQueue_mutex.lock(); | ||
705 | + if(m_vdecQueue.size() > 20){ | ||
706 | + m_vdecQueue_mutex.unlock(); | ||
707 | + std::this_thread::sleep_for(std::chrono::milliseconds(2)); | ||
708 | + return 1; | ||
709 | + } | ||
710 | + m_vdecQueue_mutex.unlock(); | ||
711 | + | ||
712 | + AVPacket * pkt = nullptr; | ||
713 | + m_pktQueue_mutex.lock(); | ||
714 | + if(m_pktQueue.size() <= 0){ | ||
715 | + m_pktQueue_mutex.unlock(); | ||
716 | + std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
717 | + return 1; | ||
718 | + } | ||
719 | + pkt = m_pktQueue.front(); | ||
720 | + m_pktQueue.pop(); | ||
721 | + m_pktQueue_mutex.unlock(); | ||
722 | + | ||
723 | + // 解码 | ||
724 | + void *vdecInputbuf = nullptr; | ||
725 | + int ret = acldvppMalloc((void **)&vdecInputbuf, g_pkt_size); | ||
726 | + if(ACL_ERROR_NONE != ret){ | ||
727 | + LOG_ERROR("[{}]- acldvppMalloc failed!, ret:{}", m_dec_name, ret); | ||
728 | + av_packet_free(&pkt); | ||
729 | + pkt = nullptr; | ||
730 | + return 2; | ||
731 | + } | ||
732 | + | ||
733 | + // std::ofstream outfile; | ||
734 | + // string file_name = "./pkt/pkt"; | ||
735 | + // file_name = file_name + to_string(nRecoder) + ".bin"; | ||
736 | + // outfile.open(file_name.c_str(), std::ios::binary | std::ios::app); | ||
737 | + // if (!outfile) { | ||
738 | + // std::cerr << "Failed to open file!" << std::endl; | ||
739 | + // return 2; | ||
740 | + // } | ||
741 | + | ||
742 | + // outfile.write((const char*)pkt->data, pkt->size); | ||
743 | + // outfile.close(); | ||
744 | + | ||
745 | + // nRecoder ++ ; | ||
746 | + // if(nRecoder >= 1000){ | ||
747 | + | ||
748 | + // return 2; | ||
749 | + // } | ||
750 | + | ||
751 | + | ||
752 | + | ||
753 | + ret = aclrtMemcpy(vdecInputbuf, pkt->size, pkt->data, pkt->size, ACL_MEMCPY_HOST_TO_DEVICE); | ||
754 | + if(ACL_ERROR_NONE != ret){ | ||
755 | + LOG_ERROR("[{}]- aclrtMemcpy failed", m_dec_name); | ||
756 | + av_packet_free(&pkt); | ||
757 | + pkt = nullptr; | ||
758 | + return 2; | ||
759 | + } | ||
760 | + | ||
761 | + void *vdecOutputBuf = nullptr; | ||
762 | + ret = acldvppMalloc((void **)&vdecOutputBuf, m_vdec_out_size); | ||
763 | + if(ret != ACL_ERROR_NONE){ | ||
764 | + LOG_ERROR("[{}]- acldvppMalloc failed", m_dec_name); | ||
765 | + av_packet_free(&pkt); | ||
766 | + pkt = nullptr; | ||
767 | + return 2; | ||
768 | + } | ||
769 | + | ||
770 | + acldvppStreamDesc *input_stream_desc = nullptr; | ||
771 | + acldvppPicDesc *output_pic_desc = nullptr; | ||
772 | + do{ | ||
773 | + input_stream_desc = acldvppCreateStreamDesc(); | ||
774 | + if (input_stream_desc == nullptr) { | ||
775 | + LOG_ERROR("[{}]- acldvppCreateStreamDesc failed", m_dec_name); | ||
776 | + break; | ||
777 | + } | ||
778 | + output_pic_desc = acldvppCreatePicDesc(); | ||
779 | + if (output_pic_desc == nullptr) { | ||
780 | + LOG_ERROR("[{}]- acldvppCreatePicDesc failed", m_dec_name); | ||
781 | + break; | ||
782 | + } | ||
783 | + CHECK_AND_BREAK(acldvppSetStreamDescData(input_stream_desc, vdecInputbuf), "acldvppSetStreamDescData failed"); | ||
784 | + CHECK_AND_BREAK(acldvppSetStreamDescSize(input_stream_desc, pkt->size), "acldvppSetStreamDescSize failed"); | ||
785 | + CHECK_AND_BREAK(acldvppSetPicDescData(output_pic_desc, vdecOutputBuf), "acldvppSetPicDescData failed"); | ||
786 | + CHECK_AND_BREAK(acldvppSetPicDescSize(output_pic_desc, m_vdec_out_size), "acldvppSetPicDescSize failed"); | ||
787 | + | ||
788 | + Vdec_CallBack_UserData *user_data = NULL; | ||
789 | + user_data = new Vdec_CallBack_UserData; | ||
790 | + user_data->frameId = frame_count; | ||
791 | + // user_data->startTime = startTime; | ||
792 | + user_data->sendTime = UtilTools::get_cur_time_ms(); | ||
793 | + user_data->self = this; | ||
794 | + ret = aclvdecSendFrame(vdecChannelDesc, input_stream_desc, output_pic_desc, nullptr, reinterpret_cast<void *>(user_data)); | ||
795 | + av_packet_free(&pkt); | ||
796 | + pkt = nullptr; | ||
797 | + if(ret != ACL_ERROR_NONE){ | ||
798 | + delete user_data; | ||
799 | + user_data = nullptr; | ||
800 | + LOG_ERROR("[{}]- aclvdecSendFrame failed", m_dec_name); | ||
801 | + break; | ||
802 | + } | ||
803 | + | ||
804 | + m_vdecQueue_mutex.lock(); | ||
805 | + m_vdecQueue.push(vdecInputbuf); | ||
806 | + m_vdecQueue_mutex.unlock(); | ||
807 | + | ||
808 | + return 0; | ||
809 | + }while (0); | ||
810 | + | ||
811 | + if(pkt != nullptr){ | ||
812 | + av_packet_free(&pkt); | ||
813 | + pkt = nullptr; | ||
814 | + } | ||
815 | + | ||
816 | + // 报错情形 | ||
817 | + if(input_stream_desc){ | ||
818 | + CHECK_NOT_RETURN(acldvppDestroyStreamDesc(input_stream_desc), "acldvppDestroyStreamDesc failed"); | ||
819 | + } | ||
820 | + if(output_pic_desc){ | ||
821 | + CHECK_NOT_RETURN(acldvppDestroyPicDesc(output_pic_desc), "acldvppDestroyPicDesc failed"); | ||
822 | + } | ||
823 | + | ||
824 | + if (vdecOutputBuf){ | ||
825 | + acldvppFree(vdecOutputBuf); | ||
826 | + vdecOutputBuf = nullptr; | ||
827 | + } | ||
828 | + | ||
829 | + return 1; | ||
830 | +} | ||
831 | + | ||
832 | +bool DvppDecoder::sendVdecEos(aclvdecChannelDesc *vdecChannelDesc) { | ||
833 | + // create stream desc | ||
834 | + acldvppStreamDesc *streamInputDesc = acldvppCreateStreamDesc(); | ||
835 | + if (streamInputDesc == nullptr) { | ||
836 | + LOG_ERROR("[{}]- fail to create input stream desc", m_dec_name); | ||
837 | + return false; | ||
838 | + } | ||
839 | + aclError ret = acldvppSetStreamDescEos(streamInputDesc, 1); | ||
840 | + if (ret != ACL_SUCCESS) { | ||
841 | + LOG_ERROR("[{}]- fail to set eos for stream desc, errorCode = {}", m_dec_name, static_cast<int32_t>(ret)); | ||
842 | + (void)acldvppDestroyStreamDesc(streamInputDesc); | ||
843 | + return false; | ||
844 | + } | ||
845 | + | ||
846 | + // send vdec eos frame. when all vdec callback are completed, aclvdecSendFrame can be returned. | ||
847 | + LOG_INFO("[{}]- send eos", m_dec_name); | ||
848 | + ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, nullptr, nullptr, nullptr); | ||
849 | + (void)acldvppDestroyStreamDesc(streamInputDesc); | ||
850 | + if (ret != ACL_SUCCESS) { | ||
851 | + LOG_ERROR("[{}]- fail to send eos frame, ret={}", m_dec_name, ret); | ||
852 | + return false; | ||
853 | + } | ||
854 | + | ||
855 | + return true; | ||
856 | +} | ||
857 | + | ||
858 | +void DvppDecoder::release_dvpp(){ | ||
859 | + if(m_context){ | ||
860 | + aclError ret = aclrtDestroyContext(m_context); | ||
861 | + if(ret != ACL_ERROR_NONE){ | ||
862 | + LOG_ERROR("[{}]- aclrtDestroyContext failed !", m_dec_name); | ||
863 | + } | ||
864 | + } | ||
865 | + | ||
866 | + DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); | ||
867 | + pSrcMgr->releaseChannel(m_dvpp_deviceId, m_dvpp_channel); | ||
137 | } | 868 | } |
138 | \ No newline at end of file | 869 | \ No newline at end of file |
src/decoder/dvpp/DvppDecoder.h
1 | #include<string> | 1 | #include<string> |
2 | 2 | ||
3 | #include "depend_headers.h" | 3 | #include "depend_headers.h" |
4 | -#include "CircularQueue.hpp" | ||
5 | -#include "FFReceiver.h" | ||
6 | -#include "DvppDec.h" | 4 | +#include "dvpp_headers.h" |
5 | +#include "DvppDataMemory.hpp" | ||
6 | + | ||
7 | +#include <queue> | ||
8 | +#include <mutex> | ||
9 | +#include <condition_variable> | ||
10 | + | ||
7 | 11 | ||
8 | using namespace std; | 12 | using namespace std; |
9 | 13 | ||
14 | +typedef void(*RECEIVER_FINISHED_CALLBACK)(const void* userPtr); | ||
15 | + | ||
16 | +const int g_pkt_size = 1024 * 1024; // 单个AVPacket大小的最大值 | ||
17 | + | ||
10 | class DvppDecoder{ | 18 | class DvppDecoder{ |
11 | public: | 19 | public: |
12 | DvppDecoder(); | 20 | DvppDecoder(); |
@@ -44,19 +52,72 @@ public: | @@ -44,19 +52,72 @@ public: | ||
44 | int getCachedQueueLength(); | 52 | int getCachedQueueLength(); |
45 | 53 | ||
46 | public: | 54 | public: |
47 | - void taskFinishing(); | 55 | + void doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output); |
56 | + void doProcessReport(); | ||
57 | + | ||
58 | +private: | ||
59 | + AVCodecContext* init_FFmpeg(FFDecConfig config); | ||
60 | + bool init_vdpp(FFDecConfig cfg, AVCodecContext* avctx); | ||
61 | + void release_ffmpeg(); | ||
62 | + void read_thread(); | ||
63 | + | ||
64 | + void decode_thread(); | ||
65 | + int sentFrame(aclvdecChannelDesc *vdecChannelDesc, uint64_t frame_count); | ||
66 | + bool sendVdecEos(aclvdecChannelDesc *vdecChannelDesc); | ||
67 | + void release_dvpp(); | ||
48 | 68 | ||
49 | private: | 69 | private: |
50 | FFDecConfig m_cfg; | 70 | FFDecConfig m_cfg; |
51 | string m_dec_name; | 71 | string m_dec_name; |
52 | 72 | ||
53 | - CircularQueue<AVPacket *> *m_pktQueueptr; | ||
54 | - FFReceiver m_receiver; | ||
55 | - DvppDec m_decoder; | ||
56 | - | ||
57 | const void * m_finishedDecArg; | 73 | const void * m_finishedDecArg; |
58 | DECODE_FINISHED_CALLBACK decode_finished_cbk; | 74 | DECODE_FINISHED_CALLBACK decode_finished_cbk; |
59 | 75 | ||
60 | bool m_bFinished{false}; | 76 | bool m_bFinished{false}; |
77 | + bool m_bRunning{false}; | ||
78 | + bool m_bPause{false}; | ||
79 | + bool m_bExitReportThd{false}; | ||
80 | + | ||
81 | + // 读取数据 | ||
82 | + AVStream* stream{nullptr}; | ||
83 | + int video_index{-1}; | ||
84 | + AVFormatContext *fmt_ctx{nullptr}; | ||
85 | + AVPixelFormat pix_fmt; | ||
86 | + AVCodecContext *avctx{nullptr}; | ||
87 | + AVBSFContext * h264bsfc{nullptr}; | ||
88 | + | ||
89 | + int frame_width{0}; | ||
90 | + int frame_height{0}; | ||
91 | + bool m_bReal; // 是否实时流 | ||
92 | + float m_fps{0.0}; | ||
93 | + | ||
94 | + RECEIVER_FINISHED_CALLBACK receiver_finished_cbk; | ||
95 | + | ||
96 | + pthread_t m_read_thread{0}; | ||
97 | + | ||
98 | + bool m_dec_keyframe; | ||
99 | + | ||
100 | + mutex m_pktQueue_mutex; | ||
101 | + queue<AVPacket*> m_pktQueue; | ||
102 | + | ||
103 | + // 解码 | ||
104 | + int m_dvpp_deviceId {-1}; | ||
105 | + int m_dvpp_channel {-1}; | ||
106 | + aclrtContext m_context{nullptr}; | ||
107 | + acldvppStreamFormat enType; | ||
108 | + | ||
109 | + pthread_t m_decode_thread{0}; | ||
110 | + mutex m_vdecQueue_mutex; | ||
111 | + queue<void*> m_vdecQueue; | ||
112 | + | ||
113 | + const void * m_postDecArg; | ||
114 | + POST_DECODE_CALLBACK post_decoded_cbk; | ||
115 | + | ||
116 | + int m_vdec_out_size {-1}; | ||
61 | 117 | ||
118 | + // 截图 | ||
119 | + bool m_bSnapShoting{false}; | ||
120 | + DvppDataMemory* m_cached_mem{nullptr}; | ||
121 | + mutex m_cached_mutex; | ||
122 | + condition_variable m_cached_cond; | ||
62 | }; | 123 | }; |
63 | \ No newline at end of file | 124 | \ No newline at end of file |
src/decoder/dvpp/FFReceiver.cpp deleted
1 | -#include "FFReceiver.h" | ||
2 | -#include <fstream> | ||
3 | - | ||
4 | -const int g_pkt_size = 1024 * 1024; // 单个AVPacket大小的最大值 | ||
5 | - | ||
6 | -FFReceiver::FFReceiver(/* args */) | ||
7 | -{ | ||
8 | - fmt_ctx = nullptr; | ||
9 | - m_bRunning = false; | ||
10 | - | ||
11 | - stream = nullptr; | ||
12 | - stream_index = -1; | ||
13 | - pix_fmt = AV_PIX_FMT_NONE; | ||
14 | - m_dec_name = ""; | ||
15 | - | ||
16 | - m_bPause = false; | ||
17 | - m_bReal = true; | ||
18 | - | ||
19 | - m_bFinished = false; | ||
20 | - m_dec_keyframe = false; | ||
21 | - m_fps = 0.0; | ||
22 | - | ||
23 | - m_read_thread = 0; | ||
24 | -} | ||
25 | - | ||
26 | -FFReceiver::~FFReceiver() | ||
27 | -{ | ||
28 | - releaseFFmpeg(); | ||
29 | - | ||
30 | - // 这个只能放在析构函数中,因为会影响到解码类中的m_pktQueueptr队列 | ||
31 | - // 所以应当确保在所有工作线程都退出后才释放 | ||
32 | - for(int i = 0; i < m_vec_pkt.size(); i++){ | ||
33 | - av_packet_free(&m_vec_pkt[i]); | ||
34 | - } | ||
35 | -} | ||
36 | - | ||
37 | -AVCodecContext* FFReceiver::init_FFmpeg(ReceiverConfig config){ | ||
38 | - | ||
39 | -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) | ||
40 | - av_register_all(); | ||
41 | -#endif | ||
42 | -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100) | ||
43 | - avcodec_register_all(); | ||
44 | -#endif | ||
45 | - | ||
46 | - avformat_network_init(); | ||
47 | - | ||
48 | - const char* uri = config.uri; | ||
49 | - fstream infile(uri); | ||
50 | - if (infile.is_open()){ | ||
51 | - m_bReal = false; | ||
52 | - infile.close(); | ||
53 | - }else { | ||
54 | - m_bReal = true; | ||
55 | - } | ||
56 | - | ||
57 | - m_dec_name = config.dec_name; | ||
58 | - m_pktQueueptr = config.pktQueueptr; | ||
59 | - receiver_finished_cbk = config.receiver_finished_cbk; | ||
60 | - | ||
61 | - // 打开输入视频文件 | ||
62 | - AVDictionary *options = nullptr; | ||
63 | - av_dict_set( &options, "bufsize", "655360", 0 ); | ||
64 | - av_dict_set( &options, "rtsp_transport", config.force_tcp ? "tcp" : "udp", 0 ); | ||
65 | - av_dict_set( &options, "stimeout", "30000000", 0 ); // 单位为 百万分之一秒 | ||
66 | - | ||
67 | - fmt_ctx = avformat_alloc_context(); | ||
68 | - const char* input_file = uri; | ||
69 | - if (avformat_open_input(&fmt_ctx, input_file, nullptr, &options) != 0) { | ||
70 | - LOG_ERROR("[{}]- Cannot open input file: {}", m_dec_name, input_file); | ||
71 | - return nullptr; | ||
72 | - } | ||
73 | - av_dump_format(fmt_ctx, 0, input_file, 0); | ||
74 | - | ||
75 | - // 查找流信息 | ||
76 | - if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) { | ||
77 | - LOG_ERROR("[{}]- Cannot find input stream information!", m_dec_name); | ||
78 | - return nullptr; | ||
79 | - } | ||
80 | - | ||
81 | - // 查找视频流信息 | ||
82 | - AVCodec *decoder = nullptr; | ||
83 | - stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); | ||
84 | - if (stream_index < 0) { | ||
85 | - LOG_ERROR("[{}]- Cannot find a video stream in the input file!", m_dec_name); | ||
86 | - return nullptr; | ||
87 | - } | ||
88 | - AVCodec *vcodec = avcodec_find_decoder(decoder->id); | ||
89 | - | ||
90 | - avctx = avcodec_alloc_context3(vcodec); | ||
91 | - if(avctx == nullptr){ | ||
92 | - LOG_ERROR("[{}]- alloc AVCodecContext failed!", m_dec_name); | ||
93 | - return nullptr; | ||
94 | - } | ||
95 | - | ||
96 | - do{ | ||
97 | - // 得到视频流对象 | ||
98 | - AVStream* stream = fmt_ctx->streams[stream_index]; | ||
99 | - AVCodecParameters *codecpar = stream->codecpar; | ||
100 | - if (avcodec_parameters_to_context(avctx, codecpar) < 0) | ||
101 | - break; | ||
102 | - | ||
103 | - const AVBitStreamFilter * filter = nullptr; | ||
104 | - if(codecpar->codec_id == AV_CODEC_ID_H264){ | ||
105 | - filter = av_bsf_get_by_name("h264_mp4toannexb"); | ||
106 | - }else if(codecpar->codec_id == AV_CODEC_ID_HEVC){ | ||
107 | - filter = av_bsf_get_by_name("hevc_mp4toannexb"); | ||
108 | - }else { | ||
109 | - LOG_ERROR("[{}]- codec_id is not supported!", m_dec_name); | ||
110 | - break; | ||
111 | - } | ||
112 | - | ||
113 | - int ret = av_bsf_alloc(filter, &h264bsfc); | ||
114 | - if (ret < 0){ | ||
115 | - break; | ||
116 | - } | ||
117 | - | ||
118 | - avcodec_parameters_copy(h264bsfc->par_in, codecpar); | ||
119 | - av_bsf_init(h264bsfc); | ||
120 | - | ||
121 | - frame_width = codecpar->width; | ||
122 | - frame_height = codecpar->height; | ||
123 | - pix_fmt = (AVPixelFormat)codecpar->format; | ||
124 | - m_fps = av_q2d(stream ->avg_frame_rate); | ||
125 | - | ||
126 | - LOG_INFO("[{}]- init ffmpeg success! input:{} frame_width:{} frame_height:{} fps:{} ", m_dec_name, input_file, frame_width, frame_height, m_fps); | ||
127 | - | ||
128 | - for(int i = 0; i < 50; i++){ | ||
129 | - AVPacket* pkt = av_packet_alloc(); | ||
130 | - av_init_packet( pkt ); | ||
131 | - m_vec_pkt.push_back(pkt); | ||
132 | - } | ||
133 | - m_pktQueueptr->init(m_vec_pkt); | ||
134 | - | ||
135 | - return avctx; | ||
136 | - }while(0); | ||
137 | - | ||
138 | - LOG_ERROR("[{}]- init ffmpeg failed ! input:{} ", m_dec_name); | ||
139 | - | ||
140 | - return nullptr; | ||
141 | -} | ||
142 | - | ||
143 | -void FFReceiver::releaseFFmpeg(){ | ||
144 | - m_dec_keyframe = false; | ||
145 | - if(h264bsfc){ | ||
146 | - av_bsf_free(&h264bsfc); | ||
147 | - h264bsfc = nullptr; | ||
148 | - } | ||
149 | - if (fmt_ctx){ | ||
150 | - avformat_close_input(&fmt_ctx); | ||
151 | - fmt_ctx = nullptr; | ||
152 | - } | ||
153 | - if(avctx){ | ||
154 | - avcodec_free_context(&avctx); | ||
155 | - avctx = nullptr; | ||
156 | - } | ||
157 | -} | ||
158 | - | ||
159 | -void FFReceiver::read_thread(){ | ||
160 | - | ||
161 | - int frame_count = 0; | ||
162 | - int ret = -1; | ||
163 | - | ||
164 | - AVPacket* pkt; | ||
165 | - | ||
166 | - while (m_bRunning){ | ||
167 | - | ||
168 | - if (!m_bReal){ | ||
169 | - if (m_bPause){ | ||
170 | - std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
171 | - continue; | ||
172 | - } | ||
173 | - } | ||
174 | - | ||
175 | - AVPacket* pkt = m_pktQueueptr->getTail(); | ||
176 | - if(pkt == nullptr){ | ||
177 | - std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||
178 | - continue; | ||
179 | - } | ||
180 | - | ||
181 | - // AVPacket* pkt = av_packet_alloc(); | ||
182 | - // av_init_packet( pkt ); | ||
183 | - | ||
184 | - int result = av_read_frame(fmt_ctx, pkt); | ||
185 | - if (result == AVERROR_EOF || result < 0){ | ||
186 | - av_packet_unref(pkt); | ||
187 | - LOG_ERROR("[{}]- Failed to read frame!", m_dec_name); | ||
188 | - break; | ||
189 | - } | ||
190 | - | ||
191 | - // m_pktQueueptr->addTail(); | ||
192 | - | ||
193 | - // std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
194 | - // pkt = m_pktQueueptr->getHead(); | ||
195 | - // av_packet_unref(pkt); | ||
196 | - // m_pktQueueptr->addHead(); | ||
197 | - | ||
198 | - // av_packet_free(&pkt); | ||
199 | - // pkt = nullptr; | ||
200 | - // continue; | ||
201 | - | ||
202 | - if (m_dec_keyframe && !(pkt->flags & AV_PKT_FLAG_KEY)) { | ||
203 | - av_packet_unref(pkt); | ||
204 | - continue; | ||
205 | - } | ||
206 | - | ||
207 | - if (stream_index == pkt->stream_index){ | ||
208 | - | ||
209 | - ret = av_bsf_send_packet(h264bsfc, pkt); | ||
210 | - if(ret < 0) { | ||
211 | - LOG_ERROR("[{}]- av_bsf_send_packet error!", m_dec_name); | ||
212 | - } | ||
213 | - | ||
214 | - while ((ret = av_bsf_receive_packet(h264bsfc, pkt)) == 0) { | ||
215 | - if(pkt->size > g_pkt_size){ | ||
216 | - LOG_ERROR("[{}]- pkt size 大于最大预设值!", m_dec_name); | ||
217 | - break; | ||
218 | - } | ||
219 | - | ||
220 | - if(!m_bRunning){ | ||
221 | - break; | ||
222 | - } | ||
223 | - | ||
224 | - m_pktQueueptr->addTail(); | ||
225 | - | ||
226 | - frame_count++; | ||
227 | - } | ||
228 | - } else { | ||
229 | - // 音频等其他分量的情形 | ||
230 | - av_packet_unref(pkt); | ||
231 | - } | ||
232 | - } | ||
233 | - | ||
234 | - LOG_INFO("[{}]- read thread exit.", m_dec_name); | ||
235 | - m_bFinished = true; | ||
236 | - | ||
237 | - receiver_finished_cbk(m_finishedReceiveArg); | ||
238 | -} | ||
239 | - | ||
240 | -bool FFReceiver::start(){ | ||
241 | - m_bRunning = true; | ||
242 | - | ||
243 | - pthread_create(&m_read_thread,0, | ||
244 | - [](void* arg) | ||
245 | - { | ||
246 | - FFReceiver* a=(FFReceiver*)arg; | ||
247 | - a->read_thread(); | ||
248 | - return (void*)0; | ||
249 | - } | ||
250 | - ,this); | ||
251 | - | ||
252 | - return true; | ||
253 | -} | ||
254 | - | ||
255 | -void FFReceiver::close(){ | ||
256 | - m_bRunning=false; | ||
257 | - | ||
258 | - if(m_read_thread != 0){ | ||
259 | - pthread_join(m_read_thread,0); | ||
260 | - } | ||
261 | -} | ||
262 | - | ||
263 | -float FFReceiver::fps(){ | ||
264 | - return m_fps; | ||
265 | -} | ||
266 | - | ||
267 | -bool FFReceiver::getResolution( int &width, int &height ){ | ||
268 | - width = frame_width; | ||
269 | - height = frame_height; | ||
270 | - return true; | ||
271 | -} | ||
272 | - | ||
273 | -void FFReceiver::pause(){ | ||
274 | - m_bPause = true; | ||
275 | -} | ||
276 | - | ||
277 | -void FFReceiver::resume(){ | ||
278 | - m_bPause = false; | ||
279 | -} | ||
280 | - | ||
281 | -void FFReceiver::setDecKeyframe(bool bKeyframe) | ||
282 | -{ | ||
283 | - m_dec_keyframe = bKeyframe; | ||
284 | -} | ||
285 | - | ||
286 | -bool FFReceiver::isRunning(){ | ||
287 | - return m_bRunning; | ||
288 | -} | ||
289 | - | ||
290 | -bool FFReceiver::isFinished(){ | ||
291 | - return m_bFinished; | ||
292 | -} | ||
293 | - | ||
294 | -bool FFReceiver::isPausing(){ | ||
295 | - return m_bPause; | ||
296 | -} | ||
297 | - | ||
298 | -void FFReceiver::setFinishCbkArg(const void* userPtr){ | ||
299 | - m_finishedReceiveArg = userPtr; | ||
300 | -} | ||
301 | \ No newline at end of file | 0 | \ No newline at end of file |
src/decoder/dvpp/FFReceiver.h deleted
1 | -#ifndef __FFRECEIVER_H__ | ||
2 | -#define __FFRECEIVER_H__ | ||
3 | - | ||
4 | -#include "depend_headers.h" | ||
5 | -#include "CircularQueue.hpp" | ||
6 | - | ||
7 | -typedef void(*RECEIVER_FINISHED_CALLBACK)(const void* userPtr); | ||
8 | - | ||
9 | -struct ReceiverConfig{ | ||
10 | - const char* uri; | ||
11 | - string dec_name; | ||
12 | - bool force_tcp; | ||
13 | - CircularQueue<AVPacket*> *pktQueueptr; | ||
14 | - RECEIVER_FINISHED_CALLBACK receiver_finished_cbk; // 解码线程结束后的回调接口 | ||
15 | -}; | ||
16 | - | ||
17 | -class FFReceiver | ||
18 | -{ | ||
19 | -public: | ||
20 | - FFReceiver(/* args */); | ||
21 | - ~FFReceiver(); | ||
22 | - | ||
23 | - AVCodecContext* init_FFmpeg(ReceiverConfig config); | ||
24 | - void releaseFFmpeg(); | ||
25 | - void close(); | ||
26 | - bool start(); | ||
27 | - | ||
28 | - void pause(); | ||
29 | - void resume(); | ||
30 | - void setDecKeyframe(bool bKeyframe); | ||
31 | - bool isRunning(); | ||
32 | - bool isFinished(); | ||
33 | - bool isPausing(); | ||
34 | - bool getResolution( int &width, int &height ); | ||
35 | - float fps(); | ||
36 | - | ||
37 | - void setName(string nm){ | ||
38 | - m_dec_name = nm; | ||
39 | - } | ||
40 | - | ||
41 | - void setFinishCbkArg(const void* userPtr); | ||
42 | - | ||
43 | -private: | ||
44 | - void read_thread(); | ||
45 | - | ||
46 | -private: | ||
47 | - string m_dec_name; | ||
48 | - | ||
49 | - AVStream* stream; | ||
50 | - int stream_index; | ||
51 | - AVFormatContext *fmt_ctx; | ||
52 | - AVPixelFormat pix_fmt; | ||
53 | - int frame_width{0}; | ||
54 | - int frame_height{0}; | ||
55 | - | ||
56 | - pthread_t m_read_thread; | ||
57 | - | ||
58 | - bool m_bRunning; | ||
59 | - bool m_bFinished; | ||
60 | - | ||
61 | - bool m_bPause; | ||
62 | - | ||
63 | - bool m_bReal; // 是否实时流 | ||
64 | - | ||
65 | - float m_fps; | ||
66 | - | ||
67 | - FFDecConfig m_cfg; | ||
68 | - bool m_dec_keyframe; | ||
69 | - | ||
70 | - AVCodecContext *avctx{nullptr}; | ||
71 | - AVBSFContext * h264bsfc{nullptr}; | ||
72 | - | ||
73 | - vector<AVPacket*> m_vec_pkt; | ||
74 | - CircularQueue<AVPacket *> *m_pktQueueptr; | ||
75 | - | ||
76 | - const void * m_finishedReceiveArg; | ||
77 | - RECEIVER_FINISHED_CALLBACK receiver_finished_cbk; | ||
78 | -}; | ||
79 | - | ||
80 | - | ||
81 | -#endif | ||
82 | \ No newline at end of file | 0 | \ No newline at end of file |