ffmpeg编解码详细过程

 
 

bobbypollo

转:ffmpeg编解码详细过程

原文地址:ffmpeg编解码详细过程(转)作者:心在飞翔原文出处: http://www.360doc.com/content/11/1117/09/8050095_165108638.shtml

FFMPEG解码流程

1. 注册所有容器格式和CODEC:av_register_all()

2. 打开文件:av_open_input_file()

3. 从文件中提取流信息:av_find_stream_info()

4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO

5. 查找对应的解码器:avcodec_find_decoder()

6. 打开编解码器:avcodec_open()

7. 为解码帧分配内存:avcodec_alloc_frame()

8. 不停地从码流中提取出帧数据:av_read_frame()

9. 判断帧的类型,对于视频帧调用:avcodec_decode_video()

10. 解码完后,释放解码器:avcodec_close()

11. 关闭输入文件:av_close_input_file()

首先第一件事情就是开一个视频文件并从中得到流。我们要做的第一件事情就是使用av_register_all();来初始化libavformat/libavcodec:

这一步注册库中含有的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。av_register_all()只需调用一次,所以,要放在初始化代码中。也可以仅仅注册个人的文件格式和编码。

下一步,打开文件:

AVFormatContext *pFormatCtx;
const char     *filename="myvideo.mpg";
av_open_input_file(&pFormatCtx, filename, NULL, 0,NULL);   // 打开视频文件
最后三个参数描述了文件格式,缓冲区大小(size)和格式参数;我们通过简单地指明NULL或0告诉 libavformat去自动探测文件格式并且使用默认的缓冲区大小。这里的格式参数指的是视频输出参数,比如宽高的坐标。

下一步,我们需要取出包含在文件中的流信息:
av_find_stream_info(pFormatCtx);                               // 取出流信息

AVFormatContext 结构体

dump_format(pFormatCtx, 0, filename,false);//我们可以使用这个函数把获取到得参数全部输出。

for(i=0; i<pFormatCtx->nb_streams;i++)       //区分视频流和音频流
 if(pFormatCtx->streams->codec.codec_type==CODEC_TYPE_VIDEO)//找到视频流,这里也可以换成音频
    {
       videoStream=i;
       break;
    }

接下来就需要寻找解码器

AVCodec *pCodec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

avcodec_open(pCodecCtx,pCodec);    //打开解码器
给视频帧分配空间以便存储解码后的图片:

AVFrame *pFrame;
pFrame=avcodec_alloc_frame();

/////////////////////////////////////////开始解码///////////////////////////////////////////

第一步当然是读数据:

我们将要做的是通过读取包来读取整个视频流,然后把它解码成帧,最后转换格式并且保存。

while(av_read_frame(pFormatCtx,&packet)>=0) {    //读数据

if(packet.stream_index==videoStream){       //判断是否视频流

avcodec_decode_video(pCodecCtx,pFrame,&frameFinished,

packet.data, packet.size);                       //解码

if(frameFinished) {

img_convert((AVPicture *)pFrameRGB,PIX_FMT_RGB24,(AVPicture*)pFrame,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height);//转换 }

SaveFrame(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i); //保存数据

av_free_packet(&packet);                     //释放

av_read_frame()读取一个包并且把它保存到AVPacket结构体中。这些数据可以在后面通过av_free_packet()来释放。函数avcodec_decode_video()把包转换为帧。然而当解码一个包的时候,我们可能没有得到我们需要的关于帧的信息。因此,当我们得到下一帧的时候,avcodec_decode_video()为我们设置了帧结束标志frameFinished。最后,我们使用img_convert()函数来把帧从原始格式(pCodecCtx->pix_fmt)转换成为RGB格式。要记住,你可以把一个AVFrame结构体的指针转换为AVPicture结构体的指针。最后,我们把帧和高度宽度信息传递给我们的SaveFrame函数。

到此解码完毕,显示过程使用SDL完成考虑到我们以后会使用firmware进行显示操作,SDL忽略不讲。

音视频同步

DTS(解码时间戳)和PTS(显示时间戳)

当我们调用av_read_frame()得到一个包的时候,PTS和DTS的信息也会保存在包中。但是我们真正想要的PTS是我们刚刚解码出来的原始帧的PTS,这样我们才能知道什么时候来显示它。然而,我们从avcodec_decode_video()函数中得到的帧只是一个AVFrame,其中并没有包含有用的PTS值(注意:AVFrame并没有包含时间戳信息,但当我们等到帧的时候并不是我们想要的样子)。。我们保存一帧的第一个包的PTS:这将作为整个这一帧的PTS。我们可以通过函数avcodec_decode_video()来计算出哪个包是一帧的第一个包。怎样实现呢?任何时候当一个包开始一帧的时候,avcodec_decode_video()将调用一个函数来为一帧申请一个缓冲。当然,ffmpeg允许我们重新定义那个分配内存的函数。计算前一帧和现在这一帧的时间戳来预测出下一个时间戳的时间。同时,我们需要同步视频到音频。我们将设置一个音频时间audioclock;一个内部值记录了我们正在播放的音频的位置。就像从任意的mp3播放器中读出来的数字一样。既然我们把视频同步到音频,视频线程使用这个值来算出是否太快还是太慢。

用FFMPEGSDK进行视频转码压缩时解决音视频不同步问题的方法(转)

ffmpeg 2010-07-2119:54:16 阅读163 评论0

用FFMPEGSDK进行视频转码压缩的时候,转码成功后去看视频的内容,发现音视频是不同步的。这个的确是一个恼火的事情。我在用FFMPEGSDK做h264格式的FLV文件编码Filter的时候就碰到了这个问题。

经过研究发现,FFMPEG SDK写入视频的时候有两个地方用来控制写入的时间戳,一个是AvPacket, 一个是AvFrame。在调用avcodec_encode_video的时候需要传入AvFrame的对象指针,也就是传入一帧未压缩的视频进行压缩处理,AvFrame包含一个pts的参数,这个参数就是当前帧将来在还原播放的时候的时间戳。而AvPacket里面也有pts,还有dts。说起这个就必须要说明一下I,P,B三种视频压缩帧。I帧就是关键帧,不依赖于其他视频帧,P帧是向前预测的帧,只依赖于前面的视频帧,而B帧是双向预测视频帧,依赖于前后视频帧。由于B帧的存在,因为它是双向的,必须知道前面的视频帧和后面的视频帧的详细内容后,才能知道本B帧最终该呈现什么图像。而pts和dts两个参数就是用来控制视频帧的显示和解码的顺序。

pts就是帧显示的顺序。

dts就是帧被读取进行解码的顺序。

如果没有B帧存在,dts和pts是相同的。反之,则是不相同的。关于这个的详细介绍可以参考一下mpeg的原理。

再说说AvPacket中包含的pts和dts两个到底该设置什么值?

pts和dts需要设置的就是视频帧解码和显示的顺序。每增加一帧就加一,并不是播放视频的时间戳。

但是实践证明经过rmvb解码的视频有时候并不是固定帧率的,而是变帧率的,这样,如果每压缩一帧,pts和dts加一的方案为导致音视频不同步。

那怎么来解决音视频同步的问题呢?

请看如下代码段。

lTimeStamp 是通过directshow 获取的当前的视频帧的时间戳。

m_llframe_index为当前已经经过压缩处理的帧的数量。

首先av_rescale计算得到当前压缩处理已经需要处理什么时间戳的视频帧,如果该时间戳尚未到达directshow当前提供的视频帧的时间戳,则将该帧丢弃掉。

否则进行压缩操作。并设置AVPacket的pts和dts。这里假设B帧不存在。

因为在将来播放的时候视频以我们设定的固定播放帧率进行播放,所以需要根据设定的播放帧率计算得到的视频帧时间戳和directshow提供的当前视频帧的时间戳进行比较,设定是否需要进行实施延缓播放的策略。如果需要延缓播放,则将pts增加步长2,否则以普通速度播放,则设置为1.dts与之相同。
__int64 x=av_rescale(m_llframe_index,AV_TIME_BASE*(int64_t)c->time_base.num,c->time_base.den);

if( x > lTimeStamp )
{
return TRUE;
}
m_pVideoFrame2->pts = lTimeStamp;
m_pVideoFrame2->pict_type = 0;

int out_size = avcodec_encode_video( c, m_pvideo_outbuf,video_outbuf_size,m_pVideoFrame2 );

if (out_size > 0)
{
AVPacket pkt;
av_init_packet(&pkt);

if( x > lTimeStamp )
{
   pkt.pts = pkt.dts =m_llframe_index;
   pkt.duration = 0;
}
else
{
   pkt.duration = (lTimeStamp -x)*c->time_base.den/1000000 + 1;
   pkt.pts =m_llframe_index;
   pkt.dts = pkt.pts;
   m_llframe_index +=pkt.duration;
}

//pkt.pts = lTimeStamp * (__int64)frame_rate.den / 1000;
if( c->coded_frame&&c->coded_frame->key_frame )
{
    pkt.flags |=PKT_FLAG_KEY;
}

pkt.stream_index= m_pVideoStream->index;
pkt.data= m_pvideo_outbuf;
pkt.size= out_size;

ret = av_interleaved_write_frame( m_pAvFormatContext,&pkt );
}
else
{
ret = 0;
}

请问avcodec_decode_video解码的帧为什么后面的比前面的pts小呢?

请问如下代码:
while(av_read_frame(pFormatCtxSource,&packet)>=0)
{
    if(packet.stream_index==videoStream )
    {
       int out_size= avcodec_decode_video(pCodecCtxSource,pFrameSource,&bFrameFinished, packet.data, packet.size); //Decode fromsource frame

if(bFrameFinished )
       {
         pFrameSource->pts=av_rescale_q(packet.pts,pCodecCtxSource->time_base,pStCodec->time_base);
          int out_size=avcodec_encode_video(pStCodec, video_buffer, 200000,pFrameSource); // Encodeto output
          if(out_size>0 )
          {
             //...
          }
       }
    }

av_free_packet(&packet);

}

在我Decode的时候,第一帧得到的 pFrameSource->pts是96,再解第二帧的时候,pFrameSource->pts计算完后就成了80几,后几帧也是比96小,过一会又会解出来一个100多的,接下来又是比100多小的,这是为什么?在Encode的时候,先Encode一个pts=96的,再去Encode比96小的帧就返回-1了,直到找到一个比96大的。

另外,我计算pts的方法正确吗?

答复:

Because you have B - Frame

for example:

the Inputsequence for video encoder
1  2  3  4  5   6   7
I   B  B  P  B  B   I

Let's take1,2,3.. as PTS for simplification

the out sequencefor video encoder ( this equals the decodersequence)
1  4  2   3  7  5   6
I  P   B  B  I   B   B

you will get aPTS sequence as following:

1  4  2  3  7  5  6

7  5 6sequence will be same asyour question

问:

哦,那是不是我的pts不能这么算呢?而是要每次+1,对吗?那么,packet中的pts和dts要用在什么地方呢?我这样按存储顺序进行解码的话,显示之前是不是要自己进行缓存呢?谢谢!

另外,还有个问题,既然解码的时候,不一定是按照pts递增的顺序得到的解码后的画面,那我在编码图像的时候,是应该按照解码出来的帧顺序进行编码吗?还是把帧先缓存起来,最后严格接照图像的显示顺序来编码呢?用代码来表示,就是:
方法一:
while(av_read_frame )
{    
   解码;   
   pts+1;   
   编码;   
    输出;
}

方法二:
while(av_read_frame )
{
    解码;
   if(pts<previous )
    {
      缓存;
    }
   else
    {
      编码缓存的帧并写入文件;
    }
}

这两个方法,哪个是正确的呢?因为我看到网上的代码都用的是方法一,但是我觉得方法二是对的呀?

答:

the output of decoderis the right order for display because I/Pframes will be cacheduntil next I/P

理解:

Decoder 后output的pts 是按正常的顺序,即显示的顺序输出的,如果有B帧,decoder会缓存。

但encoder后,输出的是按dts输出的。

Pts,dts并不是时间戳,而更应该理解为frame的顺序序列号。由于每帧frame的帧率并不一定是一致的,可能会变化的。转换为时间戳的话,应该是(pts*帧率)。为加深理解

可以将pts比做是第pts帧frame,假设每帧的帧率不变的话,则显示的时间戳为(pts*帧率),如果考虑帧率变化的,则要想办法将(pts*当前的帧率)累加到后面。

在tutorial5中在decode 下增加trace后打印情况:

len1 =avcodec_decode_video(is->video_st->codec,pFrame,&frameFinished,

packet->data,packet->size);

printf("-----------------------------------------------------------------------------n");

printf("avcodec_decode_videopacket->pts:%x,packet->dts:%xn",packet->pts,packet->dts);

printf("avcodec_decode_videopFrame->pkt_pts:%x,pFrame->pkt_dts:%x,pFrame->pts:%xn",pFrame->pkt_pts,pFrame->pkt_dts,pFrame->pts);

if(pFrame->opaque)

printf("avcodec_decode_video*(uint64_t*)pFrame->opaque:%xn",*(uint64_t*)pFrame->opaque);

其中播一个mp4文件的打印情况:

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:1ae,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:1ae

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:1af,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:1af

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:24c,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:1ac

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:24d,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:24d

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:24e,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video*(uint64_t*)pFrame->opaque:24e

以下为播放rm文件的情况:

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:1831b,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:1831b

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:18704,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:18704

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:18aed,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:18aed

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:18ed6,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:18ed6

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:192bf,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:192bf

-----------------------------------------------------------------------------

avcodec_decode_videopacket->pts:196a8,packet->dts:0

avcodec_decode_videopFrame->pkt_pts:0,pFrame->pkt_dts:80000000,pFrame->pts:0

avcodec_decode_video *(uint64_t*)pFrame->opaque:196a8

可以看出有的pts是+1累加,有的是加了很多,但都是按顺序累加的。当传人decoder前的packet有pts时,则decoder后获取的frame将会赋值packet的pts;当传人的packet只是一帧的部分数据或是B帧,由于decoder出来的frame要按正常的pts顺序输出,有可能decoder不会获取到frame,或decoder内部会缓存也不会输出frame,即frame的pts会为空。Frame pts(即opaque)为空的话则会看frame->dts,dts都没有的话才认为frame->pts为0.

对于:

pts *=av_q2d(is->video_st->time_base);////即pts*帧率

// Did weget avideo frame?

if(frameFinished){

pts =synchronize_video(is, pFrame, pts);

///// synchronize_video考虑了3中情况:

1.    pts拿到的话就用该pts

2.    pts没有拿到的话就用前一帧的pts时间

3.    如果该帧要重复显示,则将显示的数量*帧率,再加到前面的pts中。

if(queue_picture(is,pFrame, pts) < 0){/////传人decoder后的帧队列中,以便后续去获取show。

static double synchronize_video(VideoState *is,AVFrame*src_frame, double pts) {

doubleframe_delay;

if(pts != 0) {

is->video_clock= pts;

} else {

pts=is->video_clock;

}

/////很关键:前面传进来的pts已经是时间戳了,是当前frame开始播放的时间戳,

/////下面frame_delay是该帧显示完将要花费的时间,(pts+frame_delay)也即是/////预测的下一帧将要播放的时间戳。

frame_delay=av_q2d(is->video_st->codec->time_base);

//////重复多帧的话要累加上

frame_delay+=src_frame->repeat_pict * (frame_delay * 0.5);

is->video_clock +=frame_delay;

return pts;/////此时返回的值即为下一帧将要开始显示的时间戳。

}

///////开定时器去显示帧队列中的已经decode过的数据,按前面的分析我们已经知道帧队列中的数据已经是按pts顺序插入到队列中的。Timer的作用就是有帧率不一致及重复帧的情况造成时间戳不是线性的,有快有慢,从而tutorial5才有timer的方式来播放:追赶

以下是一个网友很直观浅显的例子解释:

ccq(183892517) 17:05:21 if(packet->dts==AV_NOPTS_VALUE 是不是就是没有获取到dts的情况?

David Cen(3727567) 17:06:44 就是有一把尺子一只蚂蚁跟着一个标杆走  David Cen(3727567) 17:06:58 标杆是匀速的蚂蚁或快或慢 DavidCen(3727567) 17:07:18 慢了你就抽它 让他跑起来快了就拽它  David Cen(3727567) 17:07:38这样音(标杆)视频(蚂蚁)就能同步了 DavidCen(3727567) 17:08:00 这里最大的问题就是音频是匀速的视频是非线性的

另外:此时vp–>pts获取到的pts已经转化为时间戳了,这个时间戳为就是当前帧显示结束的时间戳,也即是下一帧将显示的预测时间戳。

static void video_refresh_timer(void *userdata) {

VideoState *is = (VideoState*)userdata;

VideoPicture *vp;

double actual_delay, delay,sync_threshold,ref_clock, diff;

if(is->video_st) {

if(is->pictq_size == 0) {

schedule_refresh(is, 1);

} else{

vp=&is->pictq[is->pictq_rindex];

delay = vp->pts -is->frame_last_pts;  ////这是当前要显示的frame和下一副                                                        //////将要显示的frame的间隔时间

if(delay <= 0 || delay>= 1.0) {

delay =is->frame_last_delay;

}

is->frame_last_delay =delay;

is->frame_last_pts =vp->pts;

ref_clock = get_audio_clock(is);/////获取到声音当前播放的时间戳。

diff = vp->pts -ref_clock;//////vp->pts实际上是预测的下一帧将要播放的开始时间,

//////////也就是说在diff这段时间中声音是匀速发生的,但是在delay这段时间frame的显示可能就会有快//////////慢的区别。

sync_threshold = (delay >AV_SYNC_THRESHOLD) ? delay: AV_SYNC_THRESHOLD;

if(fabs(diff) < AV_NOSYNC_THRESHOLD) {

if(diff <=-sync_threshold) {

delay =0;//////下一帧画面显示的时间和当前的声音很近的话加快显示下一帧(即后面video_display显示完当前帧后开启定时器很快去显示下一帧)

} else if(diff >=sync_threshold) {

delay = 2 *delay;//////下一帧开始显示的时间和当前声音的时间隔的比较长则延缓,即两帧画面间话的显示的时间长度大于两帧画面间的声音播放的时间,则我们将两帧画显示的时候加倍拖长点,比如帧1和帧2的时间显示间隔为40ms,但帧1和帧2的声音播放时间为55ms,怎么办呢?我们不可能去打乱声音的质量的,则我们采用的方法是:将两帧画面的播放间隔加大,本来是过30ms就要开始播下一帧的,我们改成60ms后才播下一帧。

}

}/////

////当然如果diff大于AV_NOSYNC_THRESHOLD,即快进的模式了,画面跳动太大,不存在音视频同步的问题了。

is->frame_timer += delay;

actual_delay =is->frame_timer - (av_gettime() /1000000.0);

if(actual_delay < 0.010){

actual_delay = 0.010;

}

schedule_refresh(is,(int)(actual_delay * 1000 +0.5));////开定时器去显示下一帧

video_display(is);////立马显示当前帧

if(++is->pictq_rindex ==VIDEO_PICTURE_QUEUE_SIZE){

is->pictq_rindex = 0;

}

SDL_LockMutex(is->pictq_mutex);

is->pictq_size--;

SDL_CondSignal(is->pictq_cond);

SDL_UnlockMutex(is->pictq_mutex);

}

} else {

schedule_refresh(is, 100);

}

ffmpeg 编解码详细过程的更多相关文章

  1. FFmpeg编解码处理1-转码全流程简介

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584901.html FFmpeg编解码处理系列笔记: [0]. FFmpeg时间戳详 ...

  2. ffmpeg编解码视频导致噪声增大的一种解决方法

    一.前言 ffmpeg在视音频编解码领域算是一个比较成熟的解决方案了.公司的一款视频编辑软件正是基于ffmpeg做了二次封装,并在此基础上进行音视频的编解码处理.然而,在观察编码后的视频质量时,发现图 ...

  3. FFmpeg编解码处理4-音频编码

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584948.html FFmpeg编解码处理系列笔记: [0]. FFmpeg时间戳详 ...

  4. FFmpeg编解码处理3-视频编码

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584937.html FFmpeg编解码处理系列笔记: [0]. FFmpeg时间戳详 ...

  5. FFmpeg编解码处理2-编解码API详解

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584925.html FFmpeg编解码处理系列笔记: [0]. FFmpeg时间戳详 ...

  6. 最近在研究FFmpeg编解码

    好几年没上CNBLOGS了, 最近在研究FFmpeg编解码,一个人研究感到很寂寞,所以想通过博客来和大家分享和交流,呵呵. 最近研究的主题是: ANDROID手机同屏技术: 需要用到ANDROID截屏 ...

  7. ffmpeg编解码音频AAC

    本次项目的需求:手机端和PC端共享同一个音视频网络源. 所以编解码需要满足手机上编码和解码原来PC端的音视频流. 这里先封装安卓手机端音频的编解码. 编译工作依然是在linux下 ubuntu 12. ...

  8. FFmpeg 学习(五):FFmpeg 编解码 API 分析

    在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...

  9. UWP笔记-使用FFmpeg编解码

    在开发UWP媒体应用的时候,使用的MediaElement可以支持主流的格式,不过还是有些格式本地编解码器是不支持的,如.flv..rmvb等,这里讲到的是第三方开源库FFmpeg,可以直接播放更多的 ...

随机推荐

  1. Aixs2 使用总结,持续更新中 ...

    参考博客:http://zhangjunhd.blog.51cto.com/113473/23692     消息交换模式. 目前Axis2支持三种模式:In-Only.Robust-In和In-Ou ...

  2. 17monipdb根据IP获得区域

    https://www.ipip.net/download.html https://github.com/17mon/csharp IpAndPositionHelper public class ...

  3. AngularJS 使用 UI Router 实现表单向导

    Today we will be using AngularJS and the great UI Router and the Angular ngAnimate module to create ...

  4. CentOS6.4之图解SSH无验证双向登陆配置

    配置SSH无登陆验证,在很多场景下是非常方便的,尤其是在管理大型集群服务时,避免了繁琐的密码验证,在安全级别越高的服务器上,通常密码的设置更复杂,配置SSH,不仅可以用密钥保证节点间通信的安全性,同时 ...

  5. (转) 共享个很棒的vim配置

    发现了一个很棒的vim配置方法,现在共享给大家. https://github.com/kepbod/ivim   ivim - The Vim Distribution of Xiao-Ou Zha ...

  6. Python 2.7.9 Demo - 003.01.只允许相同缩进

    Right #!/usr/bin/python if True: print ("True"); print('Again'); else: print ("False& ...

  7. 【linux】Shell脚本启动程序

    Linux 下后台脚本启动Jar程序 Nohup后台输出运行日志 cd /home/zeze/jar/WeiboPost java -jar WeiboAutoPost.jar ../configur ...

  8. 续写上一篇的数组or指针操作

    C语言,同样使用if else while 这样的语法,但不同的人,就是有不同的实现方式,甚至是技巧. eg: #include <stdio.h> #include<string. ...

  9. bnuoj16491

    http://www.bnuoj.com/bnuoj/problem_show.php?pid=16491 题意:有t组测试数据,每组测试数据第一行为n,m,接下来有n种跑法,m为最大的能力,每一种跑 ...

  10. php ldap添加与修改

    /** * ldap 备份 * @param int $cardid * @param string $username * @param string $password 未加密密码 * @retu ...