diff --git "a/design/dfx/\350\247\206\351\242\221\346\241\206\346\236\266DFX\350\256\276\350\256\241.md" "b/design/dfx/\350\247\206\351\242\221\346\241\206\346\236\266DFX\350\256\276\350\256\241.md" index c85d5cb8cade5bbfa3f65c3c1850b86a270077a6..65932ac16a50cc9815a3a2660c7ecd16c392dda6 100644 --- "a/design/dfx/\350\247\206\351\242\221\346\241\206\346\236\266DFX\350\256\276\350\256\241.md" +++ "b/design/dfx/\350\247\206\351\242\221\346\241\206\346\236\266DFX\350\256\276\350\256\241.md" @@ -1,58 +1,82 @@ # 视频框架DFX设计 -## 事件设计 HiSysEvent +事件上报和播放链路设计核心实现都涉及到[HiTraceChain工具](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-dfx-hitracechain.md),该工具会通过唯一id将所有的对象都串联起来。 + +## 1. 事件设计 + 故障事件:根据视频框架的业务特点,把业务流程中关键的故障(用户会感知,而不是一个局部流程)及时上报,并同时报告完整的相关定位信息(比如trace、log等),支撑问题定位。 统计事件:根据视频框架业务演进的需要,关键的KPI指标,设计统计事件,统计关键信息。 -相关打点能力接口请查看[HiSysEvent打点接口](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-logging.md#/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-logging-config.md)。 - -### 事件管理服务 (可放在MonitorService) +### 1.1 事件管理服务 (可放在MonitorService) 职责: -1. 提供事件记录接口(比如:RecordEvent(name, parameters)),支持player framework, avcodec各个业务流程,调用事件记录接口,记录事件信息到事件管理服务。(接口和实现,放在 media_foundation,音频可能也会使用) +1. 提供事件记录接口,支持player framework, avcodec各个业务流程,调用事件记录接口,记录事件信息到事件管理服务。(接口和实现,放在 media_foundation,音频可能也会使用) 2. 记录并管理事件信息。 -3. 事件持久化到cache文件。(是否每个事件写一次文件?) -4. 按照规定的上报机制,上报事件。(故障事件立即上报,统计事件只记录信息) +3. 事件持久化到cache文件。 +4. 按照规定的上报机制,上报事件。 5. 定义事件类型、名称、字段等数据结构。 6. 统计事件,把所有事件记录信息转换为统计事件(json字符串)。 -#### 接口设计 -1. 使用简单工厂生成avplayer实例一一对应的MediaEvent实例 -```c++ -public: -MediaEvent MediaEventFactory::GetInstance(pid, uid, name); -void MediaEvent::DestroyMediaEvent(); // avplayer实例退出后需要销毁对应MediaEvent对象 +### 1.2 入口对象整体调用流程 -private: -void MediaEvent::MediaEvent(name); -``` -2. 业务中调用RecordEvent将对应的键值对传入到MediaEvent实例中 +#### 1.2.1 player framework 对象跟踪方式 +app -> js -> napi -> avplayer -> pipeline/filter -> module -> plugin -方案一:提供一个统一接口,各个业务逻辑调用的时候会保存到MediaEvent内部的PlayerInfo/RecorderInfo/RetrieverInfo实例中 -```c++ -void MediaEvent::RecordEvent(params); +1. napi创建实例(avplayer, avrecorder, avscreencapture等)的时候,就会创建HiTraceChainId。 + 后续所有调用流程,都能获取到入口实例对象的id。 +2. 借助 log trace id,它是唯一绑定一个实例的,还可以跨进程访问。 + 调用 HiTraceChain::GetId().GetChainId() 即可在任意位置获取到id。 ``` -方案二:提供定制化的接口 -```c++ -void MediaEvent::addPlayerBaseInfo(source_type, ...); -void MediaEvent::addPlayerDemuxerInfo(demuxer_type, ...); -void MediaEvent::addPlayerCodecInfo(video_height, ...); +class HiTraceChain final { +public: + // Get trace id of current thread, and return a invalid trace id if no + // trace id belong to current thread. + static HiTraceId GetId(); +} ``` -#### 数据结构设计 -- ErrorEvent : 一次失败 --- 直接调用HiSysEvent接口上报。传哪些参数,事件包含哪些内容。 -- InfoEvent : 汇聚一些信息 --- 一次上报的所有信息 +#### 1.2.2 avcodec 原子能力入口调用方式 +app -> ndk -> ndk_impl -> demuxer -> module -> plugin -```plantuml -class MediaEvent { - void Report(); +1. ndk_impl 实现里面,创建 HiTraceChainId。 +2. 后续的所有调用流程,包括记录信息,都是跟 player framework 一样,复用相同的代码。 + +原子能力需要做一个自己的 Monitor 线程。 + +### 1.3 接口设计 + +**事件定义** + +事件类型分为以下几种,定义在CallType枚举中。 + +```cpp +enum CallType { + AVPLAYER, + AVRECORDER, + METADATA_RETRIEVER, + IMAGE_GENERATER, + AVDEMUXER, + AVMUXER, + VIDEO_DECODER, + VIDEO_DECODER, + AUDIO_DECODER, + AUDIO_ENCODER, + SOUNDPOOL, + SCREEN_CAPTRUER, + AVTRANSCODER +} +``` + +**打点** + +MediaInfo用来保存单次事件(例如播放player)所有需要打点的内容,以key-value的方式保存,key定义在XXXInfo枚举中。 +```c++ +class MediaInfo { + Meta; } -class PlayInfo { - void AddSourceInfo(...); // 注释说明要写哪些key - void AddDemuxerInfo(...); - void AddDecoderInfo(...); - --- +player涉及的key +{ time_stamp : string source_type : string url : string @@ -69,20 +93,8 @@ class PlayInfo { video_mime : string } -class PlayInfoEvent extends MediaEvent { // 整个系统,只有它的一个实例 - static shared_ptr GetInstance(); - void CreatePlayInfo(pid); // 内部产生针对当前这次播放对应的PlayInfo实例,SetSource()里调用。内部实现调用 HiTraceChain::GetId().GetChainId() 获得标识player的唯一id。 pid也可能不用传入。 - shared_ptr GetPlayInfo(pid); // 获取当前播放对应的信息记录实例。内部实现通过player id,匹配正确的最新的 PlayInfo。 - void Report(); // 上报已有信息为一个Event,并清空数据 - --- - Map>> map; // key -- app name -} - -class RecordInfo { - void AddEncoderInfo(...); - void AddMuxerInfo(...); - void AddSinkerInfo(...); - ---- +recorder涉及的key +{ time_stamp : string audio_source_type : string audio_encoder : string @@ -100,28 +112,8 @@ class RecordInfo { recorder_duration : int32_t } -class RecordInfoEvent extends MediaEvent { - static RecordInfoEvent GetInstance(); - void CreateRecordInfo(pid); // 内部产生针对当前这次录制对应的RecordInfo实例, SetOutputFormat()里面调用 - RecordInfo GetRecordInfo(pid, instanceId); // 获取当前播放对应的信息记录实例 - void Report(); // 上报已有信息为一个Event,并清空数据 - --- - Map> map; // key -- app name -} - -class DemuxInfo { -} - -class DemuxInfoEvent { - static DemuxInfoEvent GetInstance(); - void CreateDemuxInfo(pid); // 内部产生针对当前这次调用对应的DemuxInfo实例 - DemuxInfo GetDemuxInfo(pid, demuxerId); // 获取当前播放对应的信息记录实例 - void Report(); // 上报已有信息为一个Event,并清空数据 - --- - Map> map; // key -- app name -} - -class MetadataRetriveInfo { +MetadataRetriver涉及的key +{ time_stamp : string retriever_player_type : string get_frame_type : string @@ -129,125 +121,65 @@ class MetadataRetriveInfo { device_model : string chip_board : string } - -class MonitorServer { - list> list; -} - ``` -最终效果: -1. 存储了所有内部记录的事件的信息 -2. 按app_name 进行聚类和统计, map> -3. 根据上述效果,设计数据结构 - -#### 事件上报机制设计 -1. 统计事件,如何根据记录的信息,生成事件? -+ 同一个avplayer/avrecorder/retriever会根据pid、uid和name生成唯一的MediaEvent实例,各个关键节点在执行过程中调用MediaEvent的RecordEvent方法,MediaEvent会根据name信息把信息保存到PlayerInfo/RecorderInfo/RetrieverInfo实例中。 -+ 当avplayer/avrecorder/retriever销毁或者所有info收集结束后会尝试把事件通过HiSysEventWrite上报,如果网络等因素无法上报,则保存到本地。 -2. 定时上报如何实现。 -MediaEvent构造方法中执行定时逻辑,默认为每晚8点上报,**同时需要做离散操作(例如离散19:30-20:30),防止出现浪涌现象。** +**事件记录及上报** + +MediaEvent用来获取MediaInfo对象和事件上报功能,业务在调用方开始处(例如播放过程的setSource方法)需要先调用一次CreateMediaInfo并传入call_type(CallType)和pid,CreateMediaInfo会新生成call_type对应的map,里面保存了一系列MediaInfo,后续整个流程的打点只需要调用GetMediaInfo方法并往MediaInfo传入key-value即可。 ```c++ -void MediaEvent::ScheduleUpload(); + +class MediaEvent { + static void CreateMediaInfo(call_type, pid); // 以player为例,内部产生针对当前这次播放对应的MediaInfo实例,SetSource()里调用。内部实现调用 HiTraceChain::GetId().GetChainId() 获得标识player的唯一id。 + static shared_ptr GetMediaInfo(pid); // 以player为例,获取当前播放对应的信息记录实例。 + static void Report(); // 上报事件时,MonitorServer调用 MediaEvent::Report 即可,同时清空数据。 + + map>> map_; +} ``` -3. 对接到 HiSysEvent接口。 +保存到本地(可以考虑使用sql_lite,待讨论)。MediaEvent构造方法中执行定时逻辑,默认为每晚8点上报,同时需要做离散操作(例如离散19:30-20:30),防止出现浪涌现象。 + +音频原子能力存活在应用进程,可能无法直接写文件(待讨论) -**hisysevent.yaml文件player事件定义** -// 原有定义,只能一个Info event就要报一次,不能做汇聚。 +### 1.4 事件上报机制设计 + +MediaEvent::Report事件上报需要用到系统[HiSysEvent打点接口](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-logging.md#/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-logging-config.md)。 + +以player为例,需要在hisysevent.yaml文件中定义player相关事件,当前需要将整个MediaInfo都汇聚成一个json字符串作为单次事件对应点位。 ``` PLAY_INFO_EVENT: __BASE: {type: BEHAVIOR, level: MINOR, tag: PlayInfo, desc: play info} - INFO: {type: STRING, desc: play info} + PLAY_INFO: {type: STRING, desc: play info} + +RECORD_INFO_EVENT: +__BASE: {type: BEHAVIOR, level: MINOR, tag: RecordInfo, desc: record info} +RECORD_INFO: {type: STRING, desc: record info} +... ``` **业务中打点方式** ```c++ -HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::XXX, "PLAYER_INFO", +HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::XXX, "PLAY_INFO_EVENT", HiviewDFX::HiSysEvent::EventType::BEHAVIOR, - "TIME_STAMP", xxx, - "SOURCE_TYPE", xxx, - "URL", xxx, - "DEMUXER_TYPE", xxx, - "AUDIO_DECODEC_TYPE", xxx, - "BIT_RATE", xxx, - "AVERAGE_FRAME_RATE", xxx, - "VIDEO_HEIGHT", xxx, - "VIDEO_WIDTH", xxx, - "PLAYER_DURATION", xxx, - "SAMPLE_RATE", xxx, - "CHANNELS", xxx, - "AUDIO_MIME", xxx, - "VIDEO_MIME", xxx); + "PLAY_INFO", xxx(单个MediaInfo中key-value对应的json字符串)); + +HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::XXX, "RECORD_INFO_EVENT", + HiviewDFX::HiSysEvent::EventType::BEHAVIOR, + "RECORD_INFO", xxx(单个MediaInfo中key-value对应的json字符串)); +... ``` Domain定义在hisysevent.h中,目前没有player_framework相关的Domain,需要新增。 注:av_session业务中已经存在类似打点工具avsession_sysevent.h,除了封装HiSysEventWrite立刻打点的接口外,还提供了定制化的接口用来保存事件的参数,每隔一个小时会尝试上报一次事件。 -## 媒体业务调用的入口对象,在整个业务流程中是如何传递和使用的 - -### player framework 对象跟踪方式 -app -> js -> napi -> avplayer -> pipeline/filter -> module -> plugin - -1. napi创建实例(avplayer, avrecorder, avscreencapture等)的时候,就会创建HiTraceChainId。 - 后续所有调用流程,都能获取到入口实例对象的id。 -2. 借助 log trace id,它是唯一绑定一个实例的,还可以跨进程访问。 - 调用 HiTraceChain::GetId().GetChainId() 即可在任意位置获取到id。 -``` -class HiTraceChain final { -public: - // Get trace id of current thread, and return a invalid trace id if no - // trace id belong to current thread. - static HiTraceId GetId(); -} -``` - -### avcodec 原子能力入口调用方式 -app -> ndk -> ndk_impl -> demuxer -> module -> plugin - -1. ndk_impl 实现里面,创建 HiTraceChainId。 -2. 后续的所有调用流程,包括记录信息,都是跟 player framework 一样,复用相同的代码。 - -我们 DemuxInfoEvent 管理的位置,不能运行 MonitorServer, -它可能是只是一个单例对象。 - -原子能力调用方式,只是在它的进程中产生出这个对象。并记录相关信息。 - -原子能力需要做一个自己的 Monitor 线程。 - -### player framework / avcodec 两种调用方式,记录信息,最好是同样的实现方式 - -我们只提供 Event 实现,在调用者进程产生对象。 - -还有一个问题: - -怎样保证 avplayer入口方式,avdemuxer入口的方式,在 module, plugin层面记录信息的代码是完全复用。 - -1. 如果区分 PlayInfo, DemuxInfo,必然导致无法统一。 - 所以,可能需要把它们所有 Info 合并。 - 那就只有用 Meta key,value 来实现。 - 提供哪些key,value,就遍历所有的key,都存下来。 - -```cpp -enum CallType { - AVPLAYER, - AVRECORDER, - AVDEMUXER, - AUDIO_DECODER, - AUDIO_ENCODER, -} -class MediaInfo { - Meta; -} - -class MediaEvent { - static void CreateMediaInfo(call_type, pid); // 内部产生针对当前这次播放对应的PlayInfo实例,SetSource()里调用。内部实现调用 HiTraceChain::GetId().GetChainId() 获得标识player的唯一id。 pid也可能不用传入。 - static shared_ptr GetMediaInfo(pid); // 获取当前播放对应的信息记录实例。内部实现通过player id,匹配正确的最新的 PlayInfo。 - static void Report(); // 上报已有信息为一个Event,并清空数据 +## 2. 播放链路设计 - map>> m; -} +播放链路主要为了将同一个player/recorder/retriever/av_codec调用过程整个链路主要的对象都串联一起。 +例如整个播放链路涉及以下对象:app -> avplayer对象 -> avcodec对象 -> hisi decoder对象。 +将所有链路对象串联后,可以更加方便的分析多实例场景下各链路情况,同时也利于相关信息打印和事件上报。 +### 2.1 接口设计 +```c++ class MediaChainManager { static void AddInfo(meta{avcodec_name:"video_hw_decoder#xxxx", hisicodec_name:"video_hw_decoder#xxxx"}); static void RemoveInfo(meta{avcodec_name:"video_hw_decoder#xxxx", hisicodec_name:"video_hw_decoder#xxxx"}); @@ -256,9 +188,6 @@ class MediaChainManager { // 设计一个数据结构 app -> 实例 -> avcodec -> hisi 实例的 // MediaChain: app -> 实例 -> avcodec -> hisi 实例的, 可以存字符串 // 实例销毁时,list对应 实例的内容要删除。 - list list; + list list; } - -``` - -最后上报事件的时候,MonitorServer调用 MediaEvent::Report 即可。 +``` \ No newline at end of file