首页 > 其他分享 >手写解复用(一):TS解析

手写解复用(一):TS解析

时间:2024-03-12 20:31:30浏览次数:24  
标签:MAX unsigned TS tsHeader 复用 type 手写 data

1、本文的基本作用:引导理解封装格式,但更重要的是要自己去阅读协议,上手实践;
2、本文的最大价值:久之,皆已遗忘;顾之,皆已忆起。

一、基本知识

1、TS Header

2、PAT

3、PMT

二、重点代码

1、解析TS Header

TS码流一般为188个字节为一包,以同步字节0x47开头,头上有4个字节,按上面格式解析就行了。

int main(int argc, char *argv[])
{
    if (MAX_TS_PACKET_LEN != fread(tsPacket, 1, MAX_TS_PACKET_LEN, fp)) {
         break;
    }
        
    ParseTsHeader(tsPacket, &tsPacketHeader);
}
void ParseTsHeader() {
    tsHeader.sync_byte = data[0];
    tsHeader.transport_error_indictor = ((data[1]>>7)&0x1);
    tsHeader.playload_unit_start_indictor = ((data[1]>>6)&0x1);
    tsHeader.transport_priority = ((data[1]>>5)&0x1);
    tsHeader.pid = (((data[1]&0x1f)<<8) | data[2]);
    tsHeader.transport_scrambling_control = ((data[3]>>6)&0x3);
    tsHeader.adaptation_field_control = ((data[3]>>4)&0x3);
    tsHeader.continuity_counter = data[3]&0xf;
}

2、解析PAT

PAT表的PID为0x00,找到PAT才能进行后续的TS的解析,PAT表里主要描述了PMT表的PID。

void ParseTsPat() {
    pat.programs[i].program_number = ((data[0]<<8) | data[1]);
        
    if (0 != pat.programs[i].program_number) {
        pat.programs[i].program_map_pid = (((data[2]&0x1f)<<8) | data[3]);
    }
}

3、解析PMT

通过PAT找到了PMT的PID之后,就能找到并解析PMT了。PMT表里主要描述了音视频的节目信息(PID、类型等等),通过这个就能定位找到到音频、视频、字幕等流数据了。

void ParseTsPmt() {
    pmt.streams[i].stream_type = data[0];
    pmt.streams[i].elementary_pid = (((data[1]&0x1f)<<8) | data[2]);
        
    es_info_length = ((data[3]&0xf)<<8) | data[4];
}

4、PES头解析

TS承载的音视频流信息叫PES,头上主要有PTS、DTS等信息。

void ParsePes() {
    getPdts()
}

void getPdts() {
    pts = (((pdtsData[0]>>1) & 0x7) << 30) | (pdtsData[1] << 22) |         (((pdtsData[2]>>1) & 0x7f) << 15) | (pdtsData[3] << 7) | (pdtsData[4]>>1 & 0x7f);

    pts2Ms = pts/90;

    hour = pts2Ms/(60*60*1000);
    minute = (pts2Ms - hour * (60*60*1000)) / (60*1000);
    second = (pts2Ms - hour * (60*60*1000) - minute * (60*1000)) / 1000;
    msecond = pts2Ms - hour * (60*60*1000) - minute * (60*1000) - second * 1000;
}

三、完整源码

纯C代码,gcc编译就能直接运行,打印格式模仿Elecard的形式。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TAB44 "    "
#define TAB46 "      "
#define PRINTF_DEBUG

#define TS_PAT_PID 0x0

#define TS_PAT_TABLE_PID 0x0
#define TS_PMT_TABLE_PID 0x2

#define MAX_PDTS_LEN 5
#define MAX_TS_PROGRAM_NUM 8
#define MAX_TS_STREAM_NUM 8
#define MAX_PDTS_STRING_LEN 12
#define MAX_TS_PACKET_LEN 188 /* 204, 188+16字节的CRC */


/******************************************************************
视频
MPEG-1 Video:stream_type为0x01
MPEG-2 Video:stream_type为0x02
AVC(H264):stream_type为0x1b
VC-1:stream_type为0xea
HEVC: stream_type为0x24

音频
Mpeg-1 Audio:stream_type为0x03
Mpeg-2 Audio:stream_type为0x04
Mpeg-2 AAC:stream_type为0x0f
Mpeg-4 AAC:stream_type为0x11
LPCM:stream_type为0x80
AC3:stream_type为0x81或0x06
DTS:stream_type为0x82
Dolby TrueHD:stream_type为0x83
AC3-Plus:stream_type为0x84
DTS_HD:stream_type为0x85
DTS-MA:stream_type为0x86
AC3-Plus_SEC:steam_type为0xa1
DTS_HD_SEC:stream_type为0xa2

字幕
PGS:stream_type为0x90
IGS:steam_type为0x91,暂不支持
Text Subtitle:stream_type为0x92
********************************************************************/
typedef enum t_ts_stream_type {
    E_STREAM_TYPE_MPEG1_VIDEO           = 0x01,
    E_STREAM_TYPE_MPEG2_VIDEO           = 0x02,
    E_STREAM_TYPE_AVC_VIDEO             = 0x1B,
    E_STREAM_TYPE_VC1_VIDEO             = 0xEA,
    E_STREAM_TYPE_MPEG1_AUDIO           = 0x03,
    E_STREAM_TYPE_MPEG2_AUDIO           = 0x04,
    E_STREAM_TYPE_MPEG2_AAC             = 0x0F,
    E_STREAM_TYPE_MPEG4_AAC             = 0x11,
    E_STREAM_TYPE_AC3                   = 0x81,
} T_TS_STREAM_TYPE;

/* 4 bytes */
typedef struct t_ts_packet_header {
    unsigned char sync_byte;
    unsigned short transport_error_indictor:1, playload_unit_start_indictor:1, transport_priority:1, pid:13;
    unsigned char transport_scrambling_control:2, adaptation_field_control:2, continuity_counter:4;
} T_TS_PACKET_HEADER;

/* PAT */
typedef struct t_ts_program {
    unsigned short program_number;
    unsigned short program_map_pid;
} T_TS_PROGRAM;

typedef struct t_ts_pat {
    unsigned char table_id;
    unsigned short section_len;
    unsigned char version_num:5;
    unsigned short programNum;

    T_TS_PROGRAM programs[MAX_TS_PROGRAM_NUM];
} T_TS_PAT;

/* PMT */
typedef struct t_ts_stream {
    unsigned char stream_type;
    unsigned short elementary_pid;
} T_TS_STREAM;

typedef struct t_ts_pmt {
    unsigned short pmtIsFind;
    unsigned char table_id;
    unsigned short section_len;
    unsigned short program_number;
    unsigned char version_num:5;
    unsigned short program_info_length;

    unsigned short streamNum;

    T_TS_STREAM streams[MAX_TS_STREAM_NUM];
} T_TS_PMT;

/* PES */
typedef struct t_ts_pes {
    unsigned char streamId;

    unsigned short pesLength;

    long long pts;
    long long dts;

    unsigned char ptsStr[MAX_PDTS_STRING_LEN+1];
    unsigned char dtsStr[MAX_PDTS_STRING_LEN+1];

    unsigned char pesHeaderLen;
} T_TS_PES;

T_TS_PAT g_TsPat = {0};
T_TS_PMT g_TsPmt[MAX_TS_PROGRAM_NUM] = {0};

static void inline printfNSpace(int num) {
    printf("%*s", num, " ");
}

static void ParseTsHeader(unsigned char* const headerData, T_TS_PACKET_HEADER *tsPacketHeader) {
    static int tsPacketNum = 0;

    int offset = 0;

    unsigned char *data = NULL;

    T_TS_PACKET_HEADER tsHeader = {0};

    memset(&tsHeader, 0x0, sizeof(tsHeader));

    data = headerData;

    tsHeader.sync_byte = data[0];
    tsHeader.transport_error_indictor = ((data[1]>>7)&0x1);
    tsHeader.playload_unit_start_indictor = ((data[1]>>6)&0x1);
    tsHeader.transport_priority = ((data[1]>>5)&0x1);
    tsHeader.pid = (((data[1]&0x1f)<<8) | data[2]);
    tsHeader.transport_scrambling_control = ((data[3]>>6)&0x3);
    tsHeader.adaptation_field_control = ((data[3]>>4)&0x3);
    tsHeader.continuity_counter = data[3]&0xf;

    memcpy(tsPacketHeader, &tsHeader, sizeof(tsHeader));

#ifdef PRINTF_DEBUG
    offset = tsPacketNum*MAX_TS_PACKET_LEN;

    switch (tsHeader.adaptation_field_control) {
        case 1:
            if (tsHeader.playload_unit_start_indictor) {
                printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d, Start indicactor}\n", 
                        offset, tsHeader.pid, MAX_TS_PACKET_LEN-4, tsHeader.continuity_counter);
            } else {
                printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d}\n", 
                        offset, tsHeader.pid, MAX_TS_PACKET_LEN-4, tsHeader.continuity_counter);
            }
            
            break;
            
        case 2:
            if (tsHeader.playload_unit_start_indictor) {
                printf("0x%08x Transport Packet{PID = 0x%x, Payload = NO, Counter = %d, Start indicactor}\n", 
                        offset, tsHeader.pid, tsHeader.continuity_counter);
            } else {
                printf("0x%08x Transport Packet{PID = 0x%x, Payload = NO, Counter = %d}\n", 
                        offset, tsHeader.pid, tsHeader.continuity_counter);
            }
            
            break;
            
        case 3:
            if (tsHeader.playload_unit_start_indictor) {
                printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d, Start indicactor}\n", 
                        offset, tsHeader.pid, MAX_TS_PACKET_LEN-4-1-data[4], tsHeader.continuity_counter);
            } else {
                printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d}\n", 
                        offset, tsHeader.pid, MAX_TS_PACKET_LEN-4-1-data[4], tsHeader.continuity_counter);
            }
            
            break;
            
        default:
            break;
    }

    tsPacketNum++;
#endif
}

static void ParseTsPat(unsigned char* const patData, T_TS_PAT *tsPat) {
    int i = 0;
    int sectionLen = 0;

    unsigned char *data = NULL;

    T_TS_PAT pat = {0};

    memset(&pat, 0x0, sizeof(pat));

    data = patData;

    pat.table_id = data[0];

    if (TS_PAT_TABLE_PID != pat.table_id) {
        return;
    }

    sectionLen = ((data[1]&0xf)<<8) | data[2];
    pat.section_len = sectionLen;

    pat.version_num = ((data[5]>>1) & 0x1f);

    data += 8;
    sectionLen -= (5+4); /* len is after section_len and not have crc */

    while (sectionLen>0) {
        if (i >= MAX_TS_PROGRAM_NUM) {
            break;
        }
        
        pat.programs[i].program_number = ((data[0]<<8) | data[1]);
        
        if (0 != pat.programs[i].program_number) {
            pat.programs[i].program_map_pid = (((data[2]&0x1f)<<8) | data[3]);
        }
        
        data += 4;
        sectionLen -= 4;
        
        i++;
        
        pat.programNum = i;
    }

    memcpy(tsPat, &pat, sizeof(pat));

#ifdef PRINTF_DEBUG
    printfNSpace(11);
    printf("Program Association Table, version: %d\n", pat.version_num);

    for (i=0; i<pat.programNum; i++) {
        printfNSpace(15);
        printf("program_number: %d, program_map_PID: 0x%x\n", pat.programs[i].program_number, pat.programs[i].program_map_pid);
    }
#endif
}

static void ParseTsPmt(unsigned char* const pmtData, T_TS_PMT *tsPmt) {
    int i = 0;
    int sectionLen = 0;
    int program_info_length = 0;
    int es_info_length = 0;

    unsigned char *data = NULL;

    T_TS_PMT pmt = {0};

    memset(&pmt, 0x0, sizeof(pmt));

    data = pmtData;

    pmt.table_id = data[0];

    if (TS_PMT_TABLE_PID != pmt.table_id) {
        return;
    }

    sectionLen = ((data[1]&0xf)<<8) | data[2];
    pmt.section_len = sectionLen;

    pmt.program_number = data[3]<<8 | data[4];

    pmt.version_num = ((data[5]>>1) & 0x1f);

    data += 10;
    sectionLen -= (7+4);

    program_info_length = ((data[0]&0xf)<<8) | data[1];

    data += 2;
    sectionLen -= 2;

    data += program_info_length;
    sectionLen -= program_info_length;

    while (sectionLen > 0) {
        if (i >= MAX_TS_STREAM_NUM) {
            break;
        }
        
        pmt.streams[i].stream_type = data[0];
        pmt.streams[i].elementary_pid = (((data[1]&0x1f)<<8) | data[2]);
        
        es_info_length = ((data[3]&0xf)<<8) | data[4];

        data += 5;
        sectionLen -= 5;
        
        data += es_info_length;
        sectionLen -= es_info_length;
        
        i++;
        
        pmt.streamNum = i;
    }

    pmt.pmtIsFind = 1;

    memcpy(tsPmt, &pmt, sizeof(pmt));

#ifdef PRINTF_DEBUG
    printfNSpace(11);
    printf("Program Map Table, version: %d\n", pmt.version_num);

    for (i=0; i<pmt.streamNum; i++) {
        printfNSpace(15);
        printf("stream_type: 0x%x(%d), elementary_pid: 0x%x(%d)\n", pmt.streams[i].stream_type, pmt.streams[i].stream_type,
            pmt.streams[i].elementary_pid, pmt.streams[i].elementary_pid);
    }
#endif
}

static void getPdts(unsigned char *pdtsData, long long *pdts, unsigned char *pdtsString) {
    int hour = 0;
    int minute = 0;
    int second = 0;
    int msecond = 0;

    long long pts = 0;
    long long pts2Ms = 0;

    unsigned char ptsStr[MAX_PDTS_STRING_LEN+1] = {0};

    /* 5个字节转33位的值 */
    pts = (((pdtsData[0]>>1) & 0x7) << 30) | (pdtsData[1] << 22) | (((pdtsData[2]>>1) & 0x7f) << 15) | (pdtsData[3] << 7) | (pdtsData[4]>>1 & 0x7f);

    /* 90KHz, 1000ms/90 */
    pts2Ms = pts/90;

    hour = pts2Ms/(60*60*1000);
    minute = (pts2Ms - hour * (60*60*1000)) / (60*1000);
    second = (pts2Ms - hour * (60*60*1000) - minute * (60*1000)) / 1000;
    msecond = pts2Ms - hour * (60*60*1000) - minute * (60*1000) - second * 1000;

    sprintf(ptsStr, "%02d:%02d:%02d:%03d", hour, minute, second, msecond);

    ptsStr[MAX_PDTS_STRING_LEN] = '\0';

    memcpy(pdtsString, ptsStr, MAX_PDTS_STRING_LEN);

    *pdts = pts;
}

/**************************************************************************************
1. 根据playload_unit_start_indictor=1, 只解析这个ts包(该字段指示section, pes等的起始标识, 
   若要拼包则根据这个字段, 一个整包结束这个字段会置0);
2. 这里暂时不获取所有的pes包去解析, 只解析PES的头;
3. 可能的问题是一个ts包可能有填充字段, 根据此字段的指示adaptation_field_control(判断有无填充),
   然后4字节包头后的第5个字节指示填充字段的长度, 若该长度大于一个ts包的长度, 则在此处是解析
   不到PES的数据的, 真正的PES数据应该在下一包;
4. adaptation_field_control(适配域控制): 表示包头是否有调整字段或有效负载.
    '00'为ISO/IEC未来使用保留;
    '01'仅含有效载荷, 无调整字段;
    '10'无有效载荷, 仅含调整字段;
    '11'调整字段后为有效载荷, 调整字段中的前一个字节表示调整字段的长度length, 有效载荷开始的位置应再偏移length个字节;
    空包应为'10'.
    ** 这个地方有一个未证实的(基本不会错), 记录下来: adapt=1时, 貌似对于PSI/SI数据在第5个字节会写一个00(代码处理的地方为main(),ParseTsPat(&tsPacket[4+1], &g_TsPat)), 
       对于pes数据若为1, 直接跟有效数据(代码处理的地方为ParsePes(), data += (4+1+data[4]).
5. 此处还有一个需说明的, 有时会发现packet_length为0. 这个比较特殊, 标准里有说明.
   因为pes_packet_length为16个字节, 最大只能支持到65535, 当一个pes包大于这个数值的时候,
   处理方法是这个值为0, 实际的大小由上层协议决定.(只有当ts传输pes的时候才能这么做,
   通过根据playload_unit_start_indictor=1/0就可以确定这个pes包的真实长度.)
6. 对于H264, 可能的就是将SPS, PPS等信息(数据量比较小)放到一个ts包中.
***************************************************************************************/
static void ParsePes(unsigned char* const pesData, T_TS_PACKET_HEADER* const tsHeader) {
    unsigned char pts_dts_flag;

    unsigned char *data = NULL;

    unsigned char pts[MAX_PDTS_LEN+1] = {0};
    unsigned char dts[MAX_PDTS_LEN+1] = {0};

    T_TS_PES pes = {0};

    data = pesData;

    memset(&pes, 0x0, sizeof(pes));

    /* deal adaptation */
    if ((0 == tsHeader->adaptation_field_control)
        || (2 == tsHeader->adaptation_field_control)) {
        return;
    }

    if (1 == tsHeader->adaptation_field_control) {
        data += 4;
    } else { /* 3 */ 
        /* header(4) + adaptlen(1) + adaptdata(adaptlen) */
        data += (4+1+data[4]);
    }

    data += 3; /* start code 00 00 01*/

    pes.streamId = data[0];
    pes.pesLength = (data[1]<<8) | data[2];

    data += 3; /* streamid(1) + pes_len(2) */

    pts_dts_flag = data[1]>>6 & 0x3;

    pes.pesHeaderLen = data[2];

    data += 3;

    switch (pts_dts_flag) {
        case 0: /* 00, no pts, dts */
            break;

        case 2: /* 10, only pts*/
            memset(pts, 0x0, sizeof(pts));

            memcpy(pts, data, MAX_PDTS_LEN);

            getPdts(pts, &pes.pts, pes.ptsStr);

            break;

        case 3: /* 11 pts & dts*/
            memset(pts, 0x0, sizeof(pts));
            memset(dts, 0x0, sizeof(dts));

            memcpy(pts, data, MAX_PDTS_LEN);
            memcpy(dts, data+MAX_PDTS_LEN, MAX_PDTS_LEN);

            getPdts(pts, &pes.pts, pes.ptsStr);
            getPdts(dts, &pes.dts, pes.dtsStr);

            break;

        default:
            break;
    }

#ifdef PRINTF_DEBUG
    if ((pes.streamId>=0xC0) && (pes.streamId<=0xDF)) {
        printfNSpace(11);
        printf("PES Packet(Audio) {stream_id = 0x%x}\n", pes.streamId);
    }

    if ((pes.streamId>=0xE0) && (pes.streamId<=0xEF)) {
        printfNSpace(11);
        printf("PES Packet(Video) {stream_id = 0x%x}\n", pes.streamId);
    }

    printfNSpace(15);
    printf("packet_length = %d, PES_header_data_length = %d\n", pes.pesLength, pes.pesHeaderLen);
    
    printfNSpace(17);
    printf("PTS: %s(%lld), DTS: %s(%lld)\n", pes.ptsStr, pes.pts, pes.dtsStr, pes.dts);
#endif

    /*
      1. todo: this test video is h264, parse pes data;
      2. get pes data, find h264 startcode, parse nual.
    */
}

int main(int argc, char *argv[])
{
    int i = 0;
    int j = 0;
    int k = 0;
    int patIsFind = 0;
    int allPmtIsFind = 0;

    unsigned char tsPacket[MAX_TS_PACKET_LEN] = {0};

    T_TS_PACKET_HEADER tsPacketHeader = {0};

    FILE *fp = NULL;

    if (2 != argc) {
        printf("Usage: tsparse **.ts\n");

        return -1;
    }

    memset(&g_TsPat, 0x0, sizeof(T_TS_PAT));
    memset(g_TsPmt, 0x0, sizeof(g_TsPmt));

    fp = fopen(argv[1], "rb");
    if (!fp) {
        printf("open file[%s] error!\n", argv[1]);

        return -1;
    }

    while (1) {
        memset(tsPacket, 0x0, MAX_TS_PACKET_LEN);
        memset(&tsPacketHeader, 0x0, sizeof(tsPacketHeader));
        
        if (MAX_TS_PACKET_LEN != fread(tsPacket, 1, MAX_TS_PACKET_LEN, fp)) {
            break;
        }
        
        ParseTsHeader(tsPacket, &tsPacketHeader);
        
        /* pat->pmt->(audio/video pid)->video data */
        if (0 == patIsFind) {
            if (TS_PAT_PID == tsPacketHeader.pid) {
                /* 4(header) + 1(adapt len)*/
                ParseTsPat(&tsPacket[4+1], &g_TsPat);
                
                patIsFind = 1;
            }
        }

        if ((1 == patIsFind) && (1 != allPmtIsFind)) {
            for (i=0; i<g_TsPat.programNum; i++) {
                if ((g_TsPat.programs[i].program_map_pid == tsPacketHeader.pid)
                    && (0 == g_TsPmt[j].pmtIsFind)) {
                    ParseTsPmt(&tsPacket[4+1], &g_TsPmt[j]);
                    
                    j++;
                }
            }
            
            for (i=0; i<g_TsPat.programNum; i++) {
                if (0 == g_TsPmt[i].pmtIsFind) {
                    break;
                }
            }
            
            if (i == g_TsPat.programNum) {
                allPmtIsFind = 1;
            }
        }
        
        if (allPmtIsFind) {
            for (i=0; i<g_TsPat.programNum; i++) {
                for (k=0; k<g_TsPmt[i].streamNum; k++) {
                    if ((g_TsPmt[i].streams[k].elementary_pid == tsPacketHeader.pid)
                        && (1 == tsPacketHeader.playload_unit_start_indictor)) {
                        ParsePes(tsPacket, &tsPacketHeader);
                    }
                }
            }
        }
    }

    fclose(fp);
}

标签:MAX,unsigned,TS,tsHeader,复用,type,手写,data
From: https://blog.csdn.net/xiaoyan19891227/article/details/136661297

相关文章

  • 第143篇:手写vue-router,实现router-view
    好家伙, 今天来手写我们的老伙计vue-router, 1.替换router新开一个项目,并使用我们手写的router 2.大致结构letVue;//保存vue的构造函数classVueRouter{constructor(options){}}VueRouter.install=(_Vue)=>{Vue=_Vue;//备......
  • TSINGSEE青犀煤矿矿井视频监控与汇聚融合管理视频监管平台建设方案
    一、背景需求随着我国经济的飞速发展,煤炭作为我国的主要能源之一,其开采和利用的重要性不言而喻。然而,煤矿事故频发,不仅造成了巨大的人员伤亡和财产损失,也对社会产生了深远的负面影响。视频监控系统作为实现煤矿智能化无人开采的关键系统与煤矿安全生产的多系统协同分析预处理的关......
  • TSINGSEE青犀视频AI方案:数据+算力+算法,人工智能的三大基石
    背景分析随着信息技术的迅猛发展,人工智能(AI)已经逐渐渗透到我们生活的各个领域,从智能家居到自动驾驶,从医疗诊断到金融风控,AI的应用正在改变着我们的生活方式。而数据、算法和算力,正是构成人工智能技术的三大核心要素,它们之间相互关联、相互影响,共同推动着人工智能的发展。1、数据......
  • vue 3+TS 封装自定义右键全局菜单(虚拟节点)
    有时我们需要点击(右键或左键)某个元素时弹出菜单,实现复制、粘贴、删除等功能。本文将介绍如何封装一个自定义的右键全局菜单(无三方依赖)。封装的菜单可自定义菜单项,图标,禁用,分割线,隐藏等。并且可以在全局任意地方使用。源码在文章末尾。效果使用<template><div><div@......
  • Rtsp转Flv在浏览器中播放
    目录概述环境项目目录清单项目搭建步骤引入相关npm依赖实例化一个express应用创建WebsocketServer并解析rtsp使用flv播放浏览器中测试代码引用概述众所周知,rtsp的流是无法在浏览器中播放的,这就导致海康摄像头、海康ISC等平台的视频流无法直接在浏览器中播放。当下是web最盛行的......
  • 深入解析:AntSK 0.1.7版本的技术革新与多模型管理策略
        在信息技术快速迭代的当下,.Net生态中的AntSK项目凭借其前沿的AI知识库和智能体技术,已经吸引了广大开发者的关注和参与。今天,我要给大家介绍的主角,AntSK0.1.7版本,无疑将是这个开源项目中的一次重大进步——多模型管理功能的引入,为使用者带来了更强大、更灵活的工具。......
  • 使用ts封装一个ajax
    练习使用ts的接口interface/***使用TS封装一个ajax*/interfaceConfig{type:string;url:string;data?:any;//可选dataType:string;}functionajax(config:Config){letxhr=newXMLHttpRequest();xhr.open(config.t......
  • Linux之ps -ef进程命令及netstat网络状态命令简记
    ps-ef释义:ps-ef表示查看全格式的全部进程。ps是linux下最常用的也是非常强大的进程查看命令,常配合管道命令|和查找命令grep同时执行来查看特定进程。参数含义:-e显示所有进程。-f全格式。-h不显示标题。-l长格式。-w宽输出。a显示终端上的所有进程,包括其他用户的......
  • ts跟react的学习点记录
    TS:1.ts与js的区别ts:js的超集用于解决大型项目的代码复杂性,可以在编辑期间发现并纠正错误,强类型,支持静态和动态类型,最终编译成js,使浏览器可以理解,支持模块、泛型和接口js:一种脚本语言,用于创建动态网页,作为一种解释型语言,只能在运行的时发现错误,不支持模块、泛型和接口react学习点:1......
  • python 使用PaddleOCR读取图片文字,并用pyttsx3转为音频
    python小白,纯纯小白,很久之前看了一遍菜鸟官网,但实在没有应用场景,所以过目即忘。最近工作不是很忙,给我出了个题目,觉得挺有意思,就玩一玩。  所以关键点就是,图片提取出文字,然后文字转音频。1.图片提取文字,PaddleOCR出题人士,给出了git上一个ocr的工具库,支持图文信息的抽取。用......