ffmpeg解析TS流


介绍: 
MPEG的系统层编码为不同的应用场景设计了两种格式: 
TS(Transport Stream) 和PS(Program Stream),
它们两者之间不具有层级关系,
在逻辑上,它们两者都是由PES(Packetized Elementary Stream)包组成的,
所以可以很方便地实现相互转换.
TS(Transport Stream): 
是将具有一个或多个独立时间基的一个或多个节目(包括音频和视频)组成一个流,
组成同一个节目的基本流(如一个视频流,多个音频流)的PES包有一个共用的时间基。
TS的包长标准为188bytes.
从上面的定义可以分成三层来看TS/PS。
ES层   : 由单独的音频(如mp3),视频流(如h.264)组成基本的ES(Elementary Stream)。
PES层  : 将基本的ES按一定的规则(如H.264以AU)进行封装,并打上时间戳,组成PES。
TS/PS层: 将PES包进行切分后再封装成188bytes大小的TS包,
同时还将一些节目信息也封装成TS包(称为section), 两者共同组成TS层。
从上面的总结,TS/PS总体上来说,是一种封装格式,用来承载数据。
所以FFmpeg
将TS/PS的解析文件定义在libavformat/mpegts.c文件中
将音频,视频的解码定义在libavcodec/mpeg12.c文件中
下面来看FFmpeg是如何进行TS的demuxer的。
1. MPEG2-TS的demuxer函数
  1. AVInputFormat ff_mpegts_demuxer = { 
  2.     "mpegts", 
  3.     NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
  4.     sizeof(MpegTSContext),
  5.     mpegts_probe,
  6.     mpegts_read_header,
  7.     mpegts_read_packet, 
  8.     mpegts_read_close, 
  9.     read_seek,
  10.     mpegts_get_pcr,
  11.     .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, 
  12. #ifdef USE_SYNCPOINT_SEARCH 
  13.     .read_seek2 = read_seek2, 
  14. #endif 
  15. };
2. 解析流中的TS格式

 

  1. /*
  2.  * 出现3种格式,主要原因是:
  3.  * TS标准是 188Bytes;
  4.  * 日本标准是192Bytes的DVH-S格式;
  5.  * 第三种的 204Bytes则是在188Bytes的基础上,加上16Bytes的FEC(前向纠错).
  6.  */
  7. #define TS_PACKET_SIZE 188
  8. #define TS_DVHS_PACKET_SIZE 192
  9. #define TS_FEC_PACKET_SIZE 204
  10. #define TS_MAX_PACKET_SIZE 204
  11. //< maximum score, half of that is used for file-extension-based detection
  12. #define AVPROBE_SCORE_MAX 100

  1. /*
  2.  * 函数功能:
  3.  * 分析流中是三种TS格式的哪一种
  4.  */
  5. static int mpegts_probe(AVProbeData *p)
  6. {
  7. #define CHECK_COUNT 10
  8.   const int size= p->buf_size;
  9.   int score, fec_score, dvhs_score;
  10.   int check_count= size / TS_FEC_PACKET_SIZE;
  11.   if (check_count < CHECK_COUNT)
  12.       return -1;
  13.   score     = analyze(p->buf, TS_PACKET_SIZE *check_count, TS_PACKET_SIZE , NULL) 
  14.               * CHECK_COUNT / check_count;
  15.   dvhs_score= analyze(p->buf, TS_DVHS_PACKET_SIZE*check_count, TS_DVHS_PACKET_SIZE, NULL)
  16.               * CHECK_COUNT / check_count;
  17.   fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE *check_count, TS_FEC_PACKET_SIZE , NULL)
  18.               * CHECK_COUNT / check_count;
  19.   /* 
  20.    * we need a clear definition for the returned score ,
  21.    * otherwise things will become messy sooner or later
  22.    */
  23.   if (score > fec_score && score > dvhs_score && score > 6) 
  24.     return AVPROBE_SCORE_MAX + score - CHECK_COUNT;
  25.   else if(dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6) 
  26.     return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT;
  27.   else if(fec_score > 6) 
  28.     return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT;
  29.   else 
  30.     return -1;
  31. }
  1. /*
  2.  * 函数功能:
  3.  * 在size大小的buf中,寻找满足特定格式,长度为packet_size的
  4.  * packet的个数;
  5.  * 显然,返回的值越大越可能是相应的格式(188/192/204)
  6.  */
  7. static int analyze(const uint8_t *buf, int size, int packet_size, int *index){
  8.   int stat[TS_MAX_PACKET_SIZE];
  9.   int i;
  10.   int x=0;
  11.   int best_score=0;
  12.   memset(stat, 0, packet_size*sizeof(int));
  13.     
  14.   for (x=i=0; i < size-3; i++)
  15.   {
  16.     if ((buf[i] == 0x47) && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30))
  17.     {
  18.       stat[x]++;
  19.             
  20.       if (stat[x] > best_score)
  21.       {
  22.         best_score= stat[x];
  23.         if (index) 
  24.           *index= x;
  25.       }
  26.     }
  27.     x++;
  28.     if (x == packet_size) 
  29.       x= 0; 
  30.   }
  31.     
  32.   return best_score;
  33. }
buf[i] == 0x47  
其中的sync_byte固定为0x47,即上面的. 
!(buf[i+1] & 0x80)   
由于transport_error_indicator为1的TS Packet实际有错误,
表示携带的数据无意义, 这样的Packet显然没什么意义.
buf[i+3] & 0x30 
对于adaptation_field_control, 如果取值为0x00,则表示为未来保留,现在不用.
这就是MPEG TS的侦测过程.
3. MPEG2-TS头解析
  1. #define NB_PID_MAX 8192
  2. #define MAX_SECTION_SIZE 4096
  3.         
  4. /* pids */
  5. #define PAT_PID 0x0000
  6. #define SDT_PID 0x0011
  7.         
  8. /* table ids */
  9. #define PAT_TID 0x00
  10. #define PMT_TID 0x02
  11. #define SDT_TID 0x42

  1. /*
  2.  * 函数功能:
  3.  * 
  4.  */
  5. int mpegts_read_header(AVFormatContext *s, AVFormatParameters *ap)
  6. {
  7.   /*
  8.    * MpegTSContext , 是为了解码不同容器格式所使用的私有数据,
  9.    * 只有在相应的诸如mpegts.c文件才可以使用的.
  10.    * 这样,增加了这个库的模块化.
  11.    */
  12.   MpegTSContext *ts = s->priv_data;
  13.   AVIOContext *pb = s->pb;
  14.   uint8_t buf[8*1024];
  15.   int len;
  16.   int64_t pos;
  17.   /* read the first 8*1024 bytes to get packet size */
  18.   pos = avio_tell(pb);                   // 获取buf的当前位置
  19.   len = avio_read(pb, buf, sizeof(buf)); // 从pb->opaque中读取sizeof(buf)个字节到buf
  20.   if (len != sizeof(buf))
  21.     goto fail;
  22.   /* 
  23.    * 获得TS包的实际长度
  24.    */
  25.   ts->raw_packet_size = get_packet_size(buf, sizeof(buf));
  26.   if (ts->raw_packet_size <= 0) 
  27.   {
  28.     av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n");
  29.     ts->raw_packet_size = TS_PACKET_SIZE;
  30.   }
  31.   ts->stream = s; 
  32.   ts->auto_guess = 0;
  33.   
  34.   if (s->iformat == &ff_mpegts_demuxer) 
  35.   {
  36.     /* normal demux */
  37.     /* first do a scaning to get all the services */
  38.     if (avio_seek(pb, pos, SEEK_SET) < 0)
  39.     {
  40.       av_log(s, AV_LOG_ERROR, "Unable to seek back to the start\n");
  41.     }
  42.     /*
  43.      * 挂载了两个Section类型的过滤器,
  44.      * 其实在TS的两种负载中,section是PES的元数据,
  45.      * 只有先解析了section,才能进一步解析PES数据,因此先挂上section的过滤器。
  46.      */
  47.     mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
  48.     mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
  49.     /*
  50.      */ 
  51.     handle_packets(ts, s->probesize / ts->raw_packet_size);
  52.     /* if could not find service, enable auto_guess */
  53.     ts->auto_guess = 1;
  54.     av_dlog(ts->stream, "tuning done\n");
  55.     s->ctx_flags |= AVFMTCTX_NOHEADER;
  56.   } 
  57.   else 
  58.   {
  59.     ...
  60.   }
  61.   avio_seek(pb, pos, SEEK_SET); 
  62.   return 0;
  63. fail:
  64.   return -1;
  65. }

  1. MpegTSFilter *mpegts_open_section_filter(MpegTSContext* ts, 
  2.                                          unsigned int pid,
  3.                                          SectionCallback* section_cb, 
  4.                                          void* opaque,
  5.                                          int check_crc)
  6. { 
  7.   MpegTSFilter *filter;
  8.   MpegTSSectionFilter *sec; 
  9.   av_dlog(ts->stream, "Filter: pid=0x%x\n", pid);
  10.   if (pid >= NB_PID_MAX || ts->pids[pid])
  11.     return NULL;
  12.   filter = av_mallocz(sizeof(MpegTSFilter));
  13.   if (!filter)
  14.     return NULL;
  15.   ts->pids[pid] = filter;
  16.   filter->type = MPEGTS_SECTION;
  17.   filter->pid = pid; 
  18.   filter->last_cc = -1;
  19.   sec = &filter->u.section_filter;
  20.   sec->section_cb = section_cb;
  21.   sec->opaque = opaque;
  22.   sec->section_buf= av_malloc(MAX_SECTION_SIZE);
  23.   sec->check_crc = check_crc;
  24.   if (!sec->section_buf) 
  25.   {
  26.     av_free(filter);
  27.     return NULL;
  28.   }
  29.   return filter;
  30. }
对于这部分代码,需要分析数据结构的定义:
依次为:
struct MpegTSContext;
|
V
struct MpegTSFilter;
|
V
+--------------+---------------+
|                              |
V                              V
MpegTSPESFilter        MpegTSSectionFilter
就是struct MpegTSContext;中有NB_PID_MAX(8192)个TS的Filter,
而每个struct MpegTSFilter
可能是 PES    的Filter
或者是 Section的Filter。
为什么NB_PID_MAX 是 8192,
需要看TS的语法结构(ISO/IEC 138138-1 page 19):

 

  1. Syntax                          No. of bits         Mnemonic
  2. transport_packet(){ 
  3.   sync_byte                        8                 bslbf
  4.   transport_error_indicator        1                 bslbf
  5.   payload_unit_start_indicator     1                 bslbf
  6.   transport_priority               1                 bslbf
  7.   PID                              13                uimsbf
  8.   transport_scrambling_control     2                 bslbf
  9.   adaptation_field_control         2                 bslbf
  10.   continuity_counter               4                 uimsbf
  11.   if (adaptation_field_control=='10' || 
  12.       adaptation_field_control=='11' )
  13.   { 
  14.         adaptation_field() 
  15.   } 
  16.        
  17.   if (adaptation_field_control=='01' || 
  18.       adaptation_field_control=='11' ) 
  19.   { 
  20.     for (i=0;i<N;i++)
  21.     { 
  22.       data_byte                     8                bslbf
  23.     } 
  24.   } 
  25. }
而8192,是2^13=8192(PID)的最大数目,
为什么会有PES和Section的区分,更详细的可以参考ISO/IEC-13818-1.
挂载上了两种section过滤器,如下:
=========================================================================
PID                |Section Name           |Callback
=========================================================================
SDT_PID(0x0011)    |ServiceDescriptionTable|sdt_cb
|                       |
PAT_PID(0x0000)    |ProgramAssociationTable|pat_cb
=========================================================================
设计成回调函数,是为了在后面使用。
4. MPEG2-TS的包处理

  1. int handle_packets(MpegTSContext *ts, int nb_packets)
  2. {
  3.   AVFormatContext *s = ts->stream;
  4.   uint8_t packet[TS_PACKET_SIZE];
  5.   int packet_num, ret;
  6.      
  7.   ts->stop_parse = 0;
  8.   packet_num = 0;
  9.   for ( ; ; ) 
  10.   {
  11.     packet_num++;
  12.     
  13.     if (nb_packets != 0 && packet_num >= nb_packets ||
  14.         ts->stop_parse > 1) 
  15.     {
  16.       ret = AVERROR(EAGAIN);
  17.       break;
  18.     }
  19.     if (ts->stop_parse > 0)
  20.       break;
  21.         
  22.     ret = read_packet(s, packet, ts->raw_packet_size);
  23.     if (ret != 0)
  24.       return ret;
  25.     ret = handle_packet(ts, packet);
  26.     if (ret != 0)
  27.       return ret;
  28.   } 
  29.   
  30.   return 0; 
  31. }
它的代码结构很简单:
handle_packets()
|
+->read_packet()
|
+->handle_packet()
|
+->write_section_data()
read_packet(),  很简单, 就是去找sync_byte(0x47),
handle_packet(),是真正处理数据的地方.它的代码如下:
  1. /* 
  2.  * 功能: handle one TS packet 
  3.  */
  4. int handle_packet(MpegTSContext *ts, const uint8_t *packet)
  5. {
  6.   AVFormatContext *s = ts->stream;
  7.   MpegTSFilter *tss;
  8.   int len, pid, cc, expected_cc, cc_ok, afc, is_start;
  9.   const uint8_t *p, *p_end;
  10.   int64_t pos;
  11.   /* 获取该包的PID */
  12.   pid = AV_RB16(packet + 1) & 0x1fff;
  13.   if (pid && discard_pid(ts, pid))
  14.      return 0;
  15.   /* 
  16.    * 是否是PES或者Section的开头
  17.    * 即syntax element: payload_unit_start_indicator 
  18.    */
  19.   is_start = packet[1] & 0x40;
  20.   tss = ts->pids[pid];
  21.   /* 
  22.    * ts->auto_guess此时为0,因此不考虑下面的代码
  23.    */
  24.   if (ts->auto_guess && tss == NULL && is_start) 
  25.   {
  26.     add_pes_stream(ts, pid, -1);
  27.     tss = ts->pids[pid];
  28.   }
  29.   if (!tss)
  30.     return 0;
  31.   /* 
  32.    * continuity check (currently not used) 
  33.    * 虽然检查,但不利用检查的结果
  34.    */
  35.   cc = (packet[3] & 0xf);
  36.   expected_cc = (packet[3] & 0x10) ? (tss->last_cc + 1) & 0x0f : tss->last_cc;
  37.   cc_ok = (tss->last_cc < 0) || (expected_cc == cc);
  38.   tss->last_cc = cc;
  39.   /* 
  40.    * 解析 adaptation_field_control 语法元素
  41.    * =======================================================
  42.    * 00 | Reserved for future use by ISO/IEC
  43.    * 01 | No adaptation_field, payload only
  44.    * 10 | Adaptation_field only, no payload
  45.    * 11 | Adaptation_field follwed by payload
  46.    * =======================================================
  47.    */ 
  48.   afc = (packet[3] >> 4) & 3;
  49.   p = packet + 4;
  50.   if (afc == 0) /* reserved value */
  51.     return 0; 
  52.   if (afc == 2) /* adaptation field only */ 
  53.     return 0;
  54.   if (afc == 3) 
  55.   {
  56.     /* 
  57.      * 跳过 adapation field 
  58.      * p[0]对应的语法元素为: adaptation_field_length
  59.      */
  60.     p += p[0] + 1;
  61.   }
  62.   /* 
  63.    * if past the end of packet, ignore 
  64.    * p已近到达TS包中的有效负载的地方
  65.    */
  66.   p_end = packet + TS_PACKET_SIZE;
  67.   if (p >= p_end)
  68.     return 0;
  69.   pos = avio_tell(ts->stream->pb);
  70.   ts->pos47= pos % ts->raw_packet_size;
  71.   if (tss->type == MPEGTS_SECTION) 
  72.   {
  73.     /*
  74.      * 针对Section, 第一个字节对应的语法元素为:pointer_field(见2.4.4.1),
  75.      * 它表示在当前TS包中,从pointer_field开始到第一个section的第一个字节间的字节数。
  76.      * 当TS包中有至少一个section的起始时,
  77.      *    payload_unit_start_indicator = 1 且 TS负载的第一个字节为pointer_field;
  78.      *    pointer_field = 0x00时,表示section的起始就在这个字节之后;
  79.      * 当TS包中没有section的起始时, 
  80.      *    payload_unit_start_indicator = 0 且 TS负载中没有pointer_field;
  81.      */
  82.     if (is_start) 
  83.     {
  84.       /* pointer field present */
  85.       len = *p++;
  86.       if (p + len > p_end)
  87.         return 0;
  88.       if (len && cc_ok) 
  89.       {
  90.         /* 
  91.          * write remaining section bytes 
  92.          * TS包的负载部分由Section A的End部分和Section B的Start组成,
  93.          * 先把Section A的End部分写入
  94.          */
  95.         write_section_data(s, tss, p, len, 0);
  96.         /* check whether filter has been closed */
  97.         if (!ts->pids[pid])
  98.           return 0;
  99.       }
  100.       p += len;
  101.       if (p < p_end) 
  102.       { 
  103.         /*
  104.          * 再将Section B的Start部分写入
  105.          */
  106.         write_section_data(s, tss, p, p_end - p, 1);
  107.       }
  108.     } 
  109.     else 
  110.     {
  111.       /* TS包负载仅是一个Section的中间部分部分,将其写入*/
  112.       if (cc_ok) 
  113.       {
  114.         write_section_data(s, tss, p, p_end - p, 0);
  115.       }
  116.     }
  117.   } 
  118.   else 
  119.   {
  120.     int ret;
  121.     /* 
  122.      * 如果是PES类型,直接调用其Callback,
  123.      * 但显然,只有Section部分解析完成后才可能解析PES
  124.      */
  125.     // Note: The position here points actually behind the current packet.
  126.     if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start,
  127.         pos - ts->raw_packet_size)) < 0)
  128.       return ret;
  129.   }
  130.   return 0;
  131. }
write_section_data()函数:
反复收集buffer中的数据,指导完成相关Section的重组过程,
然后调用之前注册的两个section_cb.
5. 节目指定信息的解析

  1. /*
  2.  * PAT(Program Association Table) 节目相关表
  3.  * 提供了节目号与PID值的对应关系
  4.  * 见ISO/IEC 13818-1 2.4.4.3 Table 2-30
  5.  */
  6. void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len);
  7. /*
  8.  * PMT(Program Map Table) 节目映射表
  9.  * 提供了节目号与组成节目的元素之间的映射关系--或者称为"节目定义"
  10.  * 见ISO/IEC 13818-1 2.4.4.8 Table 2-33
  11.  */
  12. void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len);
  13. /*
  14.  * SDT(Transport Stream Description Table) TS描述表
  15.  * 用于定义TS描述子的表
  16.  * 见ISO/IEC 13818-1 2.4.4.12 Table 2-36
  17.  */
  18. void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
6. 解析PES包
  1. /* 
  2.  * 见ISO/IEC 13818-1 2.4.3.6 Table 2-21
  3.  */
  4. int mpegts_push_data(MpegTSFilter* filter,
  5.                      const uint8_t* buf, 
  6.                      int buf_size, 
  7.                      int is_start,
  8.                      int64_t pos);
至此,整个TS层的解析基本完成。

转载于:https://www.cnblogs.com/yulang314/p/3737854.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546561.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python爬虫 asyncio aiohttp aiofiles 单线程多任务异步协程爬取图片

python爬虫 asyncio aiohttp aiofiles 多任务异步协程爬取图片 main.py """ coding: UTF8 """ # requests.get()同步代码 -> 异步操作aiohttp import asyncio import aiohttp import aiofilesurls ["https://img.lianzhixiu.com/uploa…

IOS 设置文件是否使用ARC

全项目设置&#xff1a;Building Settings修改Objective-C Automatic Reference Counting设置是否使用 单文件设置&#xff1a;Build Phases-Compile Sources.找到对应文件&#xff0c;双击后输入下面的选项 -fobjc-arc 使用arc -fno-objc-arc …

面试题:如何设计一个高并发的系统?

这道面试题涉及的知识点比较多&#xff0c;主要考察的是面试者的综合技术能力。高并发系统的设计手段有很多&#xff0c;主要体现在以下五个方面。1、前端层优化① 静态资源缓存&#xff1a;将活动页面上的所有可以静态的元素全部静态化&#xff0c;尽量减少动态元素&#xff1…

python爬虫lxml xpath测试

xpath测试1&#xff1a; main.py """ coding: UTF8 """ from lxml import etreexml """ <book><id>1</id><name>春风</name><price>1.56</price><nick>万里</nick><…

命令行下Apache日志统计举例

Apache日志统计举例加些来了解一下如何统计Apache的访问日志&#xff0c;一般可以用tail命令来实时查看日志文件变化&#xff0c;但是各种的应用系统中的日志会非常复杂&#xff0c;一堆长度超过你浏览极限的日志出现在你眼前时&#xff0c;你会觉得非常无奈&#xff0c;怎么办…

漫画:什么是中台?

没有中台的时代在传统IT企业&#xff0c;项目的物理结构是什么样的呢&#xff1f;无论项目内部的如何复杂&#xff0c;都可分为“前台”和“后台”这两部分。什么是前台&#xff1f;首先&#xff0c;这里所说的“前台”和“前端”并不是一回事。所谓前台即包括各种和用户直接交…

python lxml xpath爬取图片代码

main.py """=== coding: UTF8 ===""" import requests from lxml import etree import os import timeclass Mzitu:def __init__(self):"""@function:初始化@parameter:无@return:无"""# 首页self

MySQL 面试,必须掌握的 8 大核心点

上周末和在北京的哥们国仔涮火锅&#xff0c;席间聊起了最近面试的经历。他说想换工作的原因很简单&#xff0c;就是要涨工资&#xff0c;原来的公司呆了两年多&#xff0c;薪资浮动不超过 500 元。而身边跳槽的那些同事&#xff0c;薪资都已经是自己的 2 倍了。在准备面试的过…

基于小波变换的数字图像处理(MATLAB源代码)

基于小波变换的数字图像处理&#xff08;MATLAB源代码&#xff09; clear all; close all; clc;M256;%原图像长度N64; %水印长度[filename1,pathname]uigetfile(*.*,select the image); image1imread(num2str(filename1));subplot(2,2,1);imshow(image1); title(original image…

python Chrome + selenium自动化测试与python爬虫获取网页数据

一、使用PythonseleniumChrome 报错&#xff1a; selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 78 说明&#xff0c;这个chrom驱动支持78版本 谷歌浏览器版本 进入驱动…

[Python]网络爬虫(七):Python中的正则表达式教程(转)

接下来准备用糗百做一个爬虫的小例子。 但是在这之前&#xff0c;先详细的整理一下Python中的正则表达式的相关内容。 正则表达式在Python爬虫中的作用就像是老师点名时用的花名册一样&#xff0c;是必不可少的神兵利器。 以下内容转自CNBLOG&#xff1a;http://www.cnblogs.co…

八种常见的 SQL 错误用法

来源&#xff1a;https://dwz.cn/cgAPOWPx1、LIMIT 语句分页查询是最常用的场景之一&#xff0c;但也通常也是最容易出问题的地方。比如对于下面简单的语句&#xff0c;一般 DBA 想到的办法是在 type, name, create_time 字段上加组合索引。这样条件排序都能有效的利用到索引&a…

python 多线程 popen ping指定IP是否在线 判断连通

问题描述 在某些问题背景下,需要确认是否多台终端在线,也就是会使用我们牛逼的ping这个命令,做一些的ping操作,如果需要确认的设备比较少,也还能承受。倘若,在手中维护的设备很多,那么这无疑会变成一个恼人的问题。脚本的作用就凸显了。另外,我们需要使用多线程的一种措…

最快速度找到内存泄漏

2019独角兽企业重金招聘Python工程师标准>>> 确认是否存在内存泄漏 我们知道&#xff0c;MFC程序如果检测到存在内存泄漏&#xff0c;退出程序的时候会在调试窗口提醒内存泄漏。例如&#xff1a; class CMyApp : public CWinApp { public:BOOL InitApplication(){in…

如何手撸一个队列?队列详解和面试题汇总(含答案)

队列&#xff08;Queue&#xff09;&#xff1a;与栈相对的一种数据结构&#xff0c; 集合&#xff08;Collection&#xff09;的一个子类。队列允许在一端进行插入操作&#xff0c;而在另一端进行删除操作的线性表&#xff0c;栈的特点是后进先出&#xff0c;而队列的特点是先…

Python根据IP地址获取MAC地址

Python3根据IP地址获取MAC地址&#xff08;不能获取本机IP&#xff0c;可以获取与本机同局域网设备IP的MAC&#xff09; main.py #!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import platform import reclass IP2MAC:"""Python3根据IP地址获取…

eclipse启动tomcat 404

eclipse启动tomcat 404 引用自&#xff1a;http://blog.sina.com.cn/s/blog_8020e41101014lvu.html 感谢博主 状态描述&#xff1a; 在eclipse中启动Tomcat后&#xff0c;在浏览器中访问localhost:8080&#xff0c;显示404错误。 在%tomcat_home%/bin中启动startup.sh却可以正常…

漂亮又好用的Redis可视化客户端汇总

因为 Redis 官方只提供了命令行版的 Redis 客户端 redis-cli&#xff0c;以至于我们在使用的时候会比较麻烦&#xff0c;通常要输入一堆命令&#xff0c;而且命令行版的客户端看起来也不够直观&#xff0c;基于以上两个原因我们需要找一个可视化的 Redis 客户端&#xff0c;下面…

python基本的信号与槽函数的使用 信号发射 槽函数接收

# 熟悉信号与槽的使用 # -*- coding: utf-8 -*- from PyQt5.QtWidgets import * from PyQt5.QtCore import * import sys# 创建信号类 class QTypeSigner(QObject):# 定义一个信号sendmsg pyqtSignal(object)def __init__(self):super(QTypeSigner, self).__init__()def run(s…

Linux 高性能服务器编程——多线程编程

问题聚焦&#xff1a;在简单地介绍线程的基本知识之后&#xff0c;主要讨论三个方面的内容&#xff1a; 1 创建线程和结束线程&#xff1b; 2 读取和设置线程属性&#xff1b; 3 线程同步方式&#xff1a;POSIX信号量&#xff0c;互斥锁和条件变量。Linux线程概述线程…