热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

srs推flv流_SRS流媒体服务器之HLS源码分析(3)

0.引言阅读本文前,可以先阅读前面的文章,能够帮助你更好理解本篇文章。文章列表如下:SRS流媒体服务器之HLS源码分析(1)SRS流媒体服务器之HLS源

0.引言

阅读本文前,可以先阅读前面的文章,能够帮助你更好理解本篇文章。文章列表如下:

SRS流媒体服务器之HLS源码分析(1)


SRS流媒体服务器之HLS源码分析(2)

SRS流媒体服务器之HLS配置、测试和技术选型

SRS流媒体服务器集群之Edge模式(3)

SRS流媒体服务器集群之Edge模式(2)

SRS流媒体服务器集群之Edge模式(1)

SRS流媒体服务器集群之Forward模式(2)

SRS流媒体服务器集群之Forward模式(1)

SRS流媒体服务器之HTTP-FLV框架分析(1)

SRS流媒体服务器之RTMP推流消息处理(1)

SRS流媒体服务器之RTMP协议分析(2)

SRS流媒体框架分析(1)

SRS流媒体之RTMP推流框架分析(2)

SRS流媒体之RTMP拉流框架分析(3)

SRS流媒体服务器之RTMP协议分析(1)

简述SRS流媒体服务器相关技术

流媒体推拉流实战之RTMP协议分析(BAT面试官推荐)

流媒体服务器架构与应用分析

手把手搭建流媒体服务器详细步骤

手把手搭建FFmpeg的Windows环境

超详细手把手搭建在ubuntu系统的FFmpeg环境

本篇文章主要讲解的是在推流时,音频数据是如何通过RTMP协议转化为HLS的ts封装,视频也是类似。以源码的方式进行分析。

e5f7ea75cd34d3c189bcc57935e4713c.png

1.源码分析

SrsSharedPtrMessage是rtmp的消息格式。

srs_error_t SrsSource::on_audio_imp(SrsSharedPtrMessage* msg){ srs_error_t err &#61; srs_success; bool is_aac_sequence_header &#61; SrsFlvAudio::sh(msg->payload, msg->size); bool is_sequence_header &#61; is_aac_sequence_header; // whether consumer should drop for the duplicated sequence header. bool drop_for_reduce &#61; false; if (is_sequence_header && meta->previous_ash() && _srs_config->get_reduce_sequence_header(req->vhost)) { if (meta->previous_ash()->size &#61;&#61; msg->size) { drop_for_reduce &#61; srs_bytes_equals(meta->previous_ash()->payload, msg->payload, msg->size); srs_warn("drop for reduce sh audio, size&#61;%d", msg->size); } } // copy to all consumer if (!drop_for_reduce) { for (int i &#61; 0; i <(int)consumers.size(); i&#43;&#43;) { SrsConsumer* consumer &#61; consumers.at(i); if ((err &#61; consumer->enqueue(msg, atc, jitter_algorithm)) !&#61; srs_success) { return srs_error_wrap(err, "consume message"); } } } // Copy to hub to all utilities. if ((err &#61; hub->on_audio(msg)) !&#61; srs_success) { return srs_error_wrap(err, "consume audio"); } // cache the sequence header of aac, or first packet of mp3. // for example, the mp3 is used for hls to write the "right" audio codec. // TODO: FIXME: to refine the stream info system. if (is_aac_sequence_header || !meta->ash()) { if ((err &#61; meta->update_ash(msg)) !&#61; srs_success) { return srs_error_wrap(err, "meta consume audio"); } } // when sequence header, donot push to gop cache and adjust the timestamp. if (is_sequence_header) { return err; } // cache the last gop packets if ((err &#61; gop_cache->cache(msg)) !&#61; srs_success) { return srs_error_wrap(err, "gop cache consume audio"); } // if atc, update the sequence header to abs time. if (atc) { if (meta->ash()) { meta->ash()->timestamp &#61; msg->timestamp; } if (meta->data()) { meta->data()->timestamp &#61; msg->timestamp; } } return err;}

把Rtmp封装好的数据&#xff0c;转为音视频裸数据。

srs_error_t SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio){ srs_error_t err &#61; srs_success; SrsSharedPtrMessage* msg &#61; shared_audio; //转换封装格式 if ((err &#61; format->on_audio(msg)) !&#61; srs_success) { return srs_error_wrap(err, "format consume audio"); } // cache the sequence header if aac // donot cache the sequence header to gop_cache, return here. if (format->is_aac_sequence_header()) { srs_assert(format->acodec); SrsAudioCodecConfig* c &#61; format->acodec; static int flv_sample_sizes[] &#61; {8, 16, 0}; static int flv_sound_types[] &#61; {1, 2, 0}; // when got audio stream info. SrsStatistic* stat &#61; SrsStatistic::instance(); if ((err &#61; stat->on_audio_info(req, SrsAudioCodecIdAAC, c->sound_rate, c->sound_type, c->aac_object)) !&#61; srs_success) { return srs_error_wrap(err, "stat audio"); } srs_trace("%dB audio sh, codec(%d, profile&#61;%s, %dchannels, %dkbps, %dHZ), flv(%dbits, %dchannels, %dHZ)", msg->size, c->id, srs_aac_object2str(c->aac_object).c_str(), c->aac_channels, c->audio_data_rate / 1000, srs_aac_srates[c->aac_sample_rate], flv_sample_sizes[c->sound_size], flv_sound_types[c->sound_type], srs_flv_srates[c->sound_rate]); } if ((err &#61; hls->on_audio(msg, format)) !&#61; srs_success) { // apply the error strategy for hls. // &#64;see https://github.com/ossrs/srs/issues/264 std::string hls_error_strategy &#61; _srs_config->get_hls_on_error(req->vhost); if (srs_config_hls_is_on_error_ignore(hls_error_strategy)) { srs_warn("hls: ignore audio error %s", srs_error_desc(err).c_str()); hls->on_unpublish(); srs_error_reset(err); } else if (srs_config_hls_is_on_error_continue(hls_error_strategy)) { if (srs_hls_can_continue(srs_error_code(err), source->meta->ash(), msg)) { srs_error_reset(err); } else { return srs_error_wrap(err, "hls: audio"); } } else { return srs_error_wrap(err, "hls: audio"); } } if ((err &#61; dash->on_audio(msg, format)) !&#61; srs_success) { srs_warn("dash: ignore audio error %s", srs_error_desc(err).c_str()); srs_error_reset(err); dash->on_unpublish(); } if ((err &#61; dvr->on_audio(msg, format)) !&#61; srs_success) { srs_warn("dvr: ignore audio error %s", srs_error_desc(err).c_str()); srs_error_reset(err); dvr->on_unpublish(); } #ifdef SRS_AUTO_HDS if ((err &#61; hds->on_audio(msg)) !&#61; srs_success) { srs_warn("hds: ignore audio error %s", srs_error_desc(err).c_str()); srs_error_reset(err); hds->on_unpublish(); }#endif // copy to all forwarders. if (true) { std::vector::iterator it; for (it &#61; forwarders.begin(); it !&#61; forwarders.end(); &#43;&#43;it) { SrsForwarder* forwarder &#61; *it; if ((err &#61; forwarder->on_audio(msg)) !&#61; srs_success) { return srs_error_wrap(err, "forward: audio"); } } } return err;}

转换封装格式&#xff0c;提取音视频裸流数据&#xff0c;源码如下:

srs_error_t SrsRtmpFormat::on_audio(SrsSharedPtrMessage* shared_audio){ SrsSharedPtrMessage* msg &#61; shared_audio; //主要是取出msg的数据部分 char* data &#61; msg->payload; int size &#61; msg->size; return SrsFormat::on_audio(msg->timestamp, data, size);}

由这种SrsSharedPtrMessage格式的数据转换为新的SrsFormat格式的数据。

srs_error_t SrsFormat::on_audio(int64_t timestamp, char* data, int size){ srs_error_t err &#61; srs_success; if (!data || size <&#61; 0) { srs_trace("no audio present, ignore it."); return err; } SrsBuffer* buffer &#61; new SrsBuffer(data, size); SrsAutoFree(SrsBuffer, buffer); // We already checked the size is positive and data is not NULL. srs_assert(buffer->require(1)); // &#64;see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76 uint8_t v &#61; buffer->read_1bytes(); SrsAudioCodecId codec &#61; (SrsAudioCodecId)((v >> 4) & 0x0f); if (codec !&#61; SrsAudioCodecIdMP3 && codec !&#61; SrsAudioCodecIdAAC) { return err; } if (!acodec) { acodec &#61; new SrsAudioCodecConfig(); } if (!audio) { audio &#61; new SrsAudioFrame(); } if ((err &#61; audio->initialize(acodec)) !&#61; srs_success) { return srs_error_wrap(err, "init audio"); } // Parse by specified codec. buffer->skip(-1 * buffer->pos()); if (codec &#61;&#61; SrsAudioCodecIdMP3) { return audio_mp3_demux(buffer, timestamp); } return audio_aac_demux(buffer, timestamp);}

以aac距离&#xff0c;解封装&#xff0c;并加入aac头&#xff0c;写入cache中。

srs_error_t SrsFrame::add_sample(char* bytes, int size){ srs_error_t err &#61; srs_success; if (nb_samples >&#61; SrsMaxNbSamples) { return srs_error_new(ERROR_HLS_DECODE_ERROR, "Frame samples overflow"); } SrsSample* sample &#61; &samples[nb_samples&#43;&#43;]; sample->bytes &#61; bytes; sample->size &#61; size; return err;}

添加aac或其他格式数据。

srs_error_t SrsHlsController::write_audio(SrsAudioFrame* frame, int64_t pts){ srs_error_t err &#61; srs_success; // write audio to cache. if ((err &#61; tsmc->cache_audio(frame, pts)) !&#61; srs_success) { return srs_error_wrap(err, "hls: cache audio"); } // reap when current source is pure audio. // it maybe changed when stream info changed, // for example, pure audio when start, audio/video when publishing, // pure audio again for audio disabled. // so we reap event when the audio incoming when segment overflow. // &#64;see https://github.com/ossrs/srs/issues/151 // we use absolutely overflow of segment to make jwplayer/ffplay happy // &#64;see https://github.com/ossrs/srs/issues/151#issuecomment-71155184 if (tsmc->audio && muxer->is_segment_absolutely_overflow()) { if ((err &#61; reap_segment()) !&#61; srs_success) { return srs_error_wrap(err, "hls: reap segment"); } } // for pure audio, aggregate some frame to one. // TODO: FIXME: Check whether it&#39;s necessary. if (muxer->pure_audio() && tsmc->audio) { if (pts - tsmc->audio->start_pts flush_audio(tsmc)) !&#61; srs_success) { return srs_error_wrap(err, "hls: flush audio"); } return err;}

先写数据到cache中&#xff0c;最后把cache的数据合并写入ts文件中。

srs_error_t SrsHlsController::write_audio(SrsAudioFrame* frame, int64_t pts){ srs_error_t err &#61; srs_success; // write audio to cache. if ((err &#61; tsmc->cache_audio(frame, pts)) !&#61; srs_success) { return srs_error_wrap(err, "hls: cache audio"); } // reap when current source is pure audio. // it maybe changed when stream info changed, // for example, pure audio when start, audio/video when publishing, // pure audio again for audio disabled. // so we reap event when the audio incoming when segment overflow. // &#64;see https://github.com/ossrs/srs/issues/151 // we use absolutely overflow of segment to make jwplayer/ffplay happy // &#64;see https://github.com/ossrs/srs/issues/151#issuecomment-71155184 if (tsmc->audio && muxer->is_segment_absolutely_overflow()) { if ((err &#61; reap_segment()) !&#61; srs_success) { return srs_error_wrap(err, "hls: reap segment"); } } // for pure audio, aggregate some frame to one. // TODO: FIXME: Check whether it&#39;s necessary. if (muxer->pure_audio() && tsmc->audio) { if (pts - tsmc->audio->start_pts flush_audio(tsmc)) !&#61; srs_success) { return srs_error_wrap(err, "hls: flush audio"); } return err;}

写音频数据到cache中。

srs_error_t SrsTsMessageCache::cache_audio(SrsAudioFrame* frame, int64_t dts){ srs_error_t err &#61; srs_success; // create the ts audio message. if (!audio) { audio &#61; new SrsTsMessage(); audio->write_pcr &#61; false; audio->dts &#61; audio->pts &#61; audio->start_pts &#61; dts; } // TODO: FIXME: refine code. //audio->dts &#61; dts; //audio->pts &#61; audio->dts; audio->sid &#61; SrsTsPESStreamIdAudioCommon; // must be aac or mp3 SrsAudioCodecConfig* acodec &#61; frame->acodec(); srs_assert(acodec->id &#61;&#61; SrsAudioCodecIdAAC || acodec->id &#61;&#61; SrsAudioCodecIdMP3); // write video to cache. if (acodec->id &#61;&#61; SrsAudioCodecIdAAC) { if ((err &#61; do_cache_aac(frame)) !&#61; srs_success) { return srs_error_wrap(err, "ts: cache aac"); } } else { if ((err &#61; do_cache_mp3(frame)) !&#61; srs_success) { return srs_error_wrap(err, "ts: cache mp3"); } } return err;}

以aac举例&#xff0c;写cache时&#xff0c;注意写ADTS头:

srs_error_t SrsTsContextWriter::write_audio(SrsTsMessage* audio){ srs_error_t err &#61; srs_success; srs_info("hls: write audio pts&#61;%" PRId64 ", dts&#61;%" PRId64 ", size&#61;%d", audio->pts, audio->dts, audio->PES_packet_length); if ((err &#61; context->encode(writer, audio, vcodec, acodec)) !&#61; srs_success) { return srs_error_wrap(err, "ts: write audio"); } srs_info("hls encode audio ok"); return err;}

按照ts的封装格式&#xff0c;写入文件:

// SrsTsMessage* msg指向的数据&#xff0c;存储的是裸流数据&#xff0c;这里的AAC(带ADTS头)srs_error_t SrsTsContext::encode(ISrsStreamWriter* writer, SrsTsMessage* msg, SrsVideoCodecId vc, SrsAudioCodecId ac){ srs_error_t err &#61; srs_success; SrsTsStream vs, as; int16_t video_pid &#61; 0, audio_pid &#61; 0; switch (vc) { case SrsVideoCodecIdAVC: vs &#61; SrsTsStreamVideoH264; video_pid &#61; TS_VIDEO_AVC_PID; break; case SrsVideoCodecIdDisabled: vs &#61; SrsTsStreamReserved; break; case SrsVideoCodecIdReserved: case SrsVideoCodecIdReserved1: case SrsVideoCodecIdReserved2: case SrsVideoCodecIdSorensonH263: case SrsVideoCodecIdScreenVideo: case SrsVideoCodecIdOn2VP6: case SrsVideoCodecIdOn2VP6WithAlphaChannel: case SrsVideoCodecIdScreenVideoVersion2: case SrsVideoCodecIdHEVC: case SrsVideoCodecIdAV1: vs &#61; SrsTsStreamReserved; break; } switch (ac) { case SrsAudioCodecIdAAC: as &#61; SrsTsStreamAudioAAC; audio_pid &#61; TS_AUDIO_AAC_PID; break; case SrsAudioCodecIdMP3: as &#61; SrsTsStreamAudioMp3; audio_pid &#61; TS_AUDIO_MP3_PID; break; case SrsAudioCodecIdDisabled: as &#61; SrsTsStreamReserved; break; case SrsAudioCodecIdReserved1: case SrsAudioCodecIdLinearPCMPlatformEndian: case SrsAudioCodecIdADPCM: case SrsAudioCodecIdLinearPCMLittleEndian: case SrsAudioCodecIdNellymoser16kHzMono: case SrsAudioCodecIdNellymoser8kHzMono: case SrsAudioCodecIdNellymoser: case SrsAudioCodecIdReservedG711AlawLogarithmicPCM: case SrsAudioCodecIdReservedG711MuLawLogarithmicPCM: case SrsAudioCodecIdReserved: case SrsAudioCodecIdSpeex: case SrsAudioCodecIdReservedMP3_8kHz: case SrsAudioCodecIdReservedDeviceSpecificSound: case SrsAudioCodecIdOpus: as &#61; SrsTsStreamReserved; break; } if (as &#61;&#61; SrsTsStreamReserved && vs &#61;&#61; SrsTsStreamReserved) { return srs_error_new(ERROR_HLS_NO_STREAM, "ts: no a/v stream, vcodec&#61;%d, acodec&#61;%d", vc, ac); } // when any codec changed, write PAT/PMT table. //PAT和PMT表 if (vcodec !&#61; vc || acodec !&#61; ac) { vcodec &#61; vc; acodec &#61; ac; if ((err &#61; encode_pat_pmt(writer, video_pid, vs, audio_pid, as)) !&#61; srs_success) { return srs_error_wrap(err, "ts: encode PAT/PMT"); } } // encode the media frame to PES packets over TS. if (msg->is_audio()) { //编码为PES包 return encode_pes(writer, msg, audio_pid, as, vs &#61;&#61; SrsTsStreamReserved); } else { return encode_pes(writer, msg, video_pid, vs, vs &#61;&#61; SrsTsStreamReserved); }}

使用上下文SrsTsContext的方法进行封装&#xff0c;这里的源码就是按照ts格式进行写入&#xff0c;比如这里先封装成pes数据。也可以参考关于ts的文章以及spec。

// SrsTsMessage* msg指向的数据&#xff0c;存储的是裸流数据&#xff0c;这里的AAC(带ADTS头)srs_error_t SrsTsContext::encode(ISrsStreamWriter* writer, SrsTsMessage* msg, SrsVideoCodecId vc, SrsAudioCodecId ac){ srs_error_t err &#61; srs_success; SrsTsStream vs, as; int16_t video_pid &#61; 0, audio_pid &#61; 0; switch (vc) { case SrsVideoCodecIdAVC: vs &#61; SrsTsStreamVideoH264; video_pid &#61; TS_VIDEO_AVC_PID; break; case SrsVideoCodecIdDisabled: vs &#61; SrsTsStreamReserved; break; case SrsVideoCodecIdReserved: case SrsVideoCodecIdReserved1: case SrsVideoCodecIdReserved2: case SrsVideoCodecIdSorensonH263: case SrsVideoCodecIdScreenVideo: case SrsVideoCodecIdOn2VP6: case SrsVideoCodecIdOn2VP6WithAlphaChannel: case SrsVideoCodecIdScreenVideoVersion2: case SrsVideoCodecIdHEVC: case SrsVideoCodecIdAV1: vs &#61; SrsTsStreamReserved; break; } switch (ac) { case SrsAudioCodecIdAAC: as &#61; SrsTsStreamAudioAAC; audio_pid &#61; TS_AUDIO_AAC_PID; break; case SrsAudioCodecIdMP3: as &#61; SrsTsStreamAudioMp3; audio_pid &#61; TS_AUDIO_MP3_PID; break; case SrsAudioCodecIdDisabled: as &#61; SrsTsStreamReserved; break; case SrsAudioCodecIdReserved1: case SrsAudioCodecIdLinearPCMPlatformEndian: case SrsAudioCodecIdADPCM: case SrsAudioCodecIdLinearPCMLittleEndian: case SrsAudioCodecIdNellymoser16kHzMono: case SrsAudioCodecIdNellymoser8kHzMono: case SrsAudioCodecIdNellymoser: case SrsAudioCodecIdReservedG711AlawLogarithmicPCM: case SrsAudioCodecIdReservedG711MuLawLogarithmicPCM: case SrsAudioCodecIdReserved: case SrsAudioCodecIdSpeex: case SrsAudioCodecIdReservedMP3_8kHz: case SrsAudioCodecIdReservedDeviceSpecificSound: case SrsAudioCodecIdOpus: as &#61; SrsTsStreamReserved; break; } if (as &#61;&#61; SrsTsStreamReserved && vs &#61;&#61; SrsTsStreamReserved) { return srs_error_new(ERROR_HLS_NO_STREAM, "ts: no a/v stream, vcodec&#61;%d, acodec&#61;%d", vc, ac); } // when any codec changed, write PAT/PMT table. //PAT和PMT表 if (vcodec !&#61; vc || acodec !&#61; ac) { vcodec &#61; vc; acodec &#61; ac; if ((err &#61; encode_pat_pmt(writer, video_pid, vs, audio_pid, as)) !&#61; srs_success) { return srs_error_wrap(err, "ts: encode PAT/PMT"); } } // encode the media frame to PES packets over TS. if (msg->is_audio()) { //编码为PES包 return encode_pes(writer, msg, audio_pid, as, vs &#61;&#61; SrsTsStreamReserved); } else { return encode_pes(writer, msg, video_pid, vs, vs &#61;&#61; SrsTsStreamReserved); }}

最终到hls流时&#xff0c;都是已经经过处理过的数据。

断点调试&#xff0c;输入如下命令:

b SrsFrame::add_sample()

c

如下界面:

de08f4907e46aea7f7b640b289cae466.png

查看调用栈(调用关系是从下到上&#xff0c;即15到0&#xff0c;运行到断点处)&#xff0c;如下界面:

1e28db31efa1df1aecd44e6301e83fe0.png

0 SrsFrame::add_sample at src/kernel/srs_kernel_codec.cpp:4521 SrsFormat::audio_aac_demux at src/kernel/srs_kernel_codec.cpp:1252 in SrsFormat::on_audio (this&#61;0xa3b560, frame&#61;0xa41640, pts&#61;3060) at src/kernel/srs_kernel_codec.cpp:5873 in SrsRtmpFormat::on_audio (this&#61;0xa3b030, shared_audio&#61;0x7ffff7ee8b10, format&#61;0xa3bad0) at src/protocol/srs_protocol_format.cpp:534 0x00000000004deeaf in SrsOriginHub::on_audio (this&#61;0xa3b670, shared_audio&#61;0x7ffff7ee8b10) at src/app/srs_app_source.cpp:9695 0x00000000004e579a in SrsSource::on_audio_imp (this&#61;0xa3b1e0, msg&#61;0x7ffff7ee8b10) at src/app/srs_app_source.cpp:21916 0x00000000004e539d in SrsSource::on_audio (this&#61;0xa3b1e0, shared_audio&#61;0xa3dfa0) at src/app/srs_app_source.cpp:21417 0x00000000004d8f28 in SrsRtmpConn::process_publish_message (this&#61;0xa30d60, source&#61;0xa3b1e0, msg&#61;0xa3dfa0) at src/app/srs_app_rtmp_conn.cpp:10148 0x00000000004d8dce in SrsRtmpConn::handle_publish_message (this&#61;0xa30d60, source&#61;0xa3b1e0, msg&#61;0xa3dfa0) at src/app/srs_app_rtmp_conn.cpp:9939 0x00000000005810b6 in SrsPublishRecvThread::consume (this&#61;0x7ffff7f42800, msg&#61;0xa3dfa0) at src/app/srs_app_recv_thread.cpp:38910 0x000000000057fbd4 in SrsRecvThread::do_cycle (this&#61;0x7ffff7f42808)---Type to continue, or q to quit--- at src/app/srs_app_recv_thread.cpp:14611 0x000000000057fa25 in SrsRecvThread::cycle (this&#61;0x7ffff7f42808) at src/app/srs_app_recv_thread.cpp:11512 0x0000000000509c88 in SrsSTCoroutine::cycle (this&#61;0xa3de00) at src/app/srs_app_st.cpp:19813 0x0000000000509cfd in SrsSTCoroutine::pfn (arg&#61;0xa3de00) at src/app/srs_app_st.cpp:21314 _st_thread_main() at sched.c:33715 st_thread_create at sched.c:616

在hls处理音视频均打上断点&#xff0c;输入命令:

b SrsHls::on_video()

b SrsHls::on_audio()

c

界面如下:

36d082d00dd63e6815ef0877a366722a.png

打印音频封装格式&#xff0c;输入如下命令&#xff0c;如下界面:

print *format

739c08cceb4b1eb119d752f81ad8f3aa.png

表示一组采样数量&#xff0c;可能有多个帧:

print format->audio->nb_samples

105312f0feb2acaabf7d0522769c81ae.png

通过一层层的源码分析&#xff0c;封装格式的变化&#xff0c;都没有使用深拷贝&#xff0c;都是通过指针的方式&#xff0c;进行浅拷贝。

2.总结

本篇文章主要是讲解在SRS源码中是如何将音频的转封装格式进行转换&#xff0c;视频也是类似&#xff0c;希望能够帮到大家。欢迎关注&#xff0c;转发&#xff0c;点赞&#xff0c;收藏&#xff0c;分享&#xff0c;评论区讨论。

后期关于项目的知识&#xff0c;会在微信公众号上更新&#xff0c;如果想要学习项目&#xff0c;可以关注微信公众号“记录世界 from antonio”



推荐阅读
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
  • 本文回顾了作者初次接触Unicode编码时的经历,并详细探讨了ASCII、ANSI、GB2312、UNICODE以及UTF-8和UTF-16编码的区别和应用场景。通过实例分析,帮助读者更好地理解和使用这些编码。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
  • 本文详细解析了客户端与服务器之间的交互过程,重点介绍了Socket通信机制。IP地址由32位的4个8位二进制数组成,分为网络地址和主机地址两部分。通过使用 `ipconfig /all` 命令,用户可以查看详细的IP配置信息。此外,文章还介绍了如何使用 `ping` 命令测试网络连通性,例如 `ping 127.0.0.1` 可以检测本机网络是否正常。这些技术细节对于理解网络通信的基本原理具有重要意义。 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 性能测试中的关键监控指标与深入分析
    在软件性能测试中,关键监控指标的选取至关重要。主要目的包括:1. 评估系统的当前性能,确保其符合预期的性能标准;2. 发现软件性能瓶颈,定位潜在问题;3. 优化系统性能,提高用户体验。通过综合分析这些指标,可以全面了解系统的运行状态,为后续的性能改进提供科学依据。 ... [详细]
  • 本文探讨了如何通过编程手段在Linux系统中禁用硬件预取功能。基于Intel® Core™微架构的应用性能优化需求,文章详细介绍了相关配置方法和代码实现,旨在帮助开发人员有效控制硬件预取行为,提升应用程序的运行效率。 ... [详细]
  • Java Socket 关键参数详解与优化建议
    Java Socket 的 API 虽然被广泛使用,但其关键参数的用途却鲜为人知。本文详细解析了 Java Socket 中的重要参数,如 backlog 参数,它用于控制服务器等待连接请求的队列长度。此外,还探讨了其他参数如 SO_TIMEOUT、SO_REUSEADDR 等的配置方法及其对性能的影响,并提供了优化建议,帮助开发者提升网络通信的稳定性和效率。 ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 深入解析C语言中结构体的内存对齐机制及其优化方法
    为了提高CPU访问效率,C语言中的结构体成员在内存中遵循特定的对齐规则。本文详细解析了这些对齐机制,并探讨了如何通过合理的布局和编译器选项来优化结构体的内存使用,从而提升程序性能。 ... [详细]
author-avatar
手机用户2502902093
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有