写这边博客,一方面是因为自己在做项目的时候不太做笔记,怕以后自己忘记了。另一方面,是让正在寻求资料的同行少走一点弯路吧。不能说我这个方案怎么的好,至少是有一点参考价值的。这边博客需要一定基础才能看明白的,当然对V4L2,Swscale,x264,live555不太了解的人,我这里会给出我当时看的资料链接,但愿链接一直有效。也感谢了那些写博客的作者。

下面贴出一些资料的链接吧,认真看完肯定收获不少。

一篇关于V4L2(Video For Linux Two)http://www.cnblogs.com/lixiaoming90/archive/2012/08/25/2657019.html写的很不错的文章,认真看完了,我觉得V4L2视屏捕捉肯定不是问题。

Swscale是ffmpeg库的一部分,主要是做图像格式的转换和拉伸,缩放。这边文章介绍了Swscale的使用,http://blog.csdn.net/leixiaohua1020/article/details/14215391 。

x264是做H264编码用的,要注意的是x264的输入图像格式是:I410也就是420P。这样可以用Swscale对原始图像进行转格式。http://www.cnblogs.com/fojian/archive/2012/09/01/2666627.html 。

live555 是一个流媒体框架,主要是做RTSP协议的。写的很不错(大家都这么说的),但是读live555源代码还是有一定难度的。这里我就不过多的介绍live555的实现机制了。但是资料还是给出了,感谢作者。http://blog.csdn.net/niu_gao/article/category/1066093 。

下面我开始讲解整个服务器的实现过程吧,

  1. /*
  2. * V4L2.cpp
  3. *
  4. * Created on: 2013年12月17日
  5. * Author: ny
  6. */
  7.  
  8. #include "V4L2.h"
  9.  
  10. V4L2::V4L2()
  11. {
  12. fd = -;
  13. buffers = NULL;
  14. width = ;
  15. height = ;
  16. CLEAR(fmt); //设置帧格式
  17. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  18. fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //V4L2_PIX_FMT_YUYV;
  19. fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
  20. }
  21.  
  22. V4L2::~V4L2()
  23. {
  24. close(fd);
  25. }
  26.  
  27. int V4L2::getWidth()
  28. {
  29. return width;
  30. }
  31.  
  32. int V4L2::getHeight()
  33. {
  34. return height;
  35. }
  36.  
  37. bool V4L2::setSize(int width, int height)
  38. {
  39. fmt.fmt.pix.width = width;
  40. fmt.fmt.pix.height = height;
  41. if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -)
  42. {
  43. printf("Can not VIDIOC_S_FMT\n");
  44. return false;
  45. }
  46. getSizeInfo();
  47. return true;
  48. }
  49.  
  50. void V4L2::getSizeInfo()
  51. {
  52. if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -)
  53. {
  54. printf("Can not VIDIOC_G_FMT\n");
  55. return;
  56. }
  57. this->width = fmt.fmt.pix.width;
  58. this->height = fmt.fmt.pix.height;
  59. }
  60.  
  61. bool V4L2::initDev(const char * devName, int width, int height)
  62. {
  63. v4l2_capability cap;
  64.  
  65. fd = open(devName, O_RDWR, ); //打开设备
  66. if (fd == -)
  67. {
  68. printf("Can not open %s\n", devName);
  69. return false;
  70. }
  71. if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -) //查询设备的功能
  72. {
  73. printf("Can not get Capability\n");
  74. return false;
  75. }
  76. if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
  77. {
  78. printf("Can not capture video\n");
  79. return false;
  80. }
  81. if (!(cap.capabilities & V4L2_CAP_STREAMING))
  82. {
  83. printf("does not support streaming\n");
  84. }
  85. if (!setSize(width, height))
  86. return false;
  87. printf("fmt.fmt.pix.bytesperline:%d\n", fmt.fmt.pix.bytesperline);
  88. printf("format:%c%c%c%c\n", (fmt.fmt.pix.pixelformat & 0xff),
  89. ((fmt.fmt.pix.pixelformat >> ) & 0xff),
  90. ((fmt.fmt.pix.pixelformat >> ) & 0xff),
  91. ((fmt.fmt.pix.pixelformat >> ) & 0xff));
  92.  
  93. return initMmap();
  94. }
  95. bool V4L2::initMmap()
  96. {
  97. struct v4l2_requestbuffers req;
  98. unsigned int n_buffers;
  99. CLEAR(req);
  100.  
  101. req.count = ; //先要想内核申请buffer缓冲,一般选择4个缓冲,最多是5个
  102. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  103. req.memory = V4L2_MEMORY_MMAP;
  104.  
  105. if (- == ioctl(fd, VIDIOC_REQBUFS, &req)) //向内核里面设置buffer
  106. { //分配内存
  107. if (EINVAL == errno)
  108. {
  109. printf("%s does not support memory mapping\n", "ss");
  110. exit(EXIT_FAILURE);
  111. } else
  112. printf("VIDIOC_REQBUFS\n");
  113. }
  114.  
  115. buffers = (buffer *) calloc(req.count, sizeof(buffer)); //分配缓存
  116. if (!buffers)
  117. {
  118. printf("Out of memory\n");
  119. exit(EXIT_FAILURE);
  120. }
  121.  
  122. for (n_buffers = ; n_buffers < req.count; n_buffers++) //将buffer添加到QUERTBUF的队列里面去
  123. {
  124. struct v4l2_buffer buf;
  125. CLEAR(buf);
  126.  
  127. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  128. buf.memory = V4L2_MEMORY_MMAP;
  129. buf.index = n_buffers;
  130.  
  131. if (- == ioctl(fd, VIDIOC_QUERYBUF, &buf))
  132. printf("VIDIOC_QUERYBUF");
  133.  
  134. buffers[n_buffers].length = buf.length; //设置映射方式为mmap
  135. printf("buf.length %d\n", buffers[n_buffers].length);
  136. buffers[n_buffers].start = mmap(NULL, buf.length,
  137. PROT_READ | PROT_WRITE,
  138. MAP_SHARED, fd, buf.m.offset);
  139.  
  140. if (MAP_FAILED == buffers[n_buffers].start)
  141. printf("fail mmap\n");
  142. }
  143. return ;
  144. }
  145. bool V4L2::startStream()
  146. {
  147. unsigned int n_buffers;
  148. enum v4l2_buf_type type;
  149. /*将申请到的帧缓冲全部入队列,以便存放采集到的数据*/
  150. for (n_buffers = ; n_buffers < ; n_buffers++)
  151. {
  152. v4l2_buffer buf;
  153. CLEAR(buf);
  154.  
  155. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  156. buf.memory = V4L2_MEMORY_MMAP;
  157. buf.index = n_buffers;
  158.  
  159. if (- == ioctl(fd, VIDIOC_QBUF, &buf)) //放入缓存
  160. {
  161. printf("fail VIDIOC_QBUF");
  162. return false;
  163. }
  164. }
  165.  
  166. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  167.  
  168. if (- == ioctl(fd, VIDIOC_STREAMON, &type)) //打开视频流
  169. {
  170. printf("fail VIDIOC_STREAMON\n");
  171. return false;
  172. } else
  173. printf("StreamOn success!\n");
  174. return true;
  175. }
  176. //摄像头数据 主要是YUYV格式数据,所以需要进行对格式进行转化,转化后的数据保存在AVPicture里面,
  177. //格式是由FMT制定的,输出图像尺寸也是widht_des,height_des决定的
  178. bool V4L2::readFrame(AVPicture & pPictureDes, AVPixelFormat FMT, int widht_des,
  179. int height_des)
  180. {
  181. v4l2_buffer buf;
  182. AVPicture pPictureSrc;
  183. SwsContext * pSwsCtx;
  184. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  185. buf.memory = V4L2_MEMORY_MMAP;
  186. if (- == ioctl(fd, VIDIOC_DQBUF, &buf)) //读取
  187. {
  188. printf("fail VIDIOC_DQBUF\n");
  189. return false;
  190. }
  191. pPictureSrc.data[] = (unsigned char *) buffers[buf.index].start;
  192. pPictureSrc.data[] = pPictureSrc.data[] = pPictureSrc.data[] = NULL;
  193. pPictureSrc.linesize[] = fmt.fmt.pix.bytesperline;
  194. int i = ;
  195. for (i = ; i < ; i++)
  196. {
  197. pPictureSrc.linesize[i] = ;
  198. }
  199. pSwsCtx = sws_getContext(width, height, PIX_FMT_YUYV422, widht_des,
  200. height_des, FMT,
  201. SWS_BICUBIC, , , );
  202. int rs = sws_scale(pSwsCtx, pPictureSrc.data, pPictureSrc.linesize, ,
  203. height, pPictureDes.data, pPictureDes.linesize);
  204. if (rs == -)
  205. {
  206. printf("Can open to change to des image");
  207. return false;
  208. }
  209. sws_freeContext(pSwsCtx);
  210. if (- == ioctl(fd, VIDIOC_QBUF, &buf)) //放回缓存
  211. {
  212. printf("fail VIDIOC_QBUF\n");
  213. return false;
  214. }
  215. return true;
  216. }
  217.  
  218. bool V4L2::stopStream()
  219. {
  220. enum v4l2_buf_type type;
  221. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  222. if (- == ioctl(fd, VIDIOC_STREAMOFF, &type))
  223. {
  224. perror("Fail to ioctl 'VIDIOC_STREAMOFF'");
  225. //exit(EXIT_FAILURE);
  226. return false;
  227. }
  228. return true;
  229. }

上面的代码 主要是实现了摄像头的数据捕获,值得注意是函数bool V4L2::readFrame(AVPicture & pPictureDes, AVPixelFormat FMT, int widht_des,
int
height_des);这个是第一个参数 AVPicture是ffmpeg里面的一个结构体 主要是保存图像的。不过这个参数的初始化在
函数外面,这个大家可以看看那篇swscale的文章就知道了,还有一点就是在函数内部分配的栈数据会在函数结束后销毁的。

  1. /*
  2. * H264Encode.cpp
  3. *
  4. * Created on: 2014年1月4日
  5. * Author: ny
  6. */
  7.  
  8. #include <H264Encode.h>
  9.  
  10. H264Encode::H264Encode()
  11. {
  12. i_pts = ;
  13. x264EnCoder = NULL;
  14. pPicOut = NULL;
  15. nnal=;
  16. nals=NULL;
  17. }
  18.  
  19. H264Encode::~H264Encode()
  20. {
  21. }
  22.  
  23. void H264Encode::x264_init(AVPicture picture, int width, int height)
  24. {
  25. x264_param_default_presetm, "veryfast", "zerolatency");
  26.  
  27. param.i_width = width;
  28. param.i_height = height;
  29. param.i_fps_num = ;
  30. param.i_fps_den = ;
  31.  
  32. param.i_keyint_max = ;
  33. param.b_intra_refresh = ;
  34.  
  35. param.b_annexb = ;
  36.  
  37. x264_param_apply_profilem, "baseline");
  38. x264EnCoder = x264_encoder_openm);
  39.  
  40. x264_picture_alloc(&xPic, X264_CSP_I420, width, height);
  41.  
  42. xPic.img.plane[] = picture.data[];
  43. xPic.img.plane[] = picture.data[];
  44. xPic.img.plane[] = picture.data[];
  45. pPicOut = new x264_picture_t;
  46. }
  47.  
  48. void H264Encode::x264_encode()
  49. {
  50. xPic.i_pts = i_pts++;
  51. x264_encoder_encode(x264EnCoder, &nals, &nnal, &xPic, pPicOut);
  52. }

这部分代码 没什么好讲的 就是这个流程,值得注意的是一帧图像编码后可能生成几个nal,nals是一个nal的指针,nnal表示这帧数据有几个nal。而nal里面保存了数据信息。

  1. /*
  2. * H264OnDemandServerMediaSubsession.cpp
  3. *
  4. * Created on: 2014年1月4日
  5. * Author: ny
  6. */
  7.  
  8. #include <H264OnDemandServerMediaSubsession.h>
  9. #include <V4L2FramedSource.h>
  10. #include <live/H264VideoStreamFramer.hh>
  11. #include <live/H264VideoRTPSink.hh>
  12.  
  13. H264OnDemandServerMediaSubsession::H264OnDemandServerMediaSubsession(
  14. UsageEnvironment& env, FramedSource * source) :
  15. OnDemandServerMediaSubsession(env, true)
  16. {
  17. mp_source = source;
  18. mp_sdp_line = NULL;
  19. mp_dummy_rtpsink = NULL;
  20. m_done = ;
  21. }
  22.  
  23. H264OnDemandServerMediaSubsession::~H264OnDemandServerMediaSubsession()
  24. {
  25. }
  26.  
  27. void H264OnDemandServerMediaSubsession::chkForAuxSDPLine1()
  28. {
  29. if (mp_dummy_rtpsink->auxSDPLine())
  30. m_done = 0xff;
  31. else
  32. {
  33. int delay = * ; // 100ms
  34. nextTask() = envir().taskScheduler().scheduleDelayedTask(delay, chkForAuxSDPLine, this);
  35. }
  36. }
  37.  
  38. const char * H264OnDemandServerMediaSubsession::getAuxSDPLine(RTPSink *sink,
  39. FramedSource *source)
  40. {
  41. if (mp_sdp_line)
  42. return mp_sdp_line;
  43.  
  44. mp_dummy_rtpsink = sink;
  45. mp_dummy_rtpsink->startPlaying(*source, , );
  46. //mp_dummy_rtpsink->startPlaying(*source, afterPlayingDummy, this);
  47. chkForAuxSDPLine(this);
  48. m_done = ;
  49. envir().taskScheduler().doEventLoop(&m_done);
  50. mp_sdp_line = strdup(mp_dummy_rtpsink->auxSDPLine());
  51. mp_dummy_rtpsink->stopPlaying();
  52.  
  53. return mp_sdp_line;
  54. }
  55.  
  56. RTPSink * H264OnDemandServerMediaSubsession::createNewRTPSink(Groupsock *rtpsock, unsigned char type, FramedSource *source)
  57. {
  58. return H264VideoRTPSink::createNew(envir(), rtpsock, type);
  59. }
  60.  
  61. FramedSource * H264OnDemandServerMediaSubsession::createNewStreamSource( unsigned sid, unsigned &bitrate)
  62. {
  63. bitrate = ;
  64. return H264VideoStreamFramer::createNew(envir(), mp_source);
  65. }
  66. char const* H264OnDemandServerMediaSubsession::sdpLines()
  67. {
  68. return fSDPLines = (char *)
  69. "m=video 0 RTP/AVP 96\r\n"
  70. "c=IN IP4 0.0.0.0\r\n"
  71. "b=AS:96\r\n"
  72. "a=rtpmap:96 H264/90000\r\n"
  73. "a=fmtp:96 packetization-mode=1;profile-level-id=000000;sprop-parameter-sets=H264\r\n"
  74. "a=control:track1\r\n";
  75. }

H264OnDemandServerMediaSubsession类主要是继承了H264OnDemandServerMediaSubsession类。主要是为了设置SDP 描述。

  1. sdpLines()这个函数 需要根据自己的实际情况进行改写的。关于里面的含义,我这里就不多解释了,熟悉一下RTSP协议就可以完全知道含义了。
  1. /*
  2. * V4L2FramedSource.cpp
  3. *
  4. * Created on: 2014年1月4日
  5. * Author: ny
  6. */
  7.  
  8. #include <V4L2FramedSource.h>
  9.  
  10. int V4L2FramedSource::nalIndex = ;
  11.  
  12. V4L2FramedSource::V4L2FramedSource(UsageEnvironment & env) :
  13. FramedSource(env)
  14. {
  15. v4l2 = new V4L2();
  16. pEncode = new H264Encode();
  17. mp_token = NULL;
  18. printf("creater\n");
  19. v4l2->initDev("/dev/video0", , );
  20. avpicture_alloc(&Picture, PIX_FMT_YUV420P, v4l2->getWidth(), v4l2->getHeight());
  21. v4l2->startStream();
  22.  
  23. pEncode->x264_init(Picture, , );
  24. }
  25.  
  26. V4L2FramedSource::~V4L2FramedSource()
  27. {
  28. }
  29.  
  30. unsigned V4L2FramedSource::maxFrameSize() const
  31. {
  32. return * ;
  33. }
  34.  
  35. void V4L2FramedSource::doGetNextFrame()
  36. {
  37. /*double delay = 1000.0 / 25;
  38. int to_delay = delay * 1000; // us
  39. mp_token = envir().taskScheduler().scheduleDelayedTask(to_delay,
  40. getNextFrame, this);*/
  41. if (V4L2FramedSource::nalIndex == pEncode->nnal)
  42. {
  43. v4l2->readFrame(Picture, PIX_FMT_YUV420P, v4l2->getWidth(), v4l2->getHeight());
  44. pEncode->x264_encode();
  45. V4L2FramedSource::nalIndex = ;
  46. gettimeofday(&fPresentationTime, NULL);
  47. }
  48. memmove(fTo,
    pEncode->nals[V4L2FramedSource::nalIndex].p_payload,
  49. pEncode->nals[V4L2FramedSource::nalIndex].i_payload);
  50. fFrameSize = pEncode->nals[V4L2FramedSource::nalIndex].i_payload;
  51. V4L2FramedSource::nalIndex++;
  52. afterGetting(this);
  53. }
  54.  
  55. void V4L2FramedSource::getNextFrame1()
  56. {
  57.  
  58. }
  59.  
  60. /*
  61. V4L2FramedSource类继承了FramedSource类。V4L2FramedSource是我们自定义的类,主要实现了我们的视频数据如何进入到live555里面去。首先在构造函数里面,我们对v4l2进行了初始化以及x264编码初始化。这个类最重要的就是<SPAN style="BACKGROUND-COLOR: rgb(240,240,240)">doGetNextFrame函数,live555就是通过这个函数将我们的一个nal数据加载到live555里面,然后消息循环发送出去的。我们先是将v4l2捕捉的视频数据进行H264压缩编码,这里面值得注意的是一帧图像可能压缩成几个nal的,所以我这里面在确保一帧数据完全发送完了才向v4l2要数据。然后就是数据的般移了,其中数据存在fTo里面。然后就是消息了。
  62. */
  63.  
  64. /*
  65. * Application.cpp
  66. *
  67. * Created on: 2014年1月4日
  68. * Author: ny
  69. */
  70. #include <stdio.h>
  71. #include <stdlib.h>
  72. #include <unistd.h>
  73.  
  74. #include "H264OnDemandServerMediaSubsession.h"
  75. #include "V4L2FramedSource.h"
  76.  
  77. #include <live/liveMedia.hh>
  78. #include <live/BasicUsageEnvironment.hh>
  79. #include <live/UsageEnvironment.hh>
  80.  
  81. UsageEnvironment* env;
  82. static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
  83. char const* streamName, char const* inputFileName = "Live"); // fwd
  84. int main()
  85. {
  86. // 设置使用环境。Begin by setting up our usage environment:
  87. TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  88. env = BasicUsageEnvironment::createNew(*scheduler);
  89. UserAuthenticationDatabase* authDB = NULL;
  90.  
  91. RTSPServer* rtspServer = RTSPServer::createNew(*env, , authDB);
  92. if (rtspServer == NULL)
  93. {
  94. *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
  95. exit();
  96. }
  97.  
  98. char const* descriptionString =
  99. "Session streamed by \"testOnDemandRTSPServer\"";
  100. char const* streamName = "live";
  101. ServerMediaSession* sms = ServerMediaSession::createNew(*env, streamName,
  102. streamName, descriptionString);
  103. sms->addSubsession(new H264OnDemandServerMediaSubsession(*env,
  104. new V4L2FramedSource(*env)));
  105. rtspServer->addServerMediaSession(sms);
  106. announceStream(rtspServer, sms, streamName);
  107. env->taskScheduler().doEventLoop(); // does not return
  108.  
  109. return ;
  110. }
  111. static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
  112.            char const* streamName, char const* inputFileName)
  113. {
  114. char* url = rtspServer->rtspURL(sms);
  115. UsageEnvironment& env = rtspServer->envir();
  116. env << "\n\"" << streamName << "\" stream, from the file \""
  117. << inputFileName << "\"\n";
  118. env << "Play this stream using the URL \"" << url << "\"\n";
  119. delete[] url;
  120. }
  121. /*
  122. 这是我们程序的主程序,主要是参考了live555的testOnDemandRTSPServer.cpp。主要是区别是我们自己指定了rtsp链接了,以及我们自己实现的framesource。这样本地的视频采集发送服务器就完成了,可以用开源的VLC播放进行播放了,live555支持1对多的,已经测试过可以在ipad,iphone,android同步播放,延迟在1s以内。不过要VLC设置缓存时间在200ms就可以了。
  123. */

【转】V4L2+swscale+X264+live555实现流媒体服务端的更多相关文章

  1. 基于v4l2 ffmpeg x264的视频远程监控(附上编译好的库文件)

    说明:主要是基于ghostyu网友整理的< arm mini2440 基于v4l2 ffmpeg x264的视频远程监控>.自己做了一遍,遇到不少问题,就整理记录下来. 1.平台 硬件:a ...

  2. Live555 分析(二):服务端

    live555支持单播和组播,我们先分析单播的流媒体服务端,后面分析组播的流媒体服务端. 一.单播的流媒体服务端: // Create the RTSP server: RTSPServer* rts ...

  3. 庖丁解牛-----Live555源码彻底解密(RTP打包)

    本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容; 庖丁解牛-----Live555源码彻底解密(根据M ...

  4. live555学习之基本类介绍及计划任务深度探讨

    liveMedia项目的源代码包括四个基本的库,各种测试代码以及Media Server.四个基本的库分别是: UsageEnvironment&TaskScheduler, groupsoc ...

  5. Live555 实战之框架简单介绍

    作者:咕唧咕唧liukun321 来自:http://blog.csdn.net/liukun321 上一篇文章简要介绍了怎样以共享库的方式交叉编译Live555,今天再来介绍live源代码框架. 先 ...

  6. Live555 分析(一):类介绍

    从程序的结构来看,live项目包括了四个基本库.程序入口类(在mediaServer中)和一些测试代码(在testProgs中). 四个基本静态库是UsageEnvironment.BasicUsag ...

  7. live555 基本框架

    (转) 从程序的结构来看,live项目包括了四个基本库.程序入口类(在mediaServer中)和一些测试代码(在testProgs中).四个基本库是UsageEnvironment,BasicUsa ...

  8. ffmpeg用法小结,教你抓各大网站视频

    最近受邀朋友帮忙需要抓取一段某酷电影,偶然间发现ffmpeg程序.说到此可能会有人提到you-get和youtube-dl,期间也接触了该两款程序,但是由于版权原因,该软件仅仅可以抓取前几分钟预览版, ...

  9. 视频直播技术-视频-编码-传输-秒开等<转>

    转载地址:http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=2653547042&idx=1&sn=26d8728548 ...

随机推荐

  1. boost格式化输出xml

    我的boost为1.56而不是1.55 boost在xml的例子给出了一段写xml文件的代码,我简化如下: void debug_settings::save(const std::string &a ...

  2. jquery ajax 局部table 刷新技术

    点击查询:

  3. C语言漫谈(二) 图像显示 Windows和Linux

    关于图像显示有很多库可以用,Windows下有GDI,GDI+,D3D等,Linux下有X Window和Wayland,此外还有OpenGL ,SDL等图形库以及各种GUI库. 了解最原始的方式,对 ...

  4. python高级编程(第12章:优化学习)2

    #优化策略 #3个原则 """ 1a:寻找其他原因:确定第三方服务器或资源不是问题所在 2a:度量硬件:确定资源足够用 3a:编写速度测试:创建带有速度要求的场景 &qu ...

  5. Hibernaate事务管理

    Hibernate使用session时需要继承HibernateDaoSupport对象 HibernateDaoSupport对象中包含默认的getSession()方法,但不可以通过该方法直接启动 ...

  6. SQL参数化

    本文来自:caodonglin 一.SQL参数化为什么能防注入? 因为执行计划被重用了,所以可以防SQL注入. 下面有两段SQL     正常SQL: 1 select COUNT(1) from C ...

  7. EffectiveC#12,13,14--成员初始化

    1.在一个类里声明变量的同时,直接创建实例值.包括静态的和实例的变量 例:object m_o = new object(); 如下情况时不建议这么做:第一种 值类型. int i=new int() ...

  8. 自定义上传按钮 <input type="file" name = "file"/> (将file隐藏在button下)

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  9. HTML5-常见的事件- DOMContentLoaded事件

    一般我们监听文档是否加载完成是使用 window的load事件,该事件会在页面中的一切加载完毕时触发,但这个过程可能会因为要加载的外部资源过多而等待时间过长. DOMContentLoaded事件:则 ...

  10. Android——ExpandableListView事件拦截

    1.满足条件 如果使用ExpandableListView,需要子item响应一个事件,比如重新启动一个新的activity,需要满足下面的条件: (1).修改Adapter返回值 覆写BaseExp ...