一个偶然的机缘,好像要做直播相关的项目

为了筹备,前期做一些只是储备,于是开始学习ffmpeg

这是学习的第一课

做一个简单的播放器,播放视频画面帧

思路是,将视频文件解码,得到帧,然后使用定时器,1秒显示24帧

1.创建win32工程,添加菜单项 “打开”

为了避免闪烁,MyRegisterClass中设置hbrBackground为null

2.在main函数中初始化ffmpeg库:av_register_all();

3.响应菜单打开

 void LoadVideoPlay(HWND hWnd)
{
if (gbLoadVideo)
{
return;
} TCHAR szPath[] = { };
DWORD dwPath = ;
OPENFILENAME ofn = { };
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.hInstance = hInst;
ofn.lpstrFile = szPath;
ofn.nMaxFile = dwPath;
ofn.lpstrFilter = _T("Video(*.mp4)\0*.MP4*;*.avi*\0");
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrInitialDir = _T("F:\\"); if (!GetOpenFileName(&ofn))
{
DWORD dwErr = CommDlgExtendedError();
OutputDebugString(_T("GetOpenFileName\n"));
return;
} std::wstring strVideo = szPath;
std::thread loadVideoThread([hWnd, strVideo]() {
gbLoadVideo = TRUE;
std::string sVideo = UnicodeToUTF_8(strVideo.c_str(), strVideo.size());
OpenVideoByFFmpeg(hWnd, sVideo.c_str());
gbLoadVideo = FALSE;
}); loadVideoThread.detach();
}

使用c++11的线程来加载视频文件并进行解码工作。

4.在加载完视频之后,设置窗口为不可缩放

创建缓存DC等显示环境

设置播放帧画面的定时器

5.将解码的帧画面转化为 RGB 32位格式,并存储至队列中等待播放

6.播放帧画面

在WM_PAINT消息中进行绘画

 void PlayFrame(HWND hWnd, UCHAR* buffer, UINT uWidth, UINT uHeight)
{
do
{
if (GetFramesSize() > ( * )) // 缓冲5秒的帧数
{
if (WaitForSingleObject(ghExitEvent, ) == WAIT_OBJECT_0)
{
return;
}
}
else
{
HDC hDC = GetDC(hWnd);
HBITMAP hFrame = CreateBitmapFromPixels(hDC, uWidth, uHeight, , buffer);
if (hFrame)
{
PushFrame(hFrame);
}
ReleaseDC(hWnd, hDC);
break;
} } while (true);
}

因为解码拿到的是像素数据,需要将像素数据转化为Win32兼容位图

 HBITMAP CreateBitmapFromPixels(HDC hDC, UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits)
{
if (uBitsPerPixel <= ) // NOT IMPLEMENTED YET
return NULL; HBITMAP hBitmap = ;
if (!uWidth || !uHeight || !uBitsPerPixel)
return hBitmap;
LONG lBmpSize = uWidth * uHeight * (uBitsPerPixel / );
BITMAPINFO bmpInfo = { };
bmpInfo.bmiHeader.biBitCount = uBitsPerPixel;
bmpInfo.bmiHeader.biHeight = -(static_cast<LONG>(uHeight)); // 因为拿到的帧图像是颠倒的
bmpInfo.bmiHeader.biWidth = uWidth;
bmpInfo.bmiHeader.biPlanes = ;
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Pointer to access the pixels of bitmap
UINT * pPixels = ;
hBitmap = CreateDIBSection(hDC, (BITMAPINFO *)&
bmpInfo, DIB_RGB_COLORS, (void **)&
pPixels, NULL, ); if (!hBitmap)
return hBitmap; // return if invalid bitmaps memcpy(pPixels, pBits, lBmpSize); return hBitmap;
}

7.播放完毕,回复窗口设定,关闭定时器

代码流程一目了然,用来学习ffmpeg入门

最后贴上总的代码

 // main.cpp : 定义应用程序的入口点。
// #include "stdafx.h"
#include "testPlayVideo.h" #include <windows.h>
#include <commdlg.h>
#include <deque>
#include <string>
#include <mutex>
#include <thread> extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libavutil/imgutils.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
} #pragma comment(lib, "Comdlg32.lib") #define MAX_LOADSTRING 100
#define TIMER_FRAME 101
#define CHECK_TRUE(v) {if(!v) goto cleanup;}
#define CHECK_ZERO(v) {if(v<0) goto cleanup;} // 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 std::mutex gFrameLock; // 图像位图帧的访问锁
std::deque<HBITMAP> gFrames; // 所有解码的视频图像位图帧
HDC ghFrameDC; // 视频帧图像兼容DC
HBITMAP ghFrameBmp; // 兼容DC的内存位图
HBRUSH ghFrameBrush; // 兼容DC的背景画刷
HANDLE ghExitEvent; // 程序退出通知事件
UINT uFrameTimer; // 定时器,刷新窗口显示图像位图帧
UINT uBorderWidth;
UINT uBorderHeight;
BOOL gbLoadVideo;
DWORD dwWndStyle; // 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HBITMAP CreateBitmapFromPixels(HDC hDC, UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits); HBITMAP PopFrame(void)
{
std::lock_guard<std::mutex> lg(gFrameLock); if (gFrames.empty())
{
return nullptr;
}
else
{
HBITMAP hFrame = gFrames.front();
gFrames.pop_front();
return hFrame;
}
} void PushFrame(HBITMAP hFrame)
{
std::lock_guard<std::mutex> lg(gFrameLock);
gFrames.push_back(hFrame);
} size_t GetFramesSize(void)
{
std::lock_guard<std::mutex> lg(gFrameLock);
return gFrames.size();
} void ReleasePaint(void)
{
if (ghFrameDC) DeleteDC(ghFrameDC);
if (ghFrameBmp) DeleteObject(ghFrameBmp);
if (ghFrameBrush) DeleteObject(ghFrameBrush); ghFrameDC = nullptr;
ghFrameBmp = nullptr;
ghFrameBrush = nullptr;
} void CreatePaint(HWND hWnd)
{
RECT rc;
GetClientRect(hWnd, &rc);
HDC hDC = GetDC(hWnd);
ghFrameDC = CreateCompatibleDC(hDC);
ghFrameBmp = CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
ghFrameBrush = CreateSolidBrush(RGB(, , ));
SelectObject(ghFrameDC, ghFrameBmp);
ReleaseDC(hWnd, hDC);
} void ReleaseFrames(void)
{
std::lock_guard<std::mutex> lg(gFrameLock);
for (auto& hFrame : gFrames)
{
DeleteObject(hFrame);
}
gFrames.clear(); ReleasePaint();
} void PlayFrame(HWND hWnd, UCHAR* buffer, UINT uWidth, UINT uHeight)
{
do
{
if (GetFramesSize() > ( * )) // 缓冲5秒的帧数
{
if (WaitForSingleObject(ghExitEvent, ) == WAIT_OBJECT_0)
{
return;
}
}
else
{
HDC hDC = GetDC(hWnd);
HBITMAP hFrame = CreateBitmapFromPixels(hDC, uWidth, uHeight, , buffer);
if (hFrame)
{
PushFrame(hFrame);
}
ReleaseDC(hWnd, hDC);
break;
} } while (true);
} std::string UnicodeToUTF_8(const wchar_t *pIn, size_t nSize)
{
if (pIn == NULL || nSize == )
{
return "";
} std::string s;
int n = WideCharToMultiByte(CP_UTF8, NULL, pIn, nSize, NULL, , NULL, NULL);
if (n > )
{
s.resize(n);
WideCharToMultiByte(CP_UTF8, NULL, pIn, nSize, &s[], n, NULL, NULL);
} return s;
} int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。
ghExitEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
av_register_all(); // 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTPLAYVIDEO, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); // 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
} HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTPLAYVIDEO)); MSG msg; // 主消息循环:
while (GetMessage(&msg, nullptr, , ))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} return (int) msg.wParam;
} //
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = ;
wcex.cbWndExtra = ;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTPLAYVIDEO));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = /*(HBRUSH)(COLOR_WINDOW+1)*/nullptr;
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTPLAYVIDEO);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex);
} //
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中 HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, , CW_USEDEFAULT, , nullptr, nullptr, hInstance, nullptr); if (!hWnd)
{
return FALSE;
} ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd); return TRUE;
} HBITMAP CreateBitmapFromPixels(HDC hDC, UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits)
{
if (uBitsPerPixel <= ) // NOT IMPLEMENTED YET
return NULL; HBITMAP hBitmap = ;
if (!uWidth || !uHeight || !uBitsPerPixel)
return hBitmap;
LONG lBmpSize = uWidth * uHeight * (uBitsPerPixel / );
BITMAPINFO bmpInfo = { };
bmpInfo.bmiHeader.biBitCount = uBitsPerPixel;
bmpInfo.bmiHeader.biHeight = -(static_cast<LONG>(uHeight)); // 因为拿到的帧图像是颠倒的
bmpInfo.bmiHeader.biWidth = uWidth;
bmpInfo.bmiHeader.biPlanes = ;
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Pointer to access the pixels of bitmap
UINT * pPixels = ;
hBitmap = CreateDIBSection(hDC, (BITMAPINFO *)&
bmpInfo, DIB_RGB_COLORS, (void **)&
pPixels, NULL, ); if (!hBitmap)
return hBitmap; // return if invalid bitmaps memcpy(pPixels, pBits, lBmpSize); return hBitmap;
} void PaintFrame(HWND hWnd, HDC hDC, RECT rc)
{
FillRect(ghFrameDC, &rc, ghFrameBrush);
HBITMAP hFrame = PopFrame();
if (hFrame)
{
BITMAP bmp;
GetObject(hFrame, sizeof(bmp), &bmp);
HDC hFrameDC = CreateCompatibleDC(hDC);
HBITMAP hOld = (HBITMAP)SelectObject(hFrameDC, hFrame);
BitBlt(ghFrameDC, , , bmp.bmWidth, bmp.bmHeight, hFrameDC, , , SRCCOPY);
SelectObject(hFrameDC, hOld);
DeleteObject(hFrame);
DeleteDC(hFrameDC);
} BitBlt(hDC, , , rc.right - rc.left, rc.bottom - rc.top, ghFrameDC, , , SRCCOPY);
} void OpenVideoByFFmpeg(HWND hWnd, const char* szVideo)
{
AVFormatContext* pFmtCtx = nullptr;
AVCodecContext* pCodecCtx = nullptr;
AVCodec* pCodec = nullptr;
AVFrame* pFrameSrc = nullptr;
AVFrame* pFrameRGB = nullptr;
AVPacket* pPkt = nullptr;
UCHAR* out_buffer = nullptr;
struct SwsContext * pImgCtx = nullptr;
int ret = ;
int videoStream = -;
int numBytes = ; pFmtCtx = avformat_alloc_context();
CHECK_TRUE(pFmtCtx);
ret = avformat_open_input(&pFmtCtx, szVideo, nullptr, nullptr);
CHECK_ZERO(ret);
ret = avformat_find_stream_info(pFmtCtx, nullptr);
CHECK_ZERO(ret); for (UINT i = ; i < pFmtCtx->nb_streams; ++i)
{
if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
CHECK_ZERO(videoStream); pCodecCtx = avcodec_alloc_context3(nullptr);
CHECK_TRUE(pCodecCtx);
ret = avcodec_parameters_to_context(pCodecCtx, pFmtCtx->streams[videoStream]->codecpar);
CHECK_ZERO(ret);
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
CHECK_TRUE(pCodec);
ret = avcodec_open2(pCodecCtx, pCodec, nullptr);
CHECK_ZERO(ret); pFrameSrc = av_frame_alloc();
pFrameRGB = av_frame_alloc();
CHECK_TRUE(pFrameSrc);
CHECK_TRUE(pFrameRGB); pImgCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);
CHECK_TRUE(pImgCtx); numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, );
out_buffer = (UCHAR*)av_malloc(numBytes);
CHECK_TRUE(out_buffer); ret = av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer,
AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, );
CHECK_ZERO(ret); pPkt = new AVPacket;
ret = av_new_packet(pPkt, pCodecCtx->width * pCodecCtx->height);
CHECK_ZERO(ret); SetWindowPos(hWnd, nullptr, , , pCodecCtx->width + uBorderWidth, pCodecCtx->height + uBorderHeight, SWP_NOMOVE);
ReleasePaint();
CreatePaint(hWnd);
dwWndStyle = GetWindowLong(hWnd, GWL_STYLE);
::SetWindowLong(hWnd, GWL_STYLE, dwWndStyle&~WS_SIZEBOX);
if (!uFrameTimer) uFrameTimer = SetTimer(hWnd, TIMER_FRAME, , nullptr); while (true)
{
if (av_read_frame(pFmtCtx, pPkt) < )
{
break;
} if (pPkt->stream_index == videoStream)
{
ret = avcodec_send_packet(pCodecCtx, pPkt);
if (ret < ) continue;
ret = avcodec_receive_frame(pCodecCtx, pFrameSrc);
if (ret < ) continue; ret = sws_scale(pImgCtx, pFrameSrc->data, pFrameSrc->linesize,
, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
if (ret <= ) continue;
PlayFrame(hWnd, out_buffer, pCodecCtx->width, pCodecCtx->height);
} av_packet_unref(pPkt);
} if (uFrameTimer)
{
KillTimer(hWnd, uFrameTimer);
uFrameTimer = ;
} if (dwWndStyle) ::SetWindowLong(hWnd, GWL_STYLE, dwWndStyle); cleanup:
if (pFmtCtx) avformat_free_context(pFmtCtx);
if (pCodecCtx) avcodec_free_context(&pCodecCtx);
if (pFrameSrc) av_frame_free(&pFrameSrc);
if (pFrameRGB) av_frame_free(&pFrameRGB);
if (pImgCtx) sws_freeContext(pImgCtx);
if (out_buffer) av_free(out_buffer);
if (pPkt)
{
av_packet_unref(pPkt);
delete pPkt;
}
} void LoadVideoPlay(HWND hWnd)
{
if (gbLoadVideo)
{
return;
} TCHAR szPath[] = { };
DWORD dwPath = ;
OPENFILENAME ofn = { };
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.hInstance = hInst;
ofn.lpstrFile = szPath;
ofn.nMaxFile = dwPath;
ofn.lpstrFilter = _T("Video(*.mp4)\0*.MP4*;*.avi*\0");
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrInitialDir = _T("F:\\"); if (!GetOpenFileName(&ofn))
{
DWORD dwErr = CommDlgExtendedError();
OutputDebugString(_T("GetOpenFileName\n"));
return;
} std::wstring strVideo = szPath;
std::thread loadVideoThread([hWnd, strVideo]() {
gbLoadVideo = TRUE;
std::string sVideo = UnicodeToUTF_8(strVideo.c_str(), strVideo.size());
OpenVideoByFFmpeg(hWnd, sVideo.c_str());
gbLoadVideo = FALSE;
}); loadVideoThread.detach();
} //
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
CreatePaint(hWnd); RECT rc;
GetClientRect(hWnd, &rc);
RECT rcWnd;
GetWindowRect(hWnd, &rcWnd);
uBorderWidth = (rcWnd.right - rcWnd.left) - (rc.right - rc.left) + ;
uBorderHeight = (rcWnd.bottom - rcWnd.top) - (rc.bottom - rc.top) + ;
}
break;
case WM_TIMER:
{
if (uFrameTimer && (uFrameTimer == wParam))
{
if (IsIconic(hWnd)) // 如果最小化了,则直接移除图像帧
{
HBITMAP hFrame = PopFrame();
if (hFrame)
{
DeleteObject(hFrame);
}
}
else
{
InvalidateRect(hWnd, nullptr, FALSE);
}
}
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_OPEN:
LoadVideoPlay(hWnd);
break;
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rc;
GetClientRect(hWnd, &rc);
HDC hdc = BeginPaint(hWnd, &ps);
PaintFrame(hWnd, hdc, rc);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
SetEvent(ghExitEvent);
ReleaseFrames();
PostQuitMessage();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return ;
} // “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE; case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}

main.cpp

完结撒花

FFmpeg入门,简单播放器的更多相关文章

  1. 视频播放器控制原理:ffmpeg之ffplay播放器源代码分析

    版权声明:本文由张坤原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/535574001486630869 来源:腾云阁 ht ...

  2. 基于ffmpeg的C++播放器1

    基于ffmpeg的C++播放器 (1) 2011年12月份的时候发了这篇博客 http://blog.csdn.net/qq316293804/article/details/7107049 ,博文最 ...

  3. 基于FFMPEG的跨平台播放器实现(二)

    基于FFMPEG的跨平台播放器实现(二) 上一节讲到了在Android平台下采用FFmpeg+surface组合打造播放器的方法,这一节讲一下Windows平台FFmpeg + D3D.Linux平台 ...

  4. 基于FFMPEG的跨平台播放器实现

    基于FFMPEG的跨平台播放器实现 一.背景介绍 FFmpeg是一款超级强大的开源多媒体编解码框架,提供了录制.转换以及流化音视频的完整解决方案,包含了libavcodec.libavformat等多 ...

  5. 基于Live555,ffmpeg的RTSP播放器直播与点播

    基于Live555,ffmpeg的RTSP播放器直播与点播 多路RTSP高清视频播放器下载地址:http://download.csdn.net/detail/u011352914/6604437多路 ...

  6. 仿迅雷播放器教程 -- 基于ffmpeg的C++播放器 (1)

    2011年12月份的时候发了这篇博客 http://blog.csdn.net/qq316293804/article/details/7107049 ,博文最后说会开源一个播放器,没想到快两年了,才 ...

  7. ffmpeg学习(三)——ffmpeg+SDL2 实现简单播放器

    本篇实现基于ffmpeg动态库用测试程序播放本地文件和RTSP视频流. 参考文章:http://blog.csdn.net/leixiaohua1020/article/details/8652605 ...

  8. 基于libvlc和wxWidgets的简单播放器代码阅读

    源代码来自 http://git.videolan.org/?p=vlc.git;a=blob_plain;f=doc/libvlc/wx_player.cpp // g++ wx_player.cp ...

  9. 简单播放器(增加sdl事件控制)

    #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscal ...

随机推荐

  1. java系列--HTTP协议

    一.HTTP请求信息 请求行 请求头 空行 消息体 1.防盗链: 枚举类型: 二.中文乱码问题 1.Get提交 String username = request.getParameter(" ...

  2. iOS 之 工厂模式

    参考:http://www.jikexueyuan.com/course/2054_2.html?ss=2 1. 简单工厂 简单工厂类是一个实体类.用于几种相似类的统一创建,简化流程,隔离细节. 下面 ...

  3. C-Sharp网络编程案例解析(Socket类的使用)

    Server端: using System; using System.Collections.Generic; using System.Text; using System.Net; using ...

  4. win32 安装 xcache扩展

    今天整了一上午,才整明白. 我的系统是是 win7 32位 ,用的环境是 Wamp,php版本是5.3.5,后来在http://xcache.lighttpd.net/pub/Releases/3.0 ...

  5. iOS 之 alcatraz (插件管理器)

    1. 安装 1.1. 打开命令行 curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/deploy/Scripts/ins ...

  6. LINQ to Sql系列二 简单查询和联接查询

    这一篇文章主要总结LINQ to sql的简单查询(单表查询)和联接查询(多表查询) 单表查询 需求是我们要输出TClass表中的结果.使用了from-in-select语句,代码如下: public ...

  7. mybatis判断集合为空或者元素个数为零

    mybatis判断集合为空或者元素个数为零: <if test="mlhs != null and mlhs.size() != 0"> and t.mlh_name ...

  8. .md即markdown文件的基本常用编写语法(图文并茂)

    序言: 很久没有写博客了,感觉只要是不写博客,人就很变得很懒,学的知识点感觉还是记不住,渐渐地让我明白,看的越多,懂的越少(你这话不是有毛病吗?应该是看的越多,懂的越多才对),此话怎讲,当你在茫茫的前 ...

  9. C# MongoDB 查询,分组,聚合,排序,条件,分页

    先下载个C#的驱动.MongoDB提供各种主流与非主流预言的开发驱动. C# Driver 下载地址:这里 CSharp Driver Tutorial:这里 下载文件安装或者解压缩包 如果您是安装, ...

  10. 作为测试人员,我是这么报BUG的

    在测试人员提需求的时候,大家经常会看到,测试员和开发一言不合就上BUG.然后开发一下就炸了,屡试不爽,招招致命. 曾经看到有个段子这么写道: 不要对程序员说,你的代码有BUG. 他的第一反应是:1.你 ...