一、背景
1、当播放网络视频流时(比如udp视频流),发送方(编码)和接收方(解码)是并行操作的,如果发送太慢(或因为网络原因出现延迟)的话,接收方将不能及时得到数据,导致解码出错,所以需要对接收buffer进行管理。
2、编码器会把自身的参考时钟(PCR)保存到视频流中,供解码器同步用。对于网络视频流,VLC也通过接收的PCR信息与自身系统时钟比较,以计算网络延迟并相应的调节接收buffer。

二、buffer管理(pts_delay)
1、input_clock

/*****************************************************************************
 * input_clock_Update: manages a clock reference
 *
 *  i_ck_stream: date in stream clock
 *  i_ck_system: date in system clock
 *****************************************************************************/
void input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
                         bool *pb_late,
                         bool b_can_pace_control, bool b_buffering_allowed,
                         mtime_t i_ck_stream, mtime_t i_ck_system )
{
    bool b_reset_reference = false;

assert( i_ck_stream > VLC_TS_INVALID && i_ck_system > VLC_TS_INVALID );

vlc_mutex_lock( &cl->lock );

if( !cl->b_has_reference )   //失去参考        
    {
       /* */
        b_reset_reference= true;   //重新同步    
    }
   else if( cl->last.i_stream > VLC_TS_INVALID &&
             ( (cl->last.i_stream - i_ck_stream) > CR_MAX_GAP ||
               (cl->last.i_stream - i_ck_stream) < -CR_MAX_GAP ) )  //delay超过60s,视为Stream discontinuity。
    {
        /* Stream discontinuity, for which we haven't received a
         * warning from the stream control facilities (dd-edited
         * stream ?). */    //严重错误,不应该发生。
        msg_Warn( p_log, "clock gap, unexpected stream discontinuity" );
        cl->i_ts_max = VLC_TS_INVALID;

/* */
        msg_Warn( p_log, "feeding synchro with a new reference point trying to recover from clock gap" );
        b_reset_reference= true;   //重新同步
    }

/* */
    if( b_reset_reference )
    {
        cl->i_next_drift_update = VLC_TS_INVALID;
        AvgReset( &cl->drift );   //复位平均统计值

/* Feed synchro with a new reference point. */
        cl->b_has_reference = true;
        cl->ref = clock_point_Create( i_ck_stream,
                                      __MAX( cl->i_ts_max + CR_MEAN_PTS_GAP, i_ck_system ) );   //建立ref clock point
        cl->b_has_external_clock = false;
    }

/* Compute the drift between the stream clock and the system clock
     * when we don't control the source pace */
    if( !b_can_pace_control && cl->i_next_drift_update < i_ck_system )
    {
        const mtime_t i_converted = ClockSystemToStream( cl, i_ck_system );

AvgUpdate( &cl->drift, i_converted - i_ck_stream ); //计算并更新ck_system与ck_stream的偏差(采用累积平均算法减少波动)      
        cl->i_next_drift_update = i_ck_system + CLOCK_FREQ/5; /* FIXME why that */    }    /* Update the extra buffering value */    if( !b_can_pace_control || b_reset_reference )    {        cl->i_buffering_duration = 0;   //Amount of extra buffering expressed in stream clock(复位)        }    else if( b_buffering_allowed )   //input_DecoderGetFifoSize不多,允许es_out缓冲。            {        /* Try to bufferize more than necessary by reading         * CR_BUFFERING_RATE/256 faster until we have CR_BUFFERING_TARGET.         */        const mtime_t i_duration = __MAX( i_ck_stream - cl->last.i_stream, 0 );        cl->i_buffering_duration += ( i_duration * CR_BUFFERING_RATE + 255 ) / 256;   //???        if( cl->i_buffering_duration > CR_BUFFERING_TARGET )            cl->i_buffering_duration = CR_BUFFERING_TARGET;    }    //fprintf( stderr, "input_clock_Update: %d :: %lld\n", b_buffering_allowed, cl->i_buffering_duration/1000 );    /* */    cl->last = clock_point_Create( i_ck_stream, i_ck_system );  //记录last clock point      /* It does not take the decoder latency into account but it is not really     * the goal of the clock here */    const mtime_t i_system_expected = ClockStreamToSystem( cl, i_ck_stream + AvgGet( &cl->drift ) );    //给ck_stream加上上面计算的平均差值,转化为对应的system_expected    const mtime_t i_late = ( i_ck_system - cl->i_pts_delay ) - i_system_expected;   //计算是否超出延迟设置(需要增大接收buffer?)    *pb_late = i_late > 0;    if( i_late > 0 )    {        cl->late.pi_value[cl->late.i_index] = i_late;   //记录延迟情况(作为要增大多少buffer的依据)        cl->late.i_index = ( cl->late.i_index + 1 ) % INPUT_CLOCK_LATE_COUNT;    }    vlc_mutex_unlock( &cl->lock );}void input_clock_SetJitter( input_clock_t *cl,                            mtime_t i_pts_delay, int i_cr_average ){    vlc_mutex_lock( &cl->lock );    /* Update late observations */    const mtime_t i_delay_delta = i_pts_delay - cl->i_pts_delay;    mtime_t pi_late[INPUT_CLOCK_LATE_COUNT];    for( int i = 0; i < INPUT_CLOCK_LATE_COUNT; i++ )        pi_late[i] = __MAX( cl->late.pi_value[(cl->late.i_index + 1 + i)%INPUT_CLOCK_LATE_COUNT] - i_delay_delta, 0 );    for( int i = 0; i < INPUT_CLOCK_LATE_COUNT; i++ )        cl->late.pi_value[i] = 0;    cl->late.i_index = 0;    for( int i = 0; i < INPUT_CLOCK_LATE_COUNT; i++ )    {        if( pi_late[i] <= 0 )            continue;        cl->late.pi_value[cl->late.i_index] = pi_late[i];        cl->late.i_index = ( cl->late.i_index + 1 ) % INPUT_CLOCK_LATE_COUNT;    }    /* TODO always save the value, and when rebuffering use the new one if smaller     * TODO when increasing -> force rebuffering     */    if( cl->i_pts_delay < i_pts_delay )        cl->i_pts_delay = i_pts_delay;     //更新pts_delay!!!         /* */    if( i_cr_average < 10 )        i_cr_average = 10;    if( cl->drift.i_divider != i_cr_average )        AvgRescale( &cl->drift, i_cr_average );    vlc_mutex_unlock( &cl->lock );}

2、es_out

    case ES_OUT_SET_PCR:
    case ES_OUT_SET_GROUP_PCR:
    {
.
        /* TODO do not use mdate() but proper stream acquisition date */
        bool b_late;
        input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input),
                            &b_late,
                            p_sys->p_input->p->b_can_pace_control || p_sys->b_buffering,
                            EsOutIsExtraBufferingAllowed( out ),
                            i_pcr, mdate() );

if( !p_sys->p_pgrm )
            return VLC_SUCCESS;

if( p_sys->b_buffering )
        {
            /* Check buffering state on master clock update */
            EsOutDecodersStopBuffering( out, false );
        }
        else if( p_pgrm == p_sys->p_pgrm )
        {
            if( b_late && ( !p_sys->p_input->p->p_sout ||
                                 !p_sys->p_input->p->b_out_pace_control ) )
            {
                const mtime_t i_pts_delay_base = p_sys->i_pts_delay - p_sys->i_pts_jitter;   //系统当前的pts_delay值(一开始通过用户的"network-caching"初始化,后续根据延迟统计情况动态更新)
                mtime_t i_pts_delay = input_clock_GetJitter( p_pgrm->p_clock );   //获取上面统计的平均延迟

/* Avoid dangerously high value */
                const mtime_t i_jitter_max = INT64_C(1000) * var_InheritInteger( p_sys->p_input, "clock-jitter" );
                if( i_pts_delay > __MIN( i_pts_delay_base + i_jitter_max, INPUT_PTS_DELAY_MAX ) )   //pts_delay > 5s (严重延迟)                   
                {  
                     msg_Err( p_sys->p_input,
                             "ES_OUT_SET_(GROUP_)PCR  is called too late (jitter of %d ms ignored)",
                             (int)(i_pts_delay - i_pts_delay_base) / 1000 );
                    i_pts_delay = p_sys->i_pts_delay;   //恢复用户设置的pts_delay

/* reset clock */
                    for( int i = 0; i < p_sys->i_pgrm; i++ )
                      input_clock_Reset( p_sys->pgrm[i]->p_clock ); //复位时钟,重新同步。                
                }
               else
                {
                    msg_Err( p_sys->p_input,
                             "ES_OUT_SET_(GROUP_)PCR  is called too late (pts_delay increased to %d ms)",
                             (int)(i_pts_delay/1000) );   //延迟在范围内(5s),增加pts_delay为统计值。

/* Force a rebufferization when we are too late */

/* It is not really good, as we throw away already buffered data
                     * TODO have a mean to correctly reenter bufferization */
                    es_out_Control( out, ES_OUT_RESET_PCR );
                }

es_out_SetJitter( out, i_pts_delay_base, i_pts_delay - i_pts_delay_base, p_sys->i_cr_average );   //更新p_sys->i_pts_delay和p_sys->i_pts_jitter            }        }        return VLC_SUCCESS;    }    case ES_OUT_SET_JITTER:    {        mtime_t i_pts_delay  = (mtime_t)va_arg( args, mtime_t );        mtime_t i_pts_jitter = (mtime_t)va_arg( args, mtime_t );        int     i_cr_average = (int)va_arg( args, int );        bool b_change_clock =            i_pts_delay + i_pts_jitter != p_sys->i_pts_delay ||            i_cr_average != p_sys->i_cr_average;        assert( i_pts_jitter >= 0 );        p_sys->i_pts_delay  = i_pts_delay + i_pts_jitter;        p_sys->i_pts_jitter = i_pts_jitter;        p_sys->i_cr_average = i_cr_average;        for( int i = 0; i < p_sys->i_pgrm && b_change_clock; i++ )            input_clock_SetJitter( p_sys->pgrm[i]->p_clock,                                   i_pts_delay + i_pts_jitter, i_cr_average );        return VLC_SUCCESS;    }static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )   //汇报buffer进度/等待decoder fifo empty{    es_out_sys_t *p_sys = out->p_sys;    int i_ret;    mtime_t i_stream_start;    mtime_t i_system_start;    mtime_t i_stream_duration;    mtime_t i_system_duration;    if (input_clock_GetState( p_sys->p_pgrm->p_clock,                                  &i_stream_start, &i_system_start,                                  &i_stream_duration, &i_system_duration ))        return;    mtime_t i_preroll_duration = 0;    if( p_sys->i_preroll_end >= 0 )        i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );    const mtime_t i_buffering_duration = p_sys->i_pts_delay +                                         i_preroll_duration +                                         p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;   //计算接收缓冲的长度(时间)    if( i_stream_duration <= i_buffering_duration && !b_forced )   //还buffer完            {double f_level;        if (i_buffering_duration == 0)            f_level = 0;        else            f_level = __MAX( (double)i_stream_duration / i_buffering_duration, 0 );        input_SendEventCache( p_sys->p_input, f_level );   //INPUT_EVENT_CACHE        msg_Dbg( p_sys->p_input, "Buffering %d%%", (int)(100 * f_level) );        return;    }    input_SendEventCache( p_sys->p_input, 1.0 );   //INPUT_EVENT_CACHE    msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",              (int)(i_stream_duration/1000), (int)(i_system_duration/1000) );    p_sys->b_buffering = false;   //buffer完,标志之,除非EsOutChangePosition或EsOutFrameNext,否则不再buffer。    p_sys->i_preroll_end = -1;    if( p_sys->i_buffering_extra_initial > 0 )    {        /* FIXME wrong ? */        return;    }    const mtime_t i_decoder_buffering_start = mdate();    for( int i = 0; i < p_sys->i_es; i++ )    {        es_out_id_t *p_es = p_sys->es[i];        if( !p_es->p_dec || p_es->fmt.i_cat == SPU_ES )            continue;        input_DecoderWait( p_es->p_dec );   //等待解码器准备好(开始工作)        if( p_es->p_dec_record )            input_DecoderWait( p_es->p_dec_record );    }    msg_Dbg( p_sys->p_input, "Decoder wait done in %d ms",              (int)(mdate() - i_decoder_buffering_start)/1000 );    /* Here is a good place to destroy unused vout with every demuxer */    input_resource_TerminateVout( p_sys->p_input->p->p_resource );    /* */    const mtime_t i_wakeup_delay = 10*1000; /* FIXME CLEANUP thread wake up time*/    const mtime_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : mdate();    input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, true,                                    i_current_date + i_wakeup_delay - i_buffering_duration );  //
实际buffer的数据一般要比用户设置的(i_buffering_duration,其实主要是i_pts_delay)要长,这时候要算出多出来的
部分,然后调整本地时钟(ref.i_system),以补偿buffer过程中引入的缓冲时间,避免解码和显示的时候报错。(建议调试的时候,关掉时钟
同步,容易分析)    for( int i = 0; i < p_sys->i_es; i++ )    {        es_out_id_t *p_es = p_sys->es[i];        if( !p_es->p_dec )            continue;        input_DecoderStopWait( p_es->p_dec );   //buffer前调用input_DecoderStartWait;buffer完后,调用input_DecoderStopWait。        if( p_es->p_dec_record )            input_DecoderStopWait( p_es->p_dec_record );    }}

3、p_sys->i_pts_delay初始值

src/input/input.c

static int InputSourceInit( input_thread_t *p_input,
                            input_source_t *in, const char *psz_mrl,
                            const char *psz_forced_demux, bool b_in_can_fail )
{
.
        if( !p_input->b_preparsing )
        {
            bool b;

stream_Control( p_stream, STREAM_CAN_CONTROL_PACE,
                            &in->b_can_pace_control );  
            in->b_can_rate_control = in->b_can_pace_control;   //false

in->b_rescale_ts = true;

stream_Control( p_stream, STREAM_CAN_PAUSE, &in->b_can_pause );   //false
            var_SetBool( p_input, "can-pause",
                         in->b_can_pause || !in->b_can_pace_control ); /* XXX temporary because of es_out_timeshift*/
            var_SetBool( p_input, "can-rate",
                         !in->b_can_pace_control || in->b_can_rate_control ); /* XXX temporary because of es_out_timeshift*/
            var_SetBool( p_input, "can-rewind",
                         !in->b_rescale_ts && !in->b_can_pace_control );

stream_Control( p_stream, STREAM_CAN_SEEK, &b );   //false
            var_SetBool( p_input, "can-seek", b );

in->b_title_demux = false;

stream_Control( p_stream, STREAM_GET_PTS_DELAY, &i_pts_delay );
        }
....

    if( !p_input->b_preparsing )
    {
        int i_attachment;
        input_attachment_t **attachment;
        if( !demux_Control( in->p_demux, DEMUX_GET_ATTACHMENTS,
                             &attachment, &i_attachment ) )
        {
            vlc_mutex_lock( &p_input->p->p_item->lock );
            AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment, &p_input->p->attachment_demux,
                              i_attachment, attachment, in->p_demux );
            vlc_mutex_unlock( &p_input->p->p_item->lock );
        }

/* PTS delay: request from demux first. This is required for
         * access_demux and some special cases like SDP demux. Otherwise,
         * fallback to access */
        if( demux_Control( in->p_demux, DEMUX_GET_PTS_DELAY,
                           &in->i_pts_delay ) )
            in->i_pts_delay = i_pts_delay;   //把i_pts_delay的值赋给in->i_pts_delay
        if( in->i_pts_delay > INPUT_PTS_DELAY_MAX )
            in->i_pts_delay = INPUT_PTS_DELAY_MAX;
        else if( in->i_pts_delay < 0 )
            in->i_pts_delay = 0;
    }

static void UpdatePtsDelay( input_thread_t *p_input )
{
    input_thread_private_t *p_sys = p_input->p;

/* Get max pts delay from input source */
    mtime_t i_pts_delay = p_sys->input.i_pts_delay;
    for( int i = 0; i < p_sys->i_slave; i++ )
        i_pts_delay = __MAX( i_pts_delay, p_sys->slave[i]->i_pts_delay );

if( i_pts_delay < 0 )
        i_pts_delay = 0;

/* Take care of audio/spu delay */
    const mtime_t i_audio_delay = var_GetTime( p_input, "audio-delay" );
    const mtime_t i_spu_delay   = var_GetTime( p_input, "spu-delay" );
    const mtime_t i_extra_delay = __MIN( i_audio_delay, i_spu_delay );
    if( i_extra_delay < 0 )
        i_pts_delay -= i_extra_delay;

/* Update cr_average depending on the caching */
    const int i_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;   //计算i_pts_delay对应的i_cr_average

/* */
    es_out_SetDelay( p_input->p->p_es_out_display, AUDIO_ES, i_audio_delay );    es_out_SetDelay( p_input->p->p_es_out_display, SPU_ES, i_spu_delay );    es_out_SetJitter( p_input->p->p_es_out, i_pts_delay, 0, i_cr_average );   //设置p_sys->i_pts_delay初始值(详细见上面的ES_OUT_SET_JITTER分支)}

modules/access/udp.c:

        case ACCESS_GET_PTS_DELAY:
            pi_64 = (int64_t*)va_arg( args, int64_t * );
            *pi_64 = INT64_C(1000)
                   * var_InheritInteger(p_access, "network-caching");   //通过“network-caching”变量获取i_pts_delay初始值

break;

三、时钟同步(drift)
Data caching is mainly a way to cope with streams which have unreliable 
sending rates (be it network/server latency, jitter, bursts, etc). Now 
if you need heavy caching, that more than likely means you have large 
changes in your receiving rate, so you'll want to increase cr-average to 
smooth out these changes.

This is why the default caching in the udp access module is pretty low (udp 
is mostly used for reliable / low delay streaming), while you have a high 
caching value for http.

I do agree that the problems you raise need to be fixed, although I don't 
think your fixes are appropriate.

The clock synchro algo is there to keep the playback speed synchronised with 
the server, not only to avoid buffer underruns / overruns, but also to keep 
a low delay from the server (depending on the value of caching set by the 
user).

The problem with VLC's clock is that it does its synchro by comparing the 
time of arrival of data with the timestamp set on it by the server, aka the 
PCR (which btw is why the prebuffering introduced lately might not be such 
a good idea).
This method is appropriate for very low delay streaming which doesn't have 
much jitter and bursts in the transmission, but it isn't for anything else 
(and remember, VLC was designed as an intranet streaming client).
If VLC is allowed to cache enough data, then a more sensible way to do this 
synchro is to actually look at how much data we have in our cache (in units 
of time), and if this value is above or below some treshold then start 
adapting the clock. It might still be interesting (or not) to keep using 
the current synchro algo for very low delay streams though.

1、drift计算
请参考前面的"input_clock_Update"

void input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
                         bool *pb_late,
                         bool b_can_pace_control, bool b_buffering_allowed,
                         mtime_t i_ck_stream, mtime_t i_ck_system )
{
.
    if( !b_can_pace_control && cl->i_next_drift_update < i_ck_system )
    {
        const mtime_t i_converted = ClockSystemToStream( cl, i_ck_system );

AvgUpdate( &cl->drift, i_converted - i_ck_stream ); //计算并更新平均偏移
        cl->i_next_drift_update = i_ck_system + CLOCK_FREQ/5; /* FIXME why that */
    }

2、drift的使用

int input_clock_ConvertTS( input_clock_t *cl,
                           int *pi_rate, mtime_t *pi_ts0, mtime_t *pi_ts1,
                           mtime_t i_ts_bound )
{
.
    /* */
    const mtime_t i_ts_buffering = cl->i_buffering_duration * cl->i_rate / INPUT_RATE_DEFAULT;
    const mtime_t i_ts_delay = cl->i_pts_delay + ClockGetTsOffset( cl );

/* */
    if( *pi_ts0 > VLC_TS_INVALID )
    {
        *pi_ts0 = ClockStreamToSystem( cl, *pi_ts0 + AvgGet( &cl->drift ) );   //补偿时钟偏移
        if( *pi_ts0 > cl->i_ts_max )            cl->i_ts_max = *pi_ts0;        *pi_ts0 += i_ts_delay;    }

vlc_input buffer管理 & 时钟同步(转)的更多相关文章

  1. CentOS时钟同步服务器

    ①本地时钟服务器需要安装chrony服务,可以通过yum.rpm.源码包安装,chrony支持C/S模式 ②编辑本地时钟服务,使其指向提供标准时间服务器,例如:中国国家授时中心NTP服务器. 修改配置 ...

  2. linux GPU上多个buffer间的同步 —— ww_mutex、dma-fence的使用 笔记

    原文链接:https://www.cnblogs.com/yaongtime/p/14111134.html   WW-Mutexes   在GPU中一次Render可能会涉及到对多个buffer的引 ...

  3. buildroot ntp 网络时钟同步

    /********************************************************************** * buildroot ntp 网络时钟同步 * 说明: ...

  4. ffmpeg的内部Video Buffer管理和传送机制

    ffmpeg的内部Video Buffer管理和传送机制 本文主要介绍ffmpeg解码器内部管理Video Buffer的原理和过程,ffmpeg的Videobuffer为内部管理,其流程大致为:注册 ...

  5. Linux下的ntp时钟同步问题

    前段时间,项目中有个需求,需要将linux和windows的时间进行同步,网上也有很多类似时钟同步的帖子,大致类似:不过本次的linux的机器有点特殊,没有service命令,而且要求在另一台suse ...

  6. (3)I2C总线的字节格式,时钟同步和仲裁

    字节格式 发送到SDA线上的每个字节必须是8位.每次传输的字节数量是不受限制的.每个字节后必须跟着一个ACK应答位.数据从最高有效位(MSB)开始传输.如果从机要执行一些功能后才能接收或者发送新的完整 ...

  7. 大型分布式C++框架《四:netio之buffer管理器 下》

    每周一篇又来了.这次主要介绍netio的buffer管理器. 首先buffer管理是每一个网络层不可回避的问题.怎么高效的使用buffer是很关键的问题.这里主要介绍下我们的netio是怎么处理.说实 ...

  8. ntp 时钟同步

    注意: 如果你无法和外部网络的时钟同步,请检查UDP端口时候被封.

  9. 关闭VirtualBox虚拟机的时钟同步

    原文链接:关闭VirtualBox虚拟机的时钟同步 在VirtualBox的虚拟机上默认虚拟机的时间是会和物理机同步的,但可以通过下面的命令来关闭 1. 首先查看虚拟机列表 VBoxManage li ...

随机推荐

  1. Node.js权威指南 (8) - 创建HTTP与HTTPS服务器及客户端

    8.1 HTTP服务器 / 177 8.1.1 创建HTTP服务器 / 177 8.1.2 获取客户端请求信息 / 182 8.1.3 转换URL字符串与查询字符串 / 184 8.1.4 发送服务器 ...

  2. 启动tomcat的 startup.bat屏幕一闪而过

    有时启动tomcat 时,屏幕一闪而过,看不到是那里有问题.要想让屏幕停下来,做法如下: 1.打开 startup.bat 文件,在文件最后加上最后加一行@pause 2.重新运行 startup.b ...

  3. HDU-4089 Activation

    http://acm.hdu.edu.cn/showproblem.php?pid=4089 Activation Time Limit: 20000/10000 MS (Java/Others)   ...

  4. codevs 1421 秋静叶&秋穣子(树上DP+博弈)

    1421 秋静叶&秋穣子   题目描述 Description 在幻想乡,秋姐妹是掌管秋天的神明,作为红叶之神的姐姐静叶和作为丰收之神的妹妹穰子.如果把红叶和果实联系在一 起,自然会想到烤红薯 ...

  5. 用xib文件,配置UITableViewCell

    http://www.cnblogs.com/lixingle/p/3287499.html 在运行时候,如果出现“this class is not key value coding-complia ...

  6. [Hibernate] 注解映射例子

    Hibernate 注解(Hibernate Annotation) 是一种比较新的方式,通过在 java 简单类增加注解,来声明 java 类和数据库表的映射,作用和 xml 文件相似.hibern ...

  7. linux0.12 编译过程

    感谢这篇文章的作者:    http://www.cnblogs.com/strugglesometimes/p/4231359.html 编译是个很蛋疼的事情,本想把linux0.12在bochs上 ...

  8. Counting - SGU 117(快速幂)

    题目大意:求下面N个数里面有多少个数的M次方能整除K 代码如下: ======================================================== #include&l ...

  9. ll 详解

    长选项必须使用的参数对于短选项时也是必需使用的.  -a, --all                     不隐藏任何以. 开始的项目  -A, --almost-all             ...

  10. 找不到这个cache.properties缓存文件

    Android  Studio在导入第三库同步时报错: C:\Users\Administrator\.gradle\caches\2.4\scripts\asLocalRepo88_4u65z0u2 ...