FFmpeg入门,简单播放器
一个偶然的机缘,好像要做直播相关的项目
为了筹备,前期做一些只是储备,于是开始学习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入门,简单播放器的更多相关文章
- 视频播放器控制原理:ffmpeg之ffplay播放器源代码分析
版权声明:本文由张坤原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/535574001486630869 来源:腾云阁 ht ...
- 基于ffmpeg的C++播放器1
基于ffmpeg的C++播放器 (1) 2011年12月份的时候发了这篇博客 http://blog.csdn.net/qq316293804/article/details/7107049 ,博文最 ...
- 基于FFMPEG的跨平台播放器实现(二)
基于FFMPEG的跨平台播放器实现(二) 上一节讲到了在Android平台下采用FFmpeg+surface组合打造播放器的方法,这一节讲一下Windows平台FFmpeg + D3D.Linux平台 ...
- 基于FFMPEG的跨平台播放器实现
基于FFMPEG的跨平台播放器实现 一.背景介绍 FFmpeg是一款超级强大的开源多媒体编解码框架,提供了录制.转换以及流化音视频的完整解决方案,包含了libavcodec.libavformat等多 ...
- 基于Live555,ffmpeg的RTSP播放器直播与点播
基于Live555,ffmpeg的RTSP播放器直播与点播 多路RTSP高清视频播放器下载地址:http://download.csdn.net/detail/u011352914/6604437多路 ...
- 仿迅雷播放器教程 -- 基于ffmpeg的C++播放器 (1)
2011年12月份的时候发了这篇博客 http://blog.csdn.net/qq316293804/article/details/7107049 ,博文最后说会开源一个播放器,没想到快两年了,才 ...
- ffmpeg学习(三)——ffmpeg+SDL2 实现简单播放器
本篇实现基于ffmpeg动态库用测试程序播放本地文件和RTSP视频流. 参考文章:http://blog.csdn.net/leixiaohua1020/article/details/8652605 ...
- 基于libvlc和wxWidgets的简单播放器代码阅读
源代码来自 http://git.videolan.org/?p=vlc.git;a=blob_plain;f=doc/libvlc/wx_player.cpp // g++ wx_player.cp ...
- 简单播放器(增加sdl事件控制)
#include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscal ...
随机推荐
- AndroidManifest.xml文件
AndroidManifest.xml常用标签解读 1.全局篇(包名,版本信息) 2.组件篇(四大组件) Activity Service Content Provider Broadcast Rec ...
- ubuntu下常用的apt-get 命令参数
apt-cache search package 搜索包 apt-cache show package 获取包的相关信息,如说明.大小.版本等 sudo apt-get install package ...
- spring整合mybatis,springMVC的0配置文件方式
0配置文件的形式主要是采用spring3.0提供的@configuration注解和spring容器在启动的时候会加载实现了WebApplicationInitializer的类,并调用其onStar ...
- 如何在Eclipse中配置python开发环境
考虑到网上关于Eclipse中配置Python开发环境的文章千篇一律,故写此文以总结. 本文主要内容是:三种Pydev配置方法和一种PyDev卸载方法. 本文的前提是你已经安装了Eclipse和pyt ...
- Zepto 使用中的一些注意点(转)
http://www.zeptojs.cn/ zepto英文站在线文档 http://www.css88.com/doc/zeptojs_api/ zepto中文站在线文档 htt ...
- 与文件上传到的三个类:FileItem类、ServletFileUpload 类、DiskFileItemFactory类
文件上传: ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中, 在使用ServletFileUpload对象解析请求时需要根据DiskFi ...
- js架构设计模式——由项目浅谈JS中MVVM模式
1. 背景 最近项目原因使用了durandal.js和knockout.js,颇有受益.决定写一个比较浅显的总结. 之前一直在用SpringMVC框架写后台,前台是用JSP+JS+标签库,算是很 ...
- mysql 使用sqldump来进行数据库还原
MYSQLdump参数详解 mysqldump备份: 复制代码代码如下: mysqldump -u用户名 -p密码 -h主机 数据库 a -w “sql条件” –lock-all-tables > ...
- 【JAVA笔记】JAVA后端实现统一扫码支付:微信篇
最近做完了一个项目,正好没事做,产品经理就给我安排了一个任务. 做一个像收钱吧这样可以统一扫码收钱的功能. 一开始并不知道是怎么实现的,咨询了好几个朋友,才知道大概的业务流程:先是开一个网页用 ...
- 玩转 Ceph 的正确姿势
玩转 Ceph 的正确姿势 本文先介绍 Ceph, 然后会聊到一些正确使用 Ceph 的姿势:在集群规模小的时候,Ceph 怎么玩都没问题:但集群大了(到PB级别),这些准则可是保证集群健康运行的不二 ...