Alsa 读取wave文件,并播放wave 文件
对于一个wave文件,如果需要播放,涉及到几个方面
1.对于wave文件的解析
2.通过解析wave文件,将得到的参数(主要是sampfrequency, bitsperSample,channel)通过alsa api设下去
3.正确找到data的起始点
4.play alsa
1.对于wave文件的解析,需要知道wave文件的格式
注意几点,标准的是44byte的头,但是有些情况下会有additional info, 占据2字节。头信息参见下图,也可以参考wave 文件解析
endian |
field name |
Size |
|
big | ChunkID | 4 | 文件头标识,一般就是" RIFF" 四个字母 |
little | ChunkSize | 4 | 整个数据文件的大小,不包括上面ID和Size本身 |
big | Format | 4 | 一般就是" WAVE" 四个字母 |
big | SubChunk1ID | 4 | 格式说明块,本字段一般就是"fmt " |
little | SubChunk1Size | 4 | 本数据块的大小,不包括ID和Size字段本身 |
little | AudioFormat | 2 | 音频的格式说明 |
little | NumChannels | 2 | 声道数 |
little | SampleRate | 4 | 采样率 |
little | ByteRate | 4 | 比特率,每秒所需要的字节数 |
little | BlockAlign | 2 | 数据块对齐单元 |
little | BitsPerSample | 2 | 采样时模数转换的分辨率 |
big | SubChunk2ID | 4 | 真正的声音数据块,本字段一般是"data" |
little | SubChunk2Size | 4 | 本数据块的大小,不包括ID和Size字段本身 |
little | Data | N | 音频的采样数据 |
2.设置alsa的参数可以详见代码
3.通过解析wave file可以知道我们data的起始位置
4.通过alsa来play,详见代码
- /**
- @file TestAlsaPlayWave.cpp
- @brief This is a short example to play the audio wave, please define the path in the main func
- @par author: jlm
- @par pre env: alsa
- @todo
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string>
- #include <sched.h>
- #include <errno.h>
- #include <getopt.h>
- #include <iostream>
- #include <asoundlib.h>
- #include <sys/time.h>
- #include <math.h>
- using namespace std;
- /*********Type definition***********************/
- typedef unsigned char uint8;
- typedef unsigned short uint16;
- typedef enum EBitsPerSample
- {
- BITS_UNKNOWN = ,
- BITS_PER_SAMPLE_8 = ,
- BITS_PER_SAMPLE_16 = ,
- BITS_PER_SAMPLE_32 =
- }EBitsPerSample_t;
- typedef enum ENumOfChannels
- {
- NUM_OF_CHANNELS_1 = ,
- NUM_OF_CHANNELS_2 =
- }ENumOfChannels_t;
- #if 0
- /** PCM state */
- typedef enum _snd_pcm_state {
- /** Open */
- SND_PCM_STATE_OPEN = ,
- /** Setup installed */
- SND_PCM_STATE_SETUP,
- /** Ready to start */
- SND_PCM_STATE_PREPARED,
- /** Running */
- SND_PCM_STATE_RUNNING,
- /** Stopped: underrun (playback) or overrun (capture) detected */
- SND_PCM_STATE_XRUN,
- /** Draining: running (playback) or stopped (capture) */
- SND_PCM_STATE_DRAINING,
- /** Paused */
- SND_PCM_STATE_PAUSED,
- /** Hardware is suspended */
- SND_PCM_STATE_SUSPENDED,
- /** Hardware is disconnected */
- SND_PCM_STATE_DISCONNECTED,
- SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED
- } snd_pcm_state_t;
- #endif
- typedef struct ALSA_CONFIGURATION
- {
- std::string alsaDevice;
- std::string friendlyName;
- /// Read: Buffer size should be large enough to prevent overrun (read / write buffer full)
- unsigned int alsaBufferSize;
- /// Chunk size should be smaller to prevent underrun (write buffer empty)
- unsigned int alsaPeriodFrame;
- unsigned int samplingFrequency;//48kHz
- EBitsPerSample bitsPerSample;
- ENumOfChannels numOfChannels;
- bool block; // false means nonblock
- snd_pcm_access_t accessType;
- snd_pcm_stream_t streamType; // Playback or capture
- unsigned int alsaCapturePeriod; // Length of each capture period
- }Alsa_Conf;
- typedef struct Wave_Header
- {
- uint8 ChunkID[];
- uint8 ChunkSize[];
- uint8 Format[];
- uint8 SubChunk1ID[];
- uint8 SubChunk1Size[];
- uint8 AudioFormat[];
- uint8 NumChannels[];
- uint8 SampleRate[];
- uint8 ByteRate[];
- uint8 BlockAlign[];
- uint8 BitsPerSample[];
- uint8 CombineWaveFileExtra2Bytes[];
- uint8 SubChunk2ID[];
- uint8 SubChunk2Size[];
- }Wave_Header_t;
- typedef struct Wave_Header_Size_Info
- {
- uint8 ChunkID[];
- uint8 ChunkSize[];
- uint8 Format[];
- uint8 SubChunk1ID[];
- uint8 SubChunk1Size[];
- }Wave_Header_Size_Info_t;
- typedef struct Wave_Header_Audio_Info
- {
- uint8 AudioFormat[];
- uint8 NumChannels[];
- uint8 SampleRate[];
- uint8 ByteRate[];
- uint8 BlockAlign[];
- uint8 BitsPerSample[];
- }Wave_Header_Audio_Info_t;
- typedef struct Wave_Header_Additional_Info
- {
- uint8 AdditionalInfo_2Bytes[]; //this depends on the SubChunk1Size,normal if SubChunk1Size=16 then match the normal wave format, if SubChunk1Size=18 then 2 more additional info bytes
- }Wave_Header_Additional_Info_t;
- typedef struct Wave_Header_Data_Info
- {
- uint8 SubChunk2ID[];
- uint8 SubChunk2Size[];
- }Wave_Header_Data_Info_t;
- /*********Global Variable***********************/
- snd_pcm_uframes_t g_frames; //just test purpose
- /*********Func Declaration***********************/
- void TestAlsaDevice(snd_pcm_t** phandler);
- bool PrepareAlsaDevice(Alsa_Conf* palsaCfg,snd_pcm_t** phandler);
- bool closeAlsaDevice(snd_pcm_t** phandler);
- bool ParseWaveFile(const string wavepath,Alsa_Conf* palsaCfg);
- uint16 HandleLittleEndian(uint8* arr,int size);
- bool PlayWave(FILE** fp,snd_pcm_t** phandler,Alsa_Conf* palsaCfg);
- uint16 HandleLittleEndian(uint8* arr,int size)
- {
- uint16 value=;
- ;i<size;i++)
- {
- value=value+(arr[i]<<(*i));
- }
- return value;
- }
- #if 0
- //this is the return value
- ChunkID = "RIFF"
- ChunkSize =
- Format = "WAVE"
- fmt = "fmt "
- SubChunk1Size =
- AudioFormat =
- NumChannels =
- SampleRate =
- ByteRate =
- BlockAlign =
- BitsPerSample =
- SubChunk2ID = "data"
- SubChunk2Size =
- #endif
- //parse the wave file
- bool ParseWaveFile(const string wavepath,Alsa_Conf* palsaCfg,FILE** fp)
- {
- ;
- //FILE* fp=NULL;
- *fp=fopen(wavepath.c_str(),"rb");
- if(*fp==NULL)
- {
- cout<<"Can parse the wave file-->need check the file name"<<endl;
- }
- /*********************size info parse begin*************************/
- //read size info
- Wave_Header_Size_Info_t wav_size_info;
- memset(&wav_size_info,,sizeof(Wave_Header_Size_Info_t));
- ret=fread(&wav_size_info,,*fp);
- )
- {
- cout<<"read error"<<endl;
- return false;
- }
- string ChunkID="";
- ;i<;i++)
- {
- ChunkID+=wav_size_info.ChunkID[i];
- }
- string riff="RIFF";
- !=strcmp(ChunkID.c_str(),riff.c_str()))
- {
- cout<<"Invalid the fist Chunk id"<<endl;
- return false;
- }
- uint16 ChunkSize=HandleLittleEndian(wav_size_info.ChunkSize,);
- cout<<"The ChunSize is "<<ChunkSize<<endl;
- string Format="";
- ;i<;i++)
- {
- Format+=wav_size_info.Format[i];
- }
- !=strcmp(Format.c_str(),"WAVE"))
- {
- cout<<"Invalid the format"<<endl;
- return false;
- }
- string SubChunk1ID="";
- ;i<;i++)
- {
- SubChunk1ID+=wav_size_info.SubChunk1ID[i];
- }
- string fmt="fmt ";
- !=strcmp(SubChunk1ID.c_str(),fmt.c_str()))
- {
- cout<<"Invalid the SubChunk1ID "<<endl;
- return false;
- }
- uint16 SubChunk1Size=HandleLittleEndian(wav_size_info.SubChunk1Size,);
- && SubChunk1Size!=)
- {
- cout<<"Invalid the SubChunk1Size"<<endl;
- return false;
- }
- /*********************Audio info parse begin*************************/
- Wave_Header_Audio_Info_t wav_audio_info;
- memset(&wav_audio_info,,sizeof(Wave_Header_Audio_Info_t));
- ret=fread(&wav_audio_info,,*fp);
- )
- {
- cout<<"read error"<<endl;
- return false;
- }
- //fseek(fp,sizeof(Wave_Header_Size_Info_t),0);//文件指针偏移3个字节到'2' because fread will shift the pointer
- uint16 AudioFormat =HandleLittleEndian(wav_audio_info.AudioFormat,);
- uint16 NumChannels =HandleLittleEndian(wav_audio_info.NumChannels,);
- uint16 SampleRate =HandleLittleEndian(wav_audio_info.SampleRate,);
- uint16 ByteRate =HandleLittleEndian(wav_audio_info.ByteRate,);
- uint16 BlockAlign =HandleLittleEndian(wav_audio_info.BlockAlign,);
- uint16 BitsPerSample=HandleLittleEndian(wav_audio_info.BitsPerSample,);
- palsaCfg->numOfChannels=(ENumOfChannels)NumChannels;
- palsaCfg->samplingFrequency=SampleRate;
- palsaCfg->bitsPerSample=(EBitsPerSample)BitsPerSample;
- /*********************Additional info parse begin if needed*************************/
- )
- {
- Wave_Header_Additional_Info_t wav_additional_info;
- memset(&wav_additional_info,,sizeof(Wave_Header_Additional_Info_t));
- fread(&wav_additional_info,,*fp);
- cout<<"read wav_additional_info"<<endl;
- )
- {
- cout<<"read error"<<endl;
- return false;
- }
- uint16 AdditionalInfo=HandleLittleEndian(wav_additional_info.AdditionalInfo_2Bytes,);
- cout<<"read AdditionalInfo value="<<AdditionalInfo<<endl;
- }
- /*********************Data info parse begin *************************/
- Wave_Header_Data_Info_t wave_data_info;
- memset(&wave_data_info,,sizeof(Wave_Header_Data_Info_t));
- fread(&wave_data_info,,*fp);
- )
- {
- cout<<"read error"<<endl;
- return false;
- }
- string SubChunk2ID="";
- ;i<;i++)
- {
- SubChunk2ID+=wave_data_info.SubChunk2ID[i];
- }
- string fact="fact";
- string data="data";
- ==strcmp(SubChunk2ID.c_str(),fact.c_str()))
- {
- cout<<"SubChunk2ID fact"<<endl;
- }
- ==strcmp(SubChunk2ID.c_str(),data.c_str()))
- {
- cout<<"SubChunk2ID data"<<endl;
- }
- else
- {
- cout<<"Invalid SubChunk2ID "<<endl;
- return false;
- }
- uint16 SubChunk2Size=HandleLittleEndian(wave_data_info.SubChunk2Size,);
- cout<<"End Parse"<<endl;
- return true;
- }
- bool PlayWave(FILE** fp,snd_pcm_t** phandler,Alsa_Conf* palsaCfg)
- {
- ;
- bool ret=false;
- snd_pcm_uframes_t frames=palsaCfg->alsaPeriodFrame;
- ; //4bytes
- uint16 audio_data_size=frames*bytesPerFrame;//one period 10ms ,1600*10/1000*(2*16/8)=640bytes one period
- uint8* buffer=new uint8[audio_data_size];
- cout<<"Start play wave"<<endl;
- if(*fp==NULL || *phandler==NULL || palsaCfg==NULL)
- {
- cout<<"End play wave because something is NULL"<<endl;
- return false;
- }
- //fseek(*fp,46,SEEK_SET); //no need to do fseek because already shifted
- cout<<"available frame "<<snd_pcm_avail(*phandler)<<"my frames is "<<frames<<endl;
- while(true)
- {
- if(feof(*fp))
- {
- cout<<"Reach end of the file"<<endl;
- break;
- }
- else
- {
- if(snd_pcm_avail(*phandler)<frames)
- {
- continue;
- }
- else
- {
- memset(reinterpret_cast<,sizeof(uint8)*audio_data_size);
- err=fread(buffer,,*fp);
- )
- {
- cout<<"read error"<<endl;
- }
- if ( NULL != buffer )
- {
- err = snd_pcm_writei(*phandler, buffer, frames);
- )
- {
- cout<<"Fail to write the audio data to ALSA. Reason: "<<(snd_strerror(err));
- // recover ALSA device
- err = snd_pcm_recover(*phandler, err, );
- )
- {
- cout<<"Fail to recover ALSA device. Reason: "<<(snd_strerror(err));
- ret = false;
- }
- else
- {
- cout<<"ALSA device is recovered from error state"<<endl;
- }
- }
- }
- else
- {
- cout<<"Write buffer is NULL!"<<endl;
- }
- }
- }
- usleep(palsaCfg->alsaCapturePeriod / ( * ));
- }
- delete[] buffer;
- buffer=NULL;
- return ret;
- }
- bool PrepareAlsaDevice(Alsa_Conf* palsaCfg,snd_pcm_t** phandler)
- {
- bool ret=false;
- bool success=true;
- ;
- snd_pcm_format_t format;
- snd_pcm_hw_params_t *hw_params = NULL;
- ;
- if(palsaCfg!=NULL)
- {
- // open ALSA device
- error=snd_pcm_open(phandler,palsaCfg->alsaDevice.c_str(),palsaCfg->streamType,palsaCfg->block? :SND_PCM_NONBLOCK);
- ) //0 on success otherwise a negative error code
- {
- success=false;
- cout<<"Open Alsadevice error error code="<<snd_strerror(error)<<endl;
- }
- if(success)
- {
- //allocate hardware parameter structur
- error=snd_pcm_hw_params_malloc(&hw_params);//alsao can use snd_pcm_hw_params_alloca(&hwparams)
- )
- {
- success=false;
- hw_params=NULL;
- cout<<"Set hw params error error code="<<snd_strerror(error)<<endl;
- }
- }
- if(success)
- {
- //Fill params with a full configuration space for a PCM. initialize the hardware parameter
- error=snd_pcm_hw_params_any(*phandler,hw_params);
- )
- {
- success=false;
- cout<<"Broken configuration for PCM: no configurations available: "<<snd_strerror(error)<<endl;
- }
- }
- if(success)
- {
- // set the access type
- error = snd_pcm_hw_params_set_access(*phandler, hw_params, palsaCfg->accessType);
- )
- {
- cout<<"[SG]Fail to set access type. Reason: "<<snd_strerror(error)<<endl;
- success = false;
- }
- }
- if(success)
- {
- switch (palsaCfg->bitsPerSample)
- {
- case BITS_PER_SAMPLE_8:
- {
- format = SND_PCM_FORMAT_U8;
- break;
- }
- case BITS_PER_SAMPLE_16:
- {
- format = SND_PCM_FORMAT_S16_LE; //indicate this was little endian
- break;
- }
- case BITS_PER_SAMPLE_32:
- {
- format = SND_PCM_FORMAT_S32_LE;
- break;
- }
- default:
- {
- format = SND_PCM_FORMAT_S16_LE;
- cout<<"Invalid format"<<endl;
- success=false;
- }
- }
- if(success)
- {
- error=snd_pcm_hw_params_set_format(*phandler,hw_params,format);
- )
- {
- cout<<"set format not available for "<<snd_strerror(error)<<endl;
- success=false;
- }
- }
- }
- if(success)
- {
- error=snd_pcm_hw_params_set_rate_near(*phandler,hw_params,&palsaCfg->samplingFrequency,);
- )
- {
- cout<<"set rate not available for "<<snd_strerror(error)<<endl;
- success=false;
- }
- }
- if(success)
- {
- error=snd_pcm_hw_params_set_channels(*phandler,hw_params,palsaCfg->numOfChannels);
- )
- {
- cout<<"set_channels not available for "<<snd_strerror(error)<<endl;
- success=false;
- }
- }
- if (success)
- {
- // set period size (period size is also a chunk size for reading from ALSA)
- snd_pcm_uframes_t alsaPeriodFrame = static_cast<snd_pcm_uframes_t>(palsaCfg->alsaPeriodFrame); // One frame could be 4 bytes at most
- // set period size
- error = snd_pcm_hw_params_set_period_size_near(*phandler, hw_params, &alsaPeriodFrame, &dir);
- )
- {
- cout<<"[SG]Fail to set period size. Reason: "<<snd_strerror(error)<<endl;
- success = false;
- }
- }
- if (success)
- {
- // set hardware parameters
- error = snd_pcm_hw_params(*phandler, hw_params);
- )
- {
- cout<<"[SG]Fail to set hardware parameter. Reason: "<<snd_strerror(error)<<endl;
- success = false;
- }
- }
- if (success)
- {
- error=snd_pcm_hw_params_get_period_size(hw_params, &g_frames, &dir); //get frame
- cout<<"Frame is "<<g_frames<<endl;
- // free the memory for hardware parameter structure
- snd_pcm_hw_params_free(hw_params);
- hw_params = NULL;
- // Prepare ALSA device
- error = snd_pcm_prepare(*phandler);
- )
- {
- cout<<"Fail to prepare ALSA device. Reason: "<<(snd_strerror(error))<<endl;
- success = false;
- }
- }
- if (success)
- {
- cout<<"ALSA device is ready to use"<<endl;
- }
- else
- {
- // fail to prepare ALSA device ==> un-initialize ALSA device
- if (hw_params != NULL)
- {
- snd_pcm_hw_params_free(hw_params);
- hw_params = NULL;
- }
- closeAlsaDevice(phandler);
- }
- }
- return success;
- }
- bool closeAlsaDevice(snd_pcm_t** phandler)
- {
- bool ret = true;
- snd_pcm_state_t state;
- int snd_ret;
- if (*phandler != NULL)
- {
- // drop the pending audio frame if needed
- state = snd_pcm_state(*phandler);
- cout<<"Alsa handler sate: "<<state<<endl;
- if ((SND_PCM_STATE_RUNNING == state) || (SND_PCM_STATE_XRUN == state) || (SND_PCM_STATE_SUSPENDED == state))
- {
- snd_ret = snd_pcm_drop(*phandler);
- )
- {
- cout<<"Fail to drop ALSA device. Reason: "<<(snd_strerror(snd_ret))<<endl;
- }
- }
- // close ALSA handler
- snd_ret = snd_pcm_close(*phandler);
- )
- {
- cout<<"Fail to close ALSA device. Reason: "<<(snd_strerror(snd_ret))<<endl;
- ret = false;
- }
- *phandler = NULL;
- cout<<"CLOSE ALSA DEVICE"<<endl;
- }
- return ret;
- }
- int main()
- {
- bool ret=false;
- snd_pcm_t* m_phandler=NULL;
- Alsa_Conf* m_palsaCfg=new Alsa_Conf();
- m_palsaCfg->alsaDevice = string("sd_out_16k");
- //m_palsaCfg->samplingFrequency = 16000;
- m_palsaCfg->alsaCapturePeriod = ;
- //m_palsaCfg->numOfChannels = NUM_OF_CHANNELS_1;
- m_palsaCfg->block = true; //block
- m_palsaCfg->friendlyName = "AlsaWave";
- //m_palsaCfg->bitsPerSample = BITS_PER_SAMPLE_16;
- m_palsaCfg->alsaPeriodFrame = m_palsaCfg->samplingFrequency * m_palsaCfg->alsaCapturePeriod / ; // calculate the number of frame in one period
- m_palsaCfg->alsaBufferSize = m_palsaCfg->alsaPeriodFrame * ; //means the whole buffer was perdion*8, e.g. 10ms means every 10ms we will get/send the data
- m_palsaCfg->accessType = SND_PCM_ACCESS_RW_INTERLEAVED;
- m_palsaCfg->streamType = SND_PCM_STREAM_PLAYBACK;
- FILE* fp=NULL;
- const string wavePath="/mnt/hgfs/0_SharedFolder/0_Local_Test_Folder/01_TestFolder/TestALSA/left_1k_right_400hz.wav";
- //parse the wave file
- ret=ParseWaveFile(wavePath,m_palsaCfg,&fp);
- //update the value
- m_palsaCfg->alsaPeriodFrame = m_palsaCfg->samplingFrequency * m_palsaCfg->alsaCapturePeriod / ; // calculate the number of frame in one period
- if(ret)
- {
- //open alsa device
- ret=PrepareAlsaDevice(m_palsaCfg,&m_phandler);
- }
- if(ret)
- {
- PlayWave(&fp,&m_phandler,m_palsaCfg);
- }
- closeAlsaDevice(&m_phandler);
- if(fp!=NULL)
- {
- fclose(fp);
- fp=NULL;
- }
- delete m_palsaCfg;
- m_palsaCfg=NULL;
- ;
- }
Alsa 读取wave文件,并播放wave 文件的更多相关文章
- 读取SD卡文件夹下的MP3文件和播放MP3文件
首先获取SD卡path路径下的所有的MP3文件,并将文件名和文件大小存入List数组(此代码定义在FileUtils类中): /** * 读取目录中的Mp3文件的名字和大小 */ public Lis ...
- Linux音频编程--使用ALSA库播放wav文件
在UBUNTU系统上使用alsa库完成了对外播放的wav文件的案例. 案例代码: /** *test.c * *注意:这个例子在Ubuntu 12.04.1环境下编译运行成功. * */ #inclu ...
- Opencv 播放mp4文件和读取摄像头图以及可能会发生的一些异常问题解决方法
学习内容 学习Opencv 读取并播放本地视频和打开摄像头图像以及可能会发生的一些异常问题解决方法 代码演示 电脑环境信息: OpenCV版本:4.5.2 ,vs2017 1.视频文件读取与播放 加载 ...
- python 播放 wav 文件
未使用其他库, 只是使用 pywin32 调用系统底层 API 播放 wav 文件. # Our raison d'etre - playing sounds import pywintypes im ...
- 【转】C# 视频监控系列(12):H264播放器——播放录像文件
原文地址:http://www.cnblogs.com/over140/archive/2009/03/23/1419643.html?spm=5176.100239.blogcont51182.16 ...
- VC++中MCI播放音频文件 【转】
MCI播放mp3音频文件例程 源文件中需要包含头文件 Mmsystem.h,在Project->Settings->Link->Object/libray module中加入库 Wi ...
- 如何播放 WAV 文件?
from http://www.vckbase.com/index.php/wv/434 平时,你在多媒体软件的设计中是怎样处理声音文件的呢?使用Windows 提供的API函数 sndPlaySou ...
- ffmpeg和opencv 播放视频文件和显示器
ffmpeg它是基于最新版本,在官网下载http://ffmpeg.zeranoe.com/builds/.编译时VS2010配置相关头文件及库的路径就可以.opencv的搭建參考上一个博客. 首先简 ...
- STM32音乐播放器,文件查找的实现
使用FATFS只是完成了一个基本的文件读写,有时候我们需要扩展一些功能,比如MP3实验,需要上一曲下一曲的切换,扩展的代码如下 //显示目录下所有文件 u8 ShowFileList(u8* dirP ...
- Qt 播放音频文件
Qt播放音频文件的方法有好多中,简单介绍几种 不过一下几种方式都需要在Qt工程文件中添加 QT += multimedia 第一 QMediaPlayer类 可以播放MP3文件,同时使用也 ...
随机推荐
- iOS开发之JSON & XML
1.概述 JSON (1) 作为一种轻量级的数据交换格式,正在逐步取代XML,成为网络数据的通用格式 (2) 基于JavaScript的一个子集 (3) 易读性略差,编码手写难度大,数据量小 (4) ...
- 在TFS中通过程序动态创建Bug并感知Bug解决状态
为便于跟踪问题解决情况,预警引擎产生的比较严重的预警日志,需要在TFS中登记Bug,通过TFS的状态流转,利用TFS Bug的Web挂钩功能,动态感知Bug解决状态,从而跟踪预警问题的解决状态, 整体 ...
- SVN使用小记
SVN(Subversion)是优秀的版本控制工具,之前在eclipse里面项目管理的时候,File-->Import-->SVN-->从SVN检出项目-->创建新的资源库位置 ...
- Sass实战 sass官网
Sass实战 sass官网 1.相关视频教程:http://pan.baidu.com/s/1eSl8bUa 1.1我的项目源码:http://pan.baidu.com/s/1dFmqbyp 1.2 ...
- 安全性测试之防范 DDoS 攻击
安全性测试之防范 DDoS 攻击 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:90882 ...
- 老李分享:Uber究竟是用什么开发语言?
poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-845052 ...
- emmet(快速开发)的使用
emmet可以帮助您快速编写HTML和CSS代码,从而加速Web前端开发. 比如<html>.<head>.<body>等,现在你只需要1秒钟就可以输入这些标签. ...
- ConcurrentHashMap总结
线程不安全的HashMap 因为多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap,如以下代码 final HashM ...
- Spring Dubbo 开发笔记
第一节:概述 Spring-Dubbo 是我自己写的一个基于spring-boot和dubbo,目的是使用Spring boot的风格来使用dubbo.(即可以了解Spring boot的启动过程又可 ...
- dubbo+zipkin调用链监控
分布式环境下,对于线上出现问题往往比单体应用要复杂的多,原因是前端的一个请求可能对应后端多个系统的多个请求,错综复杂. 对于快速问题定位,我们一般希望是这样的: 从下到下关键节点的日志,入参,出差,异 ...