怎样将电网频率用于多媒体取证?面向音频取证的电网频率检测与增强【有源码】
多媒体来源取证与真伪取证的方法有很多种,其中一种有趣的体源取证方法是通过分析音频中的电网频率(俗称电流声)在音频中留下的痕迹,可以有效地检测音视频文件的全媒产生时间,并进行各种取证。体源该方法由武汉大学的全媒华光等老师提出,文末有源代码,体源解码与源码6供感兴趣的全媒老师和同学参考。
电网频率(electric network frequency,体源ENF)是全媒指交流电网的传输频率,我国标称值为 Hz(其他国家也有 Hz),体源是全媒被动多媒体取证的重要判据。它之所以能够成为取证判据,体源主要有三个原因:首先,全媒交流电和用电器的体源活动会产生以标称频率为基波的声学震动以及照明设备的灯光闪烁,这些不易被人感知的全媒振动和闪烁可以被音视频录制设备捕捉,形成一种“被动不可见水印”;其次,电网频率在标称值附近随机小范围波动,赋予了电网频率轨迹的独特性;第三,电网频率波动模式在同一电网内部所有位置保持一致,赋予了电网频率波动的一致性。经过多年的发展,电网频率判据已可用于音视频文件产生时间溯源、篡改检测与定位、地理位置溯源、云顶夺宝源码搭建重放攻击检测等取证任务。目前,电网频率分析是录音文件产生时间被动溯源的唯一有效方法。
然而,基于电网频率判据的数字取证研究仍面临一些困难。首先,并不是任意设备在任意条件下都能成功捕捉电网频率;其次,电网频率相对于录音内容和环境噪声十分微弱,在无法控制录音条件的实际取证任务中难以进行有效提取和分析。针对这两个问题,本项工作分别提出了录音文件中电网频率的检测和增强算法,并建立并开源了目前最大的“电网频率-武汉大学”(ENF-WHU)真实世界录音文件数据集,用于对相关算法进行全面评估。
为确认待验录音文件中是否存在电网频率以保证后续取证分析有效,本工作从信号检测理论出发,逐步放宽对信号模型的假设,推导出电网频率的一系列理论和实际检测器。其中,只有本工作提出的TF-detector为恒虚警率(CFAR)检测器。
由于信号模型和特性的巨大差异,现有语音(或其他信号)的增强的方法均无法有效增强电网频率信号。对于检测到电网频率的录音文件,为提升其可用性,码支付源码首发本工作提出了一种适合电网频率随机小范围缓慢波动特性的鲁棒滤波算法(robust filtering algorithm,RFA),将观测到的带噪电网频率信号调制到正弦频率调频(SFM)解析信号的瞬时频率,并引入核函数,通过处理其产生的正弦时频分布,逐个恢复去噪后的电网频率观测样本,显著提升了电网频率轨迹的质量,为后续取证分析提供了可靠数据。
以上工作为提升电网频率判据在实际取证中的可靠性,促进基于电网频率判据的录音文件取证从实验室走向实际应用提供了技术支撑。相关成果于和年分别发表在IEEE Transactions on Information Forensics and Security,作者为武汉大学华光、张海剑、廖晗、王清懿、叶登攀。
ENF-WHU数据集和MATLAB程序已开源:
github.com/ghuawhu/ENF-...
Guang Hua and Haijian Zhang*, “ENF signal enhancement in audio recordings,” IEEE Transactions on Information Forensics and Security, vol. , pp. -, .
Guang Hua, Han Liao, Qingyi Wang, Haijian Zhang*, and Dengpan Ye, “Detection of electric network frequency in audio recordings – from theory to practical detectors,” IEEE Transactions on Information Forensics and Security, vol. , pp. -, .
SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play
本章内容梳理了SRS在接收到RTMP信息后如何进行转发的过程。在此过程中,首先进行代码梳理,作者也在源码熟悉阶段,可能尚未完全梳理完接受到RTMP后信息如何处理、缓存以及转发给直播用户等内容。
SRS源码中的团体协会源码Play流程如下:
1. 进入play流程:本章内容直接从SrsRtmpConn::stream_service_cycle()方法开始梳理。
2. 在接受流程中,客户类型为SrsRtmpConnFMLEPublish “fmle publish”,而在转发流程中,客户类型为SrsRtmpConnPlay。
3. 在ponent_open创建对应流的解码线程。
3.6解封装处理
接下来是一个for(;;)循环:
(1)响应中断停止、暂停/继续、Seek操作;
(2)判断PacketQueue队列是否满了,如果满了就休眠ms,继续循环;
(3)调用av_read_frame从码流中读取若干个音频帧或一个视频帧;
(4)从输入文件中读取一个AVPacket,判断当前AVPacket是否在播放时间范围内,如果是则调用packet_queue_put函数,根据类型将其放在音频/视频/字幕的PacketQueue中。
四、stream_component_open函数3.5小节讲到,stream_component_open函数负责创建不同流的解码线程。那么它是如何创建解码线程的呢?
4.1创建AVCodecContext
AVCodecContext是编解码器上下文,保存音视频编解码相关的信息。使用avcodec_alloc_context3函数分配空间,使用avcodec_free_context函数释放空间。
4.2查找解码器
根据解码器的id,调用avcodec_find_decoder函数,查找对应的大发云php源码解码器。与之类似的一个函数是avcodec_find_encoder,用于查找FFmpeg的编码器。两个函数返回的结构体都是AVCodec。
如果指定了解码器名称,则需要调用avcodec_find_decoder_by_name函数查找解码器。
不管是哪种方式查找解码器,如果没有找到解码器,都会抛异常退出流程。
4.3解码器初始化
找到解码器后,需要打开解码器,并对解码器初始化,对应的函数是avcodec_open2,该函数也支持编码器的初始化。
4.4创建解码线程
判断解码类型,创建不同的解码线程。
switch(avctx->codec_type){ caseAVMEDIA_TYPE_AUDIO://音频...if((ret=decoder_init(&is->auddec,avctx,&is->audioq,is->continue_read_thread))<0)gotofail;...if((ret=decoder_start(&is->auddec,audio_thread,"audio_decoder",is))<0)gotoout;...caseAVMEDIA_TYPE_VIDEO://视频...if((ret=decoder_init(&is->viddec,avctx,&is->videoq,is->continue_read_thread))<0)gotofail;if((ret=decoder_start(&is->viddec,video_thread,"video_decoder",is))<0)gotoout;...caseAVMEDIA_TYPE_SUBTITLE://字幕...if((ret=decoder_init(&is->subdec,avctx,&is->subtitleq,is->continue_read_thread))<0)gotofail;if((ret=decoder_start(&is->subdec,subtitle_thread,"subtitle_decoder",is))<0)gotoout;...}线程创建在decoder_start函数中,依然使用SDL创建线程的方式,调用SDL_CreateThread函数。
五、video_thread函数视频解码线程从视频的PacketQueue中不断读取AVPacket,解码完成后将AVFrame放入视频FrameQueue。音频的解码实现和视频类似,这里仅介绍视频的解码过程。
5.1创建AVFrame
AVFrame描述解码后的原始音频数据或视频数据,通过av_frame_alloc函数分配内存,通过av_frame_free函数释放内存。
5.2视频解码
开启for(;;)循环,不断调用get_video_frame函数解码一个视频帧。该函数主要调用了decoder_decode_frame函数解码,decoder_decode_frame函数对音频、视频、字幕都进行了处理,主要依靠FFmpeg的avcodec_receive_frame函数获取解码器解码输出的数据。
拿到解码后的视频帧后,会根据音视频同步的方式和命令行的-framedrop选项,判断是否需要丢弃失去同步的视频帧。
命令行带-framedrop选项,无论哪种音视频同步机制,都会丢弃失去同步的视频帧。
命令行带-noframedrop选项,无论哪种音视频同步机制,都不会丢弃失去同步的视频帧。
命令行不带-framedrop或-noframedrop选项,若音视频同步机制为同步到视频,则不丢弃失去同步的视频帧,否则会丢弃失去同步的视频帧。
5.3放入FrameQueue
调用queue_picture函数,将AVFrame放入FrameQueue。该函数内部调用了frame_queue_push函数,采用了环形缓冲区的处理方式,对写指针windex累加。
staticvoidframe_queue_push(FrameQueue*f){ if(++f->windex==f->max_size)f->windex=0;SDL_LockMutex(f->mutex);f->size++;SDL_CondSignal(f->cond);SDL_UnlockMutex(f->mutex);}六、音视频同步ffplay默认采用将视频同步到音频的方式,分以下三种情况:
如果视频和音频进度一致,不需要同步;
如果视频落后音频,则丢弃当前帧直接播放下一帧,人眼感觉跳帧了;
如果视频超前音频,则重复显示上一帧,等待音频,人眼感觉视频画面停止了,但是有声音在播放;
ffplay视频同步到音频的逻辑在视频播放函数video_refresh中实现。该函数的调用链是:main()->event_loop()->refresh_loop_wait_event()->video_refresh。
6.1判断播放完成
调用frame_queue_nb_remaing函数计算剩余没有显示的帧数是否等于0,如果是,则不需要走剩下的步骤。计算过程比较简单,用FrameQueue的size-rindex_shown,size是FrameQueue的大小,rindex_shown表示rindex指向的节点是否已经显示,如果已经显示则为1,否则为0。
6.2播放序列匹配
****分别调用frame_queue_peek_last和frame_queue_peek函数从FrameQueue中获取上一帧和当前帧,上一帧是上次已经显示的帧,当前帧是当前待显示的帧。
(1)比较当前帧和当前PacketQueue的播放序列serial是否相等:
如果不等,重试视频播放的逻辑;
如果相等,则进入(2)流程判断;
注:serial是用来区分是不是连续的数据,如果发生了seek,会开始一个新的播放序列,
(2)比较上一帧和当前帧的播放序列serial是否相等:
如果不相等,则将frame_timer更新为当前时间;
如果相等,不处理并进入下一流程
6.3判断是否重复上一帧
(1)将上一帧lastvp和当前帧vp传入vp_duration函数,通过vp->pts-lastvp->pts计算上一帧的播放时长。
注:pts全称是PresentationTimeStamp,显示时间戳,表示解码后得到的帧的显示时间。
(2)在compute_target_delay函数中,调用get_clock函数获取视频时钟,调用get_master_clock函数获取同步时钟,计算两个时钟的差值,根据差值计算需要delay的时间。
(3)如果当前帧播放时刻(is->frame_timer+delay)大于当前时刻(time),表示当前帧的播放时间还没有到,相当于当前视频超前音频了,则需要将上一帧再播放一遍。
last_duration=vp_duration(is,lastvp,vp);delay=compute_target_delay(last_duration,is);time=av_gettime_relative()/.0;if(time<is->frame_timer+delay){ *remaining_time=FFMIN(is->frame_timer+delay-time,*remaining_time);gotodisplay;}6.4判断是否丢弃未播放的帧
如果当前队列中的帧数大于1,则需要考虑丢帧,只有一帧的时候不考虑丢帧。
(1)调用frame_queue_peek_next函数获取下一帧(下一个待显示的帧),根据当前帧和下一帧计算当前帧的播放时长,计算过程和6.3相同。
(2)满足以下条件时,开始丢帧:
当前播放模式不是步进模式;
丢帧策略生效:framedrop>0,或者当前音视频同步策略不是音频到视频。
当前帧vp还没有来得及播放,但是下一帧的播放时刻(is->frame_timer+duration)已经小于当前系统时刻(time)了。
(3)丢帧时,将is->frame_drops_late++,并调用frame_queue_next函数将上一帧删除,更新FrameQueue的读指针rindex和size。
if(frame_queue_nb_remaining(&is->pictq)>1){ Frame*nextvp=frame_queue_peek_next(&is->pictq);duration=vp_duration(is,vp,nextvp);if(!is->step&&(framedrop>0||(framedrop&&get_master_sync_type(is)!=AV_SYNC_VIDEO_MASTER))&&time>is->frame_timer+duration){ is->frame_drops_late++;frame_queue_next(&is->pictq);gotoretry;}}七、渲染ffplay最终的图像渲染是由SDL完成的,在video_display中调用了SDL_RenderPresent(render)函数,其中render参数是最开始在main函数中创建的。在渲染之前,需要将解码得到的视频帧数据转换为SDL支持的图像格式。转换过程在upload_texture函数中实现,细节不在此处分析。
音频类似,如果解码得到的音频不能被SDL支持,需要对音频进行重采样,将音频帧格式转换为SDL支持的格式。
八、小结本文从整体播放流程出发,介绍了ffplay播放器播放媒体文件的主要流程,不深陷于代码细节。同时,对FFmpeg的一些常用函数有了一些了解,对我们自己手写一个简单的播放器有很大的帮助。
----------END----------SRS流媒体服务器——单机环境搭建和源码目录介绍
启动srs
2. 显示日志信息
3. 确认srs是否正常启动
4. 安全退出正在运行的srs
5. 默认后台启动,调试需修改配置文件为前台
相关视频推荐
SRS-RTMP-WebRTC流媒体服务器入门
全球Star第一的流媒体服务器SRS4.0 WebRTC音视频通话分析
SRS流媒体服务器架构设计及源码分析
免费FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发免费学习地址
纯干货免费分享C++音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击 加群免费领取哦~
源码目录介绍
1. trunk目录
2. src下的源码
3. app
4. core
5. kernel 音视频格式相关
6. libs
7. main
8. protocol 流媒体协议相关
9. service
. utest
. 八个目录,二百零三个文件
2024-12-24 00:07
2024-12-23 23:03
2024-12-23 22:50
2024-12-23 22:29
2024-12-23 22:22