关于解决用tutorial7教程中的代码打造一款自己的播放器中的声音噪音问题
////////////////////////////////////////////////////////////////////////////////////////////
对于用FFMPEG2.01和SDL2.01最新的版本来做音频播放器,这篇文章和版本是很有值得参考的价值
这篇文章解决了我在做简易播放器的时候,用tutorial07的代码的时候,声音播放出现杂音的问题
出现杂音的问题原因可以从http://blog.csdn.net/leixiaohua1020/article/details/10528443中找到原因,
因为:
“新版本中使用了最新版本的FFMPEG类库(2014.5.7)。
FFMPEG在新版本中的音频解码方面发生了比较大的变化。
如果将旧版的主程序和新版的类库组合使用的话,会出现听到的都是杂音这一现象。
经过研究发现,新版中avcodec_decode_audio4()解码后输出的音频采样数据格式为AV_SAMPLE_FMT_FLTP(float, planar)
而不再是AV_SAMPLE_FMT_S16(signed 16 bits)。因此无法直接使用SDL进行播放。
最后的解决方法是使用SwrContext对音频采样数据进行转换之后,再进行输出播放,问题就可以得到解决了。”
/*=======================来自:http://www.cnblogs.com/lidabo/p/3701074.html================================*/
FFMPEG + SDL音频播放分析
目录 [hide]
1 抽象流程:
2 关键实现:
2.1 main()函数
2.2 decode_thread()读取文件信息和音频包
2.3 stream_component_open():设置音频参数和打开设备
2.4 audio_callback(): 回调函数,向SDL缓冲区填充数据
2.5 audio_decode_frame():解码音频
3 FFMPEG结构体
3.1 channel_layout_map
4 FFMPEG宏定义
4.1 Audio channel convenience macros
5 SDL宏定义
5.1 SDL_AudioSpec format
抽象流程:
设置SDL的音频参数 —-> 打开声音设备,播放静音 —-> ffmpeg读取音频流中数据放入队列 —-> SDL调用用户设置的函数来获取音频数据 —-> 播放音频
SDL内部维护了一个buffer来存放解码后的数据,这个buffer中的数据来源是我们注册的回调函数(audio_callback),
audio_callback调用audio_decode_frame来做具体的音频解码工作,需要引起注意的是:
从流中读取出的一个音频包(avpacket)可能含有多个音频桢(avframe),所以需要多次调用avcodec_decode_audio4来完成整个包的解码,
解码出来的数据存放在我们自己的缓冲中(audio_buf2)。SDL每一次回调都会引起数据从audio_buf2拷贝到SDL内部缓冲区,
当audio_buf2中的数据大于SDL的缓冲区大小时,需要分多次拷贝。
关键实现:
- main()函数
- int main(int argc, char **argv){
- SDL_Event event; //SDL事件变量
- VideoState *is; // 纪录视频及解码器等信息的大结构体
- is = (VideoState*) av_mallocz(sizeof(VideoState));
- if(argc < ){
- fprintf(stderr, "Usage: play <file>\n");
- exit();
- }
- av_register_all(); //注册所有ffmpeg的解码器
- /* 初始化SDL,这里只实用了AUDIO,如果有视频,好需要SDL_INIT_VIDEO等等 */
- if(SDL_Init(SDL_INIT_AUDIO)){
- fprintf(stderr, "Count not initialize SDL - %s\n", SDL_GetError());
- exit();
- }
- is_strlcpy(is->filename, argv[], sizeof(is->filename));
- /* 创建一个SDL线程来做视频解码工作,主线程进入SDL事件循环 */
- is->parse_tid = SDL_CreateThread(decode_thread, is);
- if(!is->parse_tid){
- SDL_WaitEvent(&event);
- switch(event.type){
- case FF_QUIT_EVENT:
- case SDL_QUIT:
- is->quit = ;
- SDL_Quit();
- exit();
- break;
- default:
- break;
- }
- }
- return ;
- }
decode_thread()读取文件信息和音频包:
- static int decode_thread(void *arg){
- VideoState *is = (VideoState*)arg;
- AVFormatContext *ic = NULL;
- AVPacket pkt1, *packet = &pkt1;
- int ret, i, audio_index = -;
- is->audioStream = -;
- global_video_state = is;
- /* 使用ffmpeg打开视频,解码器等 常规工作 */
- if(avFormat_open_input(&ic, is->filename, NULL, NULL) != ) {
- fprintf(stderr, "open file error: %s\n", is->filename);
- return -;
- }
- is->ic = ic;
- if(avformat_find_stream_info(ic, NULL) < ){
- fprintf(stderr, "find stream info error\n");
- return -;
- }
- av_dump_format(ic, , is->filename, );
- for(i = ; i < ic->nb_streams; i++){
- if(ic->streams[i])->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index == -){
- audio_index = i;
- break;
- }
- }
- if(audio_index >= ) {
- /* 所有设置SDL音频流信息的步骤都在这个函数里完成 */
- stream_component_open(is, audio_index);
- }
- if(is->audioStream < ){
- fprintf(stderr, "could not open codecs for file: %s\n", is->filename);
- goto fail;
- }
- /* 读包的主循环, av_read_frame不停的从文件中读取数据包(这里只取音频包)*/
- for(;;){
- if(is->quit) break;
- /* 这里audioq.size是指队列中的所有数据包带的音频数据的总量,并不是包的数量 */
- if(is->audioq.size > MAX_AUDIO_SIZE){
- SDL_Delay(); // 毫秒
- continue;
- }
- ret = av_read_frame(is->ic, packet);
- if(ret < ){
- if(ret == AVERROR_EOF || url_feof(is->ic->pb)) break;
- if(is->ic->pb && is->ic->pb->error) break;
- contiue;
- }
- if(packet->stream_index == is->audioStream){
- packet_queue_put(&is->audioq, packet);
- } else{
- av_free_packet(packet);
- }
- }
- while(!is->quit) SDL_Delay();
- fail: {
- SDL_Event event;
- event.type = FF_QUIT_EVENT;
- event.user.data1 = is;
- SDL_PushEvent(&event);
- }
- return ;
- }
stream_component_open():设置音频参数和打开设备
- int stream_component_open(videoState *is, int stream_index){
- AVFormatContext *ic = is->ic;
- AVCodecContext *codecCtx;
- AVCodec *codec;
- /* 在用SDL_OpenAudio()打开音频设备的时候需要这两个参数*/
- /* wanted_spec是我们期望设置的属性,spec是系统最终接受的参数 */
- /* 我们需要检查系统接受的参数是否正确 */
- SDL_AudioSpec wanted_spec, spec;
- int64_t wanted_channel_layout = ; // 声道布局(SDL中的具体定义见“FFMPEG结构体”部分)
- int wanted_nb_channels; // 声道数
- /* SDL支持的声道数为 1, 2, 4, 6 */
- /* 后面我们会使用这个数组来纠正不支持的声道数目 */
- const int next_nb_channels[] = { , , , , , , , };
- if(stream_index < || stream_index >= ic->nb_streams) return -;
- codecCtx = ic->streams[stream_index]->codec;
- wanted_nb_channels = codecCtx->channels;
- if(!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
- wanted_channel_layout = av_get_default_channel_lauout(wanted_channel_nb_channels);
- wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
- }
- wanted_spec.channels = av_get_channels_layout_nb_channels(wanted_channel_layout);
- wanted_spec.freq = codecCtx->sample_rate;
- if(wanted_spec.freq <= || wanted_spec.channels <=){
- fprintf(stderr, "Invaild sample rate or channel count!\n");
- return -;
- }
- wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分
- wanted_spec.silence = ; // 0指示静音
- wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; // 自定义SDL缓冲区大小
- wanted_spec.callback = audio_callback; // 音频解码的关键回调函数
- wanted_spec.userdata = is; // 传给上面回调函数的外带数据
- /* 打开音频设备,这里使用一个while来循环尝试打开不同的声道数(由上面 */
- /* next_nb_channels数组指定)直到成功打开,或者全部失败 */
- while(SDL_OpenAudio(&wanted_spec, &spec) < ){
- fprintf(stderr, "SDL_OpenAudio(%d channels): %s\n", wanted_spec.channels, SDL_GetError());
- wanted_spec.channels = next_nb_channels[FFMIN(, wanted_spec.channels)]; // FFMIN()由ffmpeg定义的宏,返回较小的数
- if(!wanted_spec.channels){
- fprintf(stderr, "No more channel to try\n");
- return -;
- }
- wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
- }
- /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */
- if(spec.format != AUDIO_S16SYS){
- fprintf(stderr, "SDL advised audio format %d is not supported\n", spec.format);
- return -;
- }
- if(spec.channels != wanted_spec.channels) {
- wanted_channel_layout = av_get_default_channel_layout(spec.channels);
- if(!wanted_channel_layout){
- fprintf(stderr, "SDL advised channel count %d is not support\n", spec.channels);
- return -;
- }
- }
- /* 把设置好的参数保存到大结构中 */
- is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16;
- is->audio_src_freq = is->audio_tgt_freq = spec.freq;
- is->audio_src_channel_layout = is->audio_tgt_layout = wanted_channel_layout;
- is->audio_src_channels = is->audio_tat_channels = spec.channels;
- codec = avcodec_find_decoder(codecCtx>codec_id);
- if(!codec || (avcodec_open2(codecCtx, codec, NULL) < )){
- fprintf(stderr, "Unsupported codec!\n");
- return -;
- }
- ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; //具体含义请查看“FFMPEG宏定义”部分
- is->audioStream = stream_index;
- is->audio_st = ic->streams[stream_index];
- is->audio_buf_size = ;
- is->audio_buf_index = ;
- memset(&is->audio_pkt, , sizeof(is->audio_pkt));
- packet_queue_init(&is->audioq);
- SDL_PauseAudio(); // 开始播放静音
- }
audio_callback(): 回调函数,向SDL缓冲区填充数据
- void audio_callback(void *userdata, Uint8 *stream, int len)
- {
- VideoState *is = (VideoState*)userdata;
- int len1, audio_data_size;
- /* len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */
- while(len > ){
- /* audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/
- /* 这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/
- /* 们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更
- /* 多的桢数据 */
- if(is->audio_buf_index >= is->audio_buf_size){
- audio_data_size = audio_decode_frame(is);
- /* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */
- if(audio_data_size < )
- {
- is->audio_buf_size = ;
- /* 清零,静音 */
- memset(is->audio_buf, , is->audio_buf_size);
- }
- else
- {
- is->audio_buf_size = audio_data_size;
- }
- is->audio_buf_index = ;
- }
- /* 查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */
- len1 = is->audio_buf_size - is->audio_buf_index;
- if(len1 > len) len1 = len;
- memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1);
- len -= len1;
- stream += len1;
- is->audio_buf_index += len1;
- }
- }
- audio_decode_frame():解码音频
- int audio_decode_frame(VideoState *is)
- {
- int len1, len2, decoded_data_size;
- AVPacket *pkt = &is->audio_pkt;
- int got_frame = ;
- int64_t dec_channel_layout;
- int wanted_nb_samples, resampled_data_size;
- for(;;)
- {
- while(is->audio_pkt_size > )
- {
- if(!is->audio_frame)
- {
- if(!(is->audio_frame = avacodec_alloc_frame()))
- {
- return AVERROR(ENOMEM);
- }
- }
- else
- avcodec_get_frame_defaults(is->audio_frame);
- len1 = avcodec_decode_audio4(is->audio_st_codec, is->audio_frame, got_frame, pkt);
- /* 解码错误,跳过整个包 */
- if(len1 < )
- {
- is->audio_pkt_size = ;
- break;
- }
- is->audio_pkt_data += len1;
- is->audio_pkt_size -= len1;
- if(!got_frame) continue;
- /* 计算解码出来的桢需要的缓冲大小 */
- decoded_data_size = av_samples_get_buffer_size(NULL,
- is->audio_frame_channels,
- is->audio_frame_nb_samples,
- is->audio_frame_format, );
- dec_channel_layout = (is->audio_frame->channel_layout && is->audio_frame->channels== av_get_channel_layout_nb_channels(is->audio_frame->channel_layout))
- ? is->audio_frame->channel_layout : av_get_default_channel_layout(is->audio_frame->channels);
- wanted_nb_samples = is->audio_frame->nb_samples;
- if (is->audio_frame->format != is->audio_src_fmt ||
- dec_channel_layout != is->audio_src_channel_layout ||
- is->audio_frame->sample_rate != is->audio_src_freq ||
- (wanted_nb_samples != is->audio_frame->nb_samples && !is->swr_ctx))
- {
- if (is->swr_ctx)
- swr_free(&is->swr_ctx);
- is->swr_ctx = swr_alloc_set_opts(NULL,
- is->audio_tgt_channel_layout,
- is->audio_tgt_fmt,
- is->audio_tgt_freq,
- dec_channel_layout,
- is->audio_frame->format,
- is->audio_frame->sample_rate,
- , NULL);
- if (!is->swr_ctx || swr_init(is->swr_ctx) < )
- {
- fprintf(stderr, "swr_init() failed\n");
- break;
- }
- is->audio_src_channel_layout = dec_channel_layout;
- is->audio_src_channels = is->audio_st->codec->channels;
- is->audio_src_freq = is->audio_st->codec->sample_rate;
- is->audio_src_fmt = is->audio_st->codec->sample_fmt;
- }
- /* 这里我们可以对采样数进行调整,增加或者减少,一般可以用来做声画同步 */
- if (is->swr_ctx)
- {
- const uint8_t **in = (const uint8_t **)is->audio_frame->extended_data;
- uint8_t *out[] = { is->audio_buf2 };
- if (wanted_nb_samples != is->audio_frame->nb_samples)
- {
- if(swr_set_compensation(is->swr_ctx,
- (wanted_nb_samples - is->audio_frame->nb_samples)*is->audio_tgt_freq/is->audio_frame->sample_rate,
- wanted_nb_samples * is->audio_tgt_freq/is->audio_frame->sample_rate) < )
- {
- fprintf(stderr, "swr_set_compensation() failed\n");
- break;
- }
- }
- len2 = swr_convert(is->swr_ctx, out, sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt),
- in, is->audio_frame->nb_samples);
- if (len2 < )
- {
- fprintf(stderr, "swr_convert() failed\n");
- break;
- }
- if(len2 == sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt))
- {
- fprintf(stderr, "warning: audio buffer is probably too small\n");
- swr_init(is->swr_ctx);
- }
- is->audio_buf = is->audio_buf2;
- resampled_data_size = len2*is->audio_tgt_channels*av_get_bytes_per_sample(is->audio_tgt_fmt);
- } else {
- resampled_data_size = decoded_data_size;
- is->audio_buf = is->audio_frame->data[];
- }
- /* 返回得到的数据 */
- return resampled_data_size;
- }
- if (pkt->data) av_free_packet(pkt);
- memset(pkt, , sizeof(*pkt));
- if (is->quit) return -;
- if (packet_queue_get(&is->audioq, pkt, ) < ) return -;
- is->audio_pkt_data = pkt->data;
- is->audio_pkt_size = pkt->size;
- }
- }
FFMPEG结构体
channel_layout_map
1 static const struct {
2 const char *name;
3 int nb_channels;
4 uint64_t layout;
5 } channel_layout_map[] = {
6 { "mono", 1, AV_CH_LAYOUT_MONO },
7 { "stereo", 2, AV_CH_LAYOUT_STEREO },
8 { "2.1", 3, AV_CH_LAYOUT_2POINT1 },
9 { "3.0", 3, AV_CH_LAYOUT_SURROUND },
10 { "3.0(back)", 3, AV_CH_LAYOUT_2_1 },
11 { "4.0", 4, AV_CH_LAYOUT_4POINT0 },
12 { "quad", 4, AV_CH_LAYOUT_QUAD },
13 { "quad(side)", 4, AV_CH_LAYOUT_2_2 },
14 { "3.1", 4, AV_CH_LAYOUT_3POINT1 },
15 { "5.0", 5, AV_CH_LAYOUT_5POINT0_BACK },
16 { "5.0(side)", 5, AV_CH_LAYOUT_5POINT0 },
17 { "4.1", 5, AV_CH_LAYOUT_4POINT1 },
18 { "5.1", 6, AV_CH_LAYOUT_5POINT1_BACK },
19 { "5.1(side)", 6, AV_CH_LAYOUT_5POINT1 },
20 { "6.0", 6, AV_CH_LAYOUT_6POINT0 },
21 { "6.0(front)", 6, AV_CH_LAYOUT_6POINT0_FRONT },
22 { "hexagonal", 6, AV_CH_LAYOUT_HEXAGONAL },
23 { "6.1", 7, AV_CH_LAYOUT_6POINT1 },
24 { "6.1", 7, AV_CH_LAYOUT_6POINT1_BACK },
25 { "6.1(front)", 7, AV_CH_LAYOUT_6POINT1_FRONT },
26 { "7.0", 7, AV_CH_LAYOUT_7POINT0 },
27 { "7.0(front)", 7, AV_CH_LAYOUT_7POINT0_FRONT },
28 { "7.1", 8, AV_CH_LAYOUT_7POINT1 },
29 { "7.1(wide)", 8, AV_CH_LAYOUT_7POINT1_WIDE },
30 { "octagonal", 8, AV_CH_LAYOUT_OCTAGONAL },
31 { "downmix", 2, AV_CH_LAYOUT_STEREO_DOWNMIX, },
32 };
FFMPEG宏定义
Audio channel convenience macros
1 #define AV_CH_LAYOUT_MONO (AV_CH_FRONT_CENTER)
2 #define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
3 #define AV_CH_LAYOUT_2POINT1 (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
4 #define AV_CH_LAYOUT_2_1 (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
5 #define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
6 #define AV_CH_LAYOUT_3POINT1 (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)
7 #define AV_CH_LAYOUT_4POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)
8 #define AV_CH_LAYOUT_4POINT1 (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)
9 #define AV_CH_LAYOUT_2_2 (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
10 #define AV_CH_LAYOUT_QUAD (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
11 #define AV_CH_LAYOUT_5POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
12 #define AV_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)
13 #define AV_CH_LAYOUT_5POINT0_BACK (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
14 #define AV_CH_LAYOUT_5POINT1_BACK (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY)
15 #define AV_CH_LAYOUT_6POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER)
16 #define AV_CH_LAYOUT_6POINT0_FRONT (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
17 #define AV_CH_LAYOUT_HEXAGONAL (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER)
18 #define AV_CH_LAYOUT_6POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER)
19 #define AV_CH_LAYOUT_6POINT1_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER)
20 #define AV_CH_LAYOUT_6POINT1_FRONT (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY)
21 #define AV_CH_LAYOUT_7POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
22 #define AV_CH_LAYOUT_7POINT0_FRONT (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
23 #define AV_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
24 #define AV_CH_LAYOUT_7POINT1_WIDE (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
25 #define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
26 #define AV_CH_LAYOUT_OCTAGONAL (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT)
27 #define AV_CH_LAYOUT_STEREO_DOWNMIX (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT)
SDL宏定义
SDL_AudioSpec format
1 AUDIO_U8 Unsigned 8-bit samples
2 AUDIO_S8 Signed 8-bit samples
3 AUDIO_U16LSB Unsigned 16-bit samples, in little-endian byte order
4 AUDIO_S16LSB Signed 16-bit samples, in little-endian byte order
5 AUDIO_U16MSB Unsigned 16-bit samples, in big-endian byte order
6 AUDIO_S16MSB Signed 16-bit samples, in big-endian byte order
7 AUDIO_U16 same as AUDIO_U16LSB (for backwards compatability probably)
8 AUDIO_S16 same as AUDIO_S16LSB (for backwards compatability probably)
9 AUDIO_U16SYS Unsigned 16-bit samples, in system byte order
10 AUDIO_S16SYS Signed 16-bit samples, in system byte order
git clone https://github.com/lnmcc/musicPlayer.git
关于解决用tutorial7教程中的代码打造一款自己的播放器中的声音噪音问题的更多相关文章
- 微信小程序(有始有终,全部代码)开发---跑步App+音乐播放器 Bug修复
开篇语 昨晚发了一篇: <简年15: 微信小程序(有始有终,全部代码)开发---跑步App+音乐播放器 > 然后上午起来吃完午饭之后,我就准备继续开工的,但是突然的,想要看B站.然后在一股 ...
- 【大结局】《从案例中学习JavaScript》之酷炫音乐播放器(四)
这是之前写的用H5制作的音乐播放器,前三节其实已经做得差不多了,音轨的制作原理已经在上一节说明,不过一直还没有和音乐对接. 本章作为该系列的一个完结篇,我会专门把动态音轨的实现代码贴出来,demo地址 ...
- 无法将从VSS中的解决方案添加到TFS的源代码管理器中
VSS是一种非常有用的项目文件管理工具,百度百科的解释是:VSS 的全称为 Visual Source Safe .作为 Microsoft Visual Studio 的一名成员,它主要任务就是负责 ...
- TFS 中如何将项目加入已有的源代码管理器中?
Visual Studio 的某解决方案已经加入 Team Foundation Server,现在再将已经存在的项目加入到解决方案中,可是签入时,并没有把新加入的项目签入,怎么办呢? 在团队资源管理 ...
- OpenGL学习脚印: uniform blocks在着色器中的使用 转自https://blog.csdn.net/wangdingqiaoit/article/details/52717963
写在前面 目前,我们在着色器中要传递多个uniform变量时,总是使用多个uniform,然后在主程序中设置这些变量的值:同时如果要在多个shader之间共享变量,例如投影矩阵projection和视 ...
- VS2010/VS2013中ashx代码折叠的问题
Tools->Options->TextEditor->File Extension Add ashx Microsoft Visual C# Apply OK 重启VS就可以了,效 ...
- 解决 git 中文路径显示 unicode 代码的问题
解决 git 中文路径显示 unicode 代码的问题 当被修改的文件中带有中文字符时,中文字符会被转换为 unicode 代码,看不出原来的文件名. 这时,只要配置 :: git config -- ...
- 好代码是管出来的——.Net中的代码规范工具及使用
上一篇文章介绍了编码标准中一些常用的工具,本篇就具体来介绍如何使用它们来完成代码管理. 本文主要内容有: Roslyn简介 开发基于Roslyn的代码分析器 常用的基于Roslyn的代码分析器 在.N ...
- 第49章 在SRAM中调试代码—零死角玩转STM32-F429系列
第49章 在SRAM中调试代码 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
随机推荐
- ubuntu14中创建python虚拟环境
一.安装python-virtualenv包 sudo apt-get install python-virtualenv 安装完成后,创建一个虚拟环境文件夹. mkdir VENVcd VENV 创 ...
- Windows 7/8/10 系统下Laravel框架的开发环境安装及部署详解(Vagrant + Homestead)
注意! laravel/homestead box项目地址已经不再是原来的 https://atlas.hashicorp.com/laravel/boxes/homestead 而已经变更成 htt ...
- C#字符串中特殊字符的转义
再基础的东西不常用的话就得记下来...不然就忘记了. 比如C#中对字符串中特殊字符的转义,一个是双引号",另一个就是转义符\ 对于同样一个字符串:地址:"C:\Users\E.tx ...
- [na]mail收发过程
以前老记不住这smtp和pop3谁收谁发. 简单邮件传输协议(SMTP),用来发送或中转发出的电子邮件, 占用tcp 25端口. 第三版邮局协议(POP3),用于将服务器上把邮件存储到本地 ...
- UIActivityIndicatorView的详细使用
转自:http://www.cnblogs.com/top5/archive/2012/05/17/2506623.html UIActivityIndicatorView实例提供轻型视图,这些视图显 ...
- CCNotificationCenter(二)---NotificationCenterTest
//类的定义 #ifndef __NOTIFICATIONCENTERTEST_H__ #define __NOTIFICATIONCENTERTEST_H__ #include "coco ...
- gnome3增加自定义程序快捷方式
gnome3增加自定义程序快捷方式 1. 安装alacarte 在命令行输入下列命令安装alacarte程序 yum -y install alacarte 安装完毕后,在命令行输入下 ...
- 回调形成树形结构tree
//Tree.vue <template> <li> <span @click="toggle"> <i v-if="isFol ...
- C++中冒号和双冒号的用法
1.冒号(:)用法 (1)表示机构内位域的定义(即该变量占几个bit空间) typedef struct _XXX{ unsigned char a:4; unsigned char c; } ; X ...
- Java中HashMap实现原理
类声明: 概述: 线程不安全: <Key, Value>两者都可以为null: 不保证映射的顺序,特别是它不保证该顺序恒久不变: HashMap使用Iterator: HashMap中ha ...