linux环境C语言实现:h264与pcm封装成AVI格式
前言
拖了很久的AVI音视频封装实例,花了一天时间终于调完了,兼容性不是太好,但作为参考学习使用应该没有问题。
RIFF和AVI以及WAV格式,可以参考前面的一些文章。这里详细介绍将一个H264视频流和一个2通道PCM音频流封装成一个AVI格式的视频文件。测试过可以在暴风,迅雷和VLC播放器上正常播放。
如果用第三方工具或是第三方库合成AVI文件,其实是很简单的事,这里不做介绍。如果是需要自己用代码合成AVI文件,我建议一般的步骤是:
- (1)将一个视频流封装成AVI文件
- (2)将一个视频流和一个音频流合成一个AVI文件
- (3)为AVI文件添加索引信息
之所以要分开来做,主要也是方便调试和定位,AVI的中文资料较少,而且它的参数也是比较的复杂。索引信息是为了视频播放时候的快进快退和拖动用的,如果没有这需求也可以不加。
在这里,我使用一个h264文件来模拟视频流,使用一个PCM文件来模拟音频流。视频流和音频流参数可以见下图:
h264媒体信息

PCM音频信息

完整工程文件目录如下:
biao@ubuntu:~/test/AVI_Create$ make clean
rm -f -f *.o test
biao@ubuntu:~/test/AVI_Create$ tree
.
├── avilib.c
├── avilib.h
├── File
│ ├── 2Channel_44100_16bit.pcm
│ ├── out.h264
│ ├── test.avi
│ └── test.h264
├── main.c
├── Makefile
├── MediaStream.c
└── MediaStream.h
1 directory, 10 files
biao@ubuntu:~/test/AVI_Create$
avilib是用来封装AVI格式,MediaStream用来模拟音视频数据流,main函数里做主要的控制。代码较多,先上main函数代码,完整的工程代码下面有下载链接。
/************************************************************
*FileName: main.h
*Date: 2018-07-15
*Author: Wen Lee
*Version: V1.0
*Description:
*Others:
*History:
***********************************************************/
#include <stdlib.h>
#include <stdio.h>
#include "avilib.h"
#include "MediaStream.h"
#define WRITE_FILE_NAME "./File/out.h264"
#define WRITE_AVI_NAME "./File/test.avi"
#define AVI_VIDEO_FLAG "00dc"
#define AVI_AUDIO_FLAG "01wb"
static FILE *g_out_h264_fd = NULL;
static FILE *g_out_avi_fd = NULL;
int CreatOuth264File(void)
{
g_out_h264_fd = fopen(WRITE_FILE_NAME,"w+");
if(NULL==g_out_h264_fd)
{
printf("%s %d fopen error \n",__FILE__,__LINE__);
return -1;
}
return 0;
}
int CreatAVIFile(void)
{
g_out_avi_fd = fopen(WRITE_AVI_NAME,"w+");
if(NULL==g_out_avi_fd)
{
printf("%s %d fopen error \n",__FILE__,__LINE__);
return -1;
}
return 0;
}
/*************************************************
Function: ExportCalcAlign
Description: Data alignment
Input: u32Data, u8Align
OutPut:
Return: Aligned data
Others:
Author: Caibiao Lee
Date: 2018-03-06
*********************************************************/
unsigned int AVICalcAlign(unsigned int u32Data,unsigned char u8Align)
{
return ((u32Data + u8Align - 1) & (~(u8Align - 1)));
}
/********************************************************
Function: TestH264ReadAndWrite
Description:将h264文件一帧一帧的读取出来,
然后再一帧一帧的把数据写入到另外一文件
用来测试模拟的视频流是否正确
Input: None
OutPut: None
Return: 0: success,none 0:error
Others:
Author: Wen Lee
Date: 2018.07.15
*********************************************************/
int TestH264ReadAndWrite(void)
{
int l_s32Ret = 0;
int l_DateLen = 0;
NALU_S * l_pstNalu = NULL;
/**创建h264输出文件**/
l_s32Ret = CreatOuth264File();
if(0!=l_s32Ret)
{
return -1;
}
/**初始化视频流数据**/
l_pstNalu = InitVideoStream(MAX_VIDEO_SIZE);
if(NULL == l_pstNalu)
{
return -1;
}
/**读取写入整个文件**/
while(1)
{
l_DateLen = ReadOneFrameVideo(l_pstNalu);
if(l_DateLen > 0)
{
l_s32Ret = fwrite(l_pstNalu->buf,1,l_DateLen,g_out_h264_fd);
if(l_s32Ret!=l_DateLen)
{
printf("%s %d write file error \n",__FILE__,__LINE__);
}
}else
{
break;
}
}
/**关闭视频流文件**/
CloseVideoStream(l_pstNalu);
fclose(g_out_h264_fd);
return 0;
}
/********************************************************
Function: TestH264ToAVI
Description:AVI文件里只封装视频文件
Input: None
OutPut: None
Return: 0: success,none 0:error
Others:
Author: Caibiao Lee
Date: 2017-12-14
*********************************************************/
int TestH264ToAVI(void)
{
int l_s32Ret = 0;
int l_DateLen = 0;
int l_u32WriteLen = 0;
unsigned int l_u32MoviLen = 0;
unsigned int l_u32AVIWriteLen = 0;
AVI_PARA_S stPara;
NALU_S * l_pstNalu = NULL;
unsigned char *l_pu8Buff = NULL;
/**创建AVI文件**/
l_s32Ret = CreatAVIFile();
if(0!=l_s32Ret)
{
return -1;
}
/**初始化视频流数据**/
l_pstNalu = InitVideoStream(MAX_VIDEO_SIZE);
if(NULL == l_pstNalu)
{
return -1;
}
/**视频参数**/
stPara.bVenc = true;
stPara.u32Width = 640;
stPara.u32Height = 360;
stPara.u32Gop = 90;
stPara.u64fps = 25;
stPara.pCompressor = "h264";
/**音频参数**/
stPara.bAenc = false; //不使用音频
stPara.u32Format = 1;
stPara.u32Rate = 8000;
stPara.u32Bits = 16;
stPara.u32Mp3Rate = 0;
/**设置是否使用索引**/
stPara.s8HasIndex = 0;
/**初始化AVI文件头结构**/
RIFF_WriteAviHeader(stPara,g_out_avi_fd);
l_pu8Buff = (unsigned char*)malloc(MAX_VIDEO_SIZE);
if(NULL==l_pu8Buff)
{
printf("%s %d malloc error \n",__FILE__,__LINE__);
return -1;
}
while(1)
{
l_DateLen = ReadOneFrameVideo(l_pstNalu);
if(l_DateLen > 0)
{
/**设置视频标签**/
memcpy(l_pu8Buff,AVI_VIDEO_FLAG,4);
/**数据对齐**/
l_u32WriteLen = AVICalcAlign(l_DateLen,4);
/**设置长度**/
l_pu8Buff[4] = (l_u32WriteLen)&0xff;
l_pu8Buff[5] = (l_u32WriteLen>> 8)&0xff;
l_pu8Buff[6] = (l_u32WriteLen>>16)&0xff;
l_pu8Buff[7] = (l_u32WriteLen>>24)&0xff;
/**实际应该写入的长度**/
l_u32WriteLen += 8 ; /**4ByteFlag + 4ByteLen**/
memcpy(l_pu8Buff + 8,l_pstNalu->buf,l_DateLen);
l_s32Ret = fwrite(l_pu8Buff,1,l_u32WriteLen,g_out_avi_fd);
if(l_s32Ret!=l_u32WriteLen)
{
printf("%s %d write file error \n",__FILE__,__LINE__);
}
l_u32MoviLen +=l_u32WriteLen;
}
else
{
break ;
}
}
/**更新AVI文件头结构中的长度值**/
l_u32AVIWriteLen = ftell(g_out_avi_fd);
/**RIFF文件中的度**/
stPara.u32AviLen = l_u32AVIWriteLen - 8; /**4ByteRIFF + 4ByteLen**/
/**movi块的长度**/
stPara.u32MoviLen = l_u32MoviLen;
/**写入到头文件中**/
RIFF_WriteAviHeader(stPara,g_out_avi_fd);
return 0;
}
/********************************************************
Function: TestH264AndPCMToAVI
Description:将h264视频流和PCM音频流封装到AVI文件中
Input: None
OutPut: None
Return: 0: success,none 0:error
Others: 测试使用的音频是2Channel_44100_16bit.pcm
音频采样率 = 44100,采样通道 = 2,位深度 = 16
假设采样间隔 = 20ms,也就是每秒采集50次
一秒钟总的数据量 = 44100 * 2*16/8 = 176400 字节
所以每帧音频数据大小 = 176400/50 = 3528 字节
视频帧率是25 音频率是50,模拟媒体流时间不准确,
所以使用存一帧视频再存两帧音频使音视频同步
Author: Wen Lee
Date: 2018-07-20
*********************************************************/
int TestH264AndPCMToAVI(void)
{
int l_s32Ret = 0;
int l_DateLen = 0;
int l_u32WriteLen = 0;
int l_u32AudioLen = 0;
unsigned int l_u32AVIWriteLen = 0;
AVI_PARA_S stPara;
NALU_S * l_pstNalu = NULL;
unsigned char *l_pu8Buff = NULL;
unsigned char l_arru8Audio[1024*4] = {0};
AVI_INDEX_ENTRY_S l_stIndexEntry;
/**创建AVI文件**/
l_s32Ret = CreatAVIFile();
if(0!=l_s32Ret)
{
return -1;
}
/**初始化视频流数据**/
l_pstNalu = InitVideoStream(MAX_VIDEO_SIZE);
if(NULL == l_pstNalu)
{
return -1;
}
/**打开音频流**/
l_s32Ret = InitAudioStream();
if(0!=l_s32Ret)
{
return -1;
}
/**视频参数**/
stPara.bVenc = true;
stPara.u32Width = 640;
stPara.u32Height = 360;
stPara.u32Gop = 90;
stPara.u64fps = 25;
stPara.pCompressor = "h264";
/**音频参数**/
stPara.bAenc = true; //不使用音频
stPara.u32Format = 1;
stPara.u32Rate = 44100;
stPara.u32Channels = 2;
stPara.u32Bits = 16;
stPara.u32Mp3Rate = 0;
stPara.u32AVChanNum = 2;
/**设置是否使用索引**/
stPara.s8HasIndex = 0;
/**初始化AVI文件头结构**/
RIFF_WriteAviHeader(stPara,g_out_avi_fd);
l_pu8Buff = (unsigned char*)malloc(MAX_VIDEO_SIZE);
if(NULL==l_pu8Buff)
{
printf("%s %d malloc error \n",__FILE__,__LINE__);
return -1;
}
while(1)
{
/**读一帧视频数据**/
l_DateLen = ReadOneFrameVideo(l_pstNalu);
if(l_DateLen > 0)
{
/**设置视频标签**/
memcpy(l_pu8Buff,AVI_VIDEO_FLAG,4);
/**数据对齐**/
l_u32WriteLen = AVICalcAlign(l_DateLen,4);
/**设置长度**/
l_pu8Buff[4] = (l_u32WriteLen)&0xff;
l_pu8Buff[5] = (l_u32WriteLen>> 8)&0xff;
l_pu8Buff[6] = (l_u32WriteLen>>16)&0xff;
l_pu8Buff[7] = (l_u32WriteLen>>24)&0xff;
/**实际应该写入的长度**/
l_u32WriteLen += 8 ; /**4ByteFlag + 4ByteLen**/
memcpy(l_pu8Buff + 8,l_pstNalu->buf,l_DateLen);
l_s32Ret = fwrite(l_pu8Buff,1,l_u32WriteLen,g_out_avi_fd);
if(l_s32Ret!=l_u32WriteLen)
{
printf("%s %d write file error \n",__FILE__,__LINE__);
}
}
else
{
break ;
}
/**读两帧音频数据**/
for(int i=0;i<2;i++)
{
l_u32AudioLen = 3528;
l_DateLen = ReadOneFrameAudio(&l_arru8Audio[8],l_u32AudioLen);
if(l_DateLen > 0)
{
/**设置视频标签**/
memcpy(l_arru8Audio,AVI_AUDIO_FLAG,4);
/**数据对齐**/
l_u32AudioLen = AVICalcAlign(l_DateLen,4);
/**设置长度**/
l_arru8Audio[4] = (l_u32AudioLen)&0xff;
l_arru8Audio[5] = (l_u32AudioLen>> 8)&0xff;
l_arru8Audio[6] = (l_u32AudioLen>>16)&0xff;
l_arru8Audio[7] = (l_u32AudioLen>>24)&0xff;
l_u32AudioLen += 8;
l_s32Ret = fwrite(l_arru8Audio,1,l_u32AudioLen,g_out_avi_fd);
if(l_s32Ret!=l_u32AudioLen)
{
printf("%s %d write file error \n",__FILE__,__LINE__);
}
}
}
}
/**更新AVI文件头结构中的长度值**/
l_u32AVIWriteLen = ftell(g_out_avi_fd);
stPara.u32MoviLen = l_u32AVIWriteLen;
/**RIFF文件中的度**/
stPara.u32AviLen = l_u32AVIWriteLen - 8; /**4ByteRIFF + 4ByteLen**/
/**写入到头文件中**/
RIFF_WriteAviHeader(stPara,g_out_avi_fd);
CloseVideoStream(l_pstNalu);
CloseAudioStream();
return 0;
}
/********************************************************
Function: TestH264AndPCMToAVI
Description:合成音频视频流,同时添加索引表
Input: None
OutPut: None
Return: 0: success,none 0:error
Others: 在TestH264AndPCMToAVI的基础上添加索引表
Author: Wen Lee
Date: 2018-07-20
*********************************************************/
int TestAVIIndex(void)
{
int l_s32Ret = 0;
int l_DateLen = 0;
int l_u32WriteLen = 0;
int l_u32AudioLen = 0;
unsigned int l_u32AVIWriteLen = 0;
AVI_PARA_S stPara;
NALU_S * l_pstNalu = NULL;
unsigned char *l_pu8Buff = NULL;
unsigned char l_arru8Audio[1024*4] = {0};
AVI_INDEX_ENTRY_S l_stIndexEntry;
WRITE_AVI_INDEX_S *pstWriteAviIndex;
/**创建AVI文件**/
l_s32Ret = CreatAVIFile();
if(0!=l_s32Ret)
{
printf("%s %d init error \n",__FILE__,__LINE__);
return -1;
}
/**初始化视频流数据**/
l_pstNalu = InitVideoStream(MAX_VIDEO_SIZE);
if(NULL == l_pstNalu)
{
printf("%s %d init error \n",__FILE__,__LINE__);
return -1;
}
/**打开音频流**/
l_s32Ret = InitAudioStream();
if(0!=l_s32Ret)
{
printf("%s %d init error \n",__FILE__,__LINE__);
return -1;
}
/**为索引表分配空间**/
pstWriteAviIndex = (WRITE_AVI_INDEX_S *)malloc(sizeof(WRITE_AVI_INDEX_S));
if(NULL==pstWriteAviIndex)
{
printf("%s %d malloc error \n",__FILE__,__LINE__);
return -1;
}
/**视频参数**/
stPara.bVenc = true;
stPara.u32Width = 640;
stPara.u32Height = 360;
stPara.u32Gop = 90;
stPara.u64fps = 25;
stPara.pCompressor = "h264";
/**音频参数**/
stPara.bAenc = true; //不使用音频
stPara.u32Format = 1;
stPara.u32Rate = 44100;
stPara.u32Channels = 2;
stPara.u32Bits = 16;
stPara.u32Mp3Rate = 0;
stPara.u32AVChanNum = 2;
/**!!设置是否使用索引 !!**/
stPara.s8HasIndex = 1;
/**初始化AVI文件头结构**/
RIFF_WriteAviHeader(stPara,g_out_avi_fd);
l_pu8Buff = (unsigned char*)malloc(MAX_VIDEO_SIZE);
if(NULL==l_pu8Buff)
{
printf("%s %d malloc error \n",__FILE__,__LINE__);
return -1;
}
while(1)
{
/**读一帧视频数据**/
l_DateLen = ReadOneFrameVideo(l_pstNalu);
if(l_DateLen > 0)
{
/**设置视频标签**/
memcpy(l_pu8Buff,AVI_VIDEO_FLAG,4);
/**数据对齐**/
l_u32WriteLen = AVICalcAlign(l_DateLen,4);
/**设置长度**/
l_pu8Buff[4] = (l_u32WriteLen)&0xff;
l_pu8Buff[5] = (l_u32WriteLen>> 8)&0xff;
l_pu8Buff[6] = (l_u32WriteLen>>16)&0xff;
l_pu8Buff[7] = (l_u32WriteLen>>24)&0xff;
/**实际应该写入的长度**/
l_u32WriteLen += 8 ; /**4ByteFlag + 4ByteLen**/
memcpy(l_pu8Buff + 8,l_pstNalu->buf,l_DateLen);
l_s32Ret = fwrite(l_pu8Buff,1,l_u32WriteLen,g_out_avi_fd);
if(l_s32Ret!=l_u32WriteLen)
{
printf("%s %d write file error \n",__FILE__,__LINE__);
}
l_stIndexEntry.u32Flags = 0x00;
/**判断是否为关键帧 关键帧为0x10 其它为0x00 **/
if((0==l_pu8Buff[8])&&(0==l_pu8Buff[9])&&(1==l_pu8Buff[10])&&(0x05==(l_pu8Buff[11]&0x1F)))
{
l_stIndexEntry.u32Flags = 0x10;
}
if((0==l_pu8Buff[8])&&(0==l_pu8Buff[9])&&(0==l_pu8Buff[10])&&(1==l_pu8Buff[11])
&&(0x05==(l_pu8Buff[12]&0x1F)))
{
l_stIndexEntry.u32Flags = 0x10;
}
l_stIndexEntry.u32ChunkId = 0x63643030; /**'00dc'**/
l_stIndexEntry.u32Size = l_u32WriteLen - 8;
RIFF_WriteAviIndex(l_stIndexEntry,pstWriteAviIndex);
}
else
{
break ;
}
/**读两帧音频数据**/
for(int i=0;i<2;i++)
{
l_u32AudioLen = 3528;
l_DateLen = ReadOneFrameAudio(&l_arru8Audio[8],l_u32AudioLen);
if(l_DateLen > 0)
{
/**设置视频标签**/
memcpy(l_arru8Audio,AVI_AUDIO_FLAG,4);
/**数据对齐**/
l_u32AudioLen = AVICalcAlign(l_DateLen,4);
/**设置长度**/
l_arru8Audio[4] = (l_u32AudioLen)&0xff;
l_arru8Audio[5] = (l_u32AudioLen>> 8)&0xff;
l_arru8Audio[6] = (l_u32AudioLen>>16)&0xff;
l_arru8Audio[7] = (l_u32AudioLen>>24)&0xff;
l_u32AudioLen += 8;
l_s32Ret = fwrite(l_arru8Audio,1,l_u32AudioLen,g_out_avi_fd);
if(l_s32Ret!=l_u32AudioLen)
{
printf("%s %d write file error \n",__FILE__,__LINE__);
}
l_stIndexEntry.u32ChunkId = 0x64773130; /**'01wd'**/
l_stIndexEntry.u32Flags = 0x00;
l_stIndexEntry.u32Size = l_u32AudioLen - 8;
RIFF_WriteAviIndex(l_stIndexEntry,pstWriteAviIndex);
}
}
}
/**更新AVI文件头结构中的长度值**/
l_u32AVIWriteLen = ftell(g_out_avi_fd);
stPara.u32MoviLen = l_u32AVIWriteLen;
/**写索引表到AVI文件中去***/
RIFF_WriteAviIndexToFile(pstWriteAviIndex,g_out_avi_fd);
/**更新AVI文件头结构中的长度值**/
l_u32AVIWriteLen = ftell(g_out_avi_fd);
/**RIFF文件中的度**/
stPara.u32AviLen = l_u32AVIWriteLen - 8; /**4ByteRIFF + 4ByteLen**/
/**写入到头文件中**/
RIFF_WriteAviHeader(stPara,g_out_avi_fd);
/**关闭资源**/
CloseVideoStream(l_pstNalu);
CloseAudioStream();
return 0;
}
int main(void)
{
int l_s32Ret = 0;
printf("Hello World \n");
/**h246文件测试**/
//l_s32Ret = TestH264ReadAndWrite();
/**只封装视频数据*/
//l_s32Ret = TestH264ToAVI();
/**同时封装音频和视频数据**/
//l_s32Ret = TestH264AndPCMToAVI();
/**添加索引**/
l_s32Ret = TestAVIIndex();
return 0;
}
编译运行最后生成test.avi文件,使用暴风播放器播放效果如下:

工程下载:
在 liwen01 公众号中回复 音视频 获取工程代码,本章代码工程名为:AVI_Create.tar.gz
---------------------------End---------------------------
长按识别二维码
关注 liwen01 公众号
linux环境C语言实现:h264与pcm封装成AVI格式的更多相关文章
- H264编码 封装成MP4格式 视频流 RTP封包
H264编码 封装成MP4格式 视频流 RTP封包 分类: 多媒体编程 2013-02-20 21:31 3067人阅读 ...
- Linux环境C语言斐波拉切数列(1,1,2,3,5,8,13,.........)实现
斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一 ...
- Linux环境下安装mysql5.6(二进制包不是rpm格式)
一.准备: 1.CentOS release 6.8 2.mysql-5.6.31-linux-glibc2.5-x86_64.tar.gz 3.Linux下MySQL5.6与MySQL5.7安装方法 ...
- iOS 将视频流(h264)和音频流封装成PS流
调用方法: static CPSPackager * testObjc = NULL; static char *pszBuffer; testObjc = new CPSPackager(); p ...
- .NET 6 从0到1使用Docker部署至Linux环境
前言 作为一名.Net菜鸟开发者,平时对Linux接触的并不多,项目部署这一块都是运维小哥顶着,但是作为混迹在云原生项目组的人咱也不能什么都不知道,该掌握的知识还是要懂的,所以借着这次机会,梳理一下项 ...
- Linux环境下使用gcc编译,gdb反汇编C语言程序
使用虚拟机 VMware Workstation 10 Linux环境:Ubuntu 14.04 LTS Server amd64 我把过程截图如下. 首先是hello world程序: 备注: ...
- Windows10下配置Linux下C语言开发环境
今天为大家介绍如在Windows10下配置Linux下C语言开发环境,首先安装linux子系统:启用开发者模式 1.打开设置 2.点击更新和安全3.点击开发者选项 4.启用开发人员模式 5.更改系统功 ...
- Linux下c语言环境概述
Linux下C语言环境概述 主要涉及编辑器.编译链接器.调试器.项目管理工具 编辑器 Linux中常用的编辑器有vi和emacs 查看vim配置文件并编辑 编译链接器 在Linux中,最常用的编译器是 ...
- Linux C编程之一:Linux下c语言的开发环境
---恢复内容开始--- 今天开始根据Linux C编程相关视频的学习所做的笔记,希望能一直坚持下去... 1.开发环境的构成 编辑器:VI: 编译器:选择GNU C/C++编译器gcc: 调试器: ...
- Linux环境下C语言线程创建---简单代码
在Linux环境下用C语言编写线程创建. //file name: pthreadtext.c #include <stdio.h> #include <pthread.h> ...
随机推荐
- Web测试基本思路:UI测试、功能测试和兼容性测试
关于网站测试的基本思路,希望对测试小白有所帮助. 一.UI测试 用户界面测试主要是拿待测网页和设计稿进行对比,主要主要以下4点: 1.注重细节: 这点最基本,就是对比是细心.细心再细心. 2.主要整体 ...
- JavaFx 打包jar(六)
JavaFx 打包jar(六) JavaFX 从入门入门到入土系列 我们编写了不少javafx,那么如何打包成jar给用户呢?下面我给出比较全的打包方式. 打包jar 下面我给出比较全的打包方式. 1 ...
- Kafka 万字精讲|工作五年这些你都知道吗?
目录 前言 一.Kafka 简介 1.1 事件流平台 1.2 Kafka 主要概念和术语 1.3 Zookeeper 二.Kafka 集群搭建和使用 2.1 使用 Docker Compose 搭建 ...
- 详解GaussDB(DWS)通信安全的小妙招:连接认证机制
本文分享自华为云社区<GaussDB(DWS)数据库安全系列之通信安全>,作者:yd_262982826. 1. 前言 适用版本:[8.1.3及以上] 网络是一个开放的环境,仅仅依靠用户名 ...
- 过亿云资源运维管控难?华为云CloudMap带你喝着咖啡做运维
摘要:华为云站点数字化平台CloudMap携手华为云图引擎GES打造云服务全栈拓扑,网络流量路径和云服务动态依赖等空间关系数据,支撑现网运行态风险识别和分钟级定位定界,构建业界领先的数字化能力. 本文 ...
- OpenCV萌新福音:易上手的数字识别实践案例
摘要:信用卡识别的案例用到了图像处理的一些基本操作,对刚上手CV的人来说还是比较友好的. 本文分享自华为云社区<Python openCV案例:信用卡数字识别>,原文作者:深蓝的回音 . ...
- 业务并发度不够,数仓的CN可以来帮忙
摘要: CN全称协调节点(Coordinator Node),是和用户关系最密切也是DWS内部非常重要的一个组件,它负责提供外部应用接口.优化全局执行计划.向Datanode分发执行计划,以及汇总.处 ...
- 插件化架构设计(3):前端可视化化平台插件架构-grafana实践
插件实践方案 其实插件你就把它当一个组件来用就好.用起来就是异步组件加载. vue异步组件加载,看官方文档:https://cn.vuejs.org/v2/guide/components-dynam ...
- pip升级和卸载安装的第三方库
pip install --upgrade 第三方库名 pip uninstall 第三方库名
- Kubernetes(K8S) yaml 介绍
使用空格做为缩进 缩进的空格数目不重要, 只要相同层级的元素左侧对齐即可 低版本缩进时不允许使用 Tab 键, 只允许使用空格 使用#标识注释, 从这个字符一直到行尾, 都会被解释器忽略 --- 使用 ...