使用SDL 2.0.20 Simple DirectMedia Layer - SDL version 2.0.20 (stable)https://www.libsdl.org/release/SDL2-devel-2.0.20-VC.zip

参考文章:

ffmpeg tutorial

学习文章的同时基于ffmpeg 4.4 和 sdl 2.0 做同样功能的mfc demo。

可以先参考:CSDN

在制作播放器时,我们将使用 SDL 来输出媒体文件的音频和视频。 SDL 是一个出色的跨平台多媒体库,可用于 MPEG 播放软件、模拟器和许多视频游戏。您需要为您的系统下载并安装 SDL 开发库,以便编译本教程中的程序。

所以我们目前的计划是替换教程 1 中的 SaveFrame() 函数,而是将我们的帧输出到屏幕上。但首先我们必须先了解如何使用 SDL 库。首先,我们必须包含库并初始化 SDL:

参考SDL2源代码分析1:初始化(SDL_Init())_雷霄骅(leixiaohua1020)的专栏-CSDN博客_sdl_init

使用SDL 播放一个视频代码流程大体如下:

初始化:

1. SDL_Init() 初始化SDL

2. SDL_CreateWindow(): 创建窗口

3. SDL_CreateRenderer() 基于窗口创建渲染器

4. SDL_CreateTexture() 创建纹理

循环渲染数据

SDL_UpdateTexture 设置纹理的数据

SDL_RenderCopy 纹理复制给渲染器

SDL_RenderPresent 显示


// MFCAudioResampleDlg.cpp: 实现文件
//#include "pch.h"
#include "framework.h"
#include "MFCAudioResample.h"
#include "MFCAudioResampleDlg.h"
#include "afxdialogex.h"
#include <thread>
#include <future>//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)#define BREAK_EVENT  (SDL_USEREVENT + 2)std::string GetFFmpegErorString(int errnum)
{static CHAR g_av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 };return std::string(av_make_error_string(g_av_error, AV_ERROR_MAX_STRING_SIZE, errnum));
}void msgBoxFFmpegError(int errnum)
{::MessageBoxA(0, 0, GetFFmpegErorString(errnum).c_str(), 0);
}std::wstring GetOneFile(BOOL bOpen = TRUE)
{CString FilePathName;CFileDialog dlg(bOpen, NULL, NULL, NULL, NULL);//TRUE为OPEN对话框,FALSE为SAVE AS对话框 if (dlg.DoModal() == IDOK) {//清空FilePathName = dlg.GetPathName();}return std::wstring(FilePathName.GetBuffer());
}#ifdef _DEBUG
#define new DEBUG_NEW
#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx
{
public:CAboutDlg();// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现
protected:DECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()// CMFCAudioResampleDlg 对话框CMFCAudioResampleDlg::CMFCAudioResampleDlg(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_MFCAUDIORESAMPLE_DIALOG, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CMFCAudioResampleDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_EDIT_INPUT_FILE, m_targetFilePath);
}BEGIN_MESSAGE_MAP(CMFCAudioResampleDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_CHOOSE_FILE, &CMFCAudioResampleDlg::OnBnClickedButtonChooseFile)
END_MESSAGE_MAP()// CMFCAudioResampleDlg 消息处理程序BOOL CMFCAudioResampleDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);         // 设置大图标SetIcon(m_hIcon, FALSE);        // 设置小图标// TODO: 在此添加额外的初始化代码return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}void CMFCAudioResampleDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。void CMFCAudioResampleDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}
}//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCAudioResampleDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);}
void CMFCAudioResampleDlg::SaveFrame(AVFrame* pFrame, int width, int height, int iFrame)
{m_Frame = pFrame;SDL_Event event;event.type = REFRESH_EVENT;SDL_PushEvent(&event);}void CMFCAudioResampleDlg::OnBnClickedButtonChooseFile()
{if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {assert(false);}// TODO: 在此添加控件通知处理程序代码auto target = GetOneFile();m_targetFilePath.SetWindowTextW(target.c_str());UpdateData(TRUE);CString strTargetWindowText;m_targetFilePath.GetWindowTextW(strTargetWindowText);AVFormatContext* avFormatContext = avformat_alloc_context();USES_CONVERSION;if (!PathFileExists(strTargetWindowText.GetBuffer())) {return;}auto result = avformat_open_input(&avFormatContext, W2A((strTargetWindowText.GetBuffer())), nullptr, nullptr);if (result < 0) {assert(false);}result = avformat_find_stream_info(avFormatContext, NULL);if (result < 0){assert(false);}av_dump_format(avFormatContext, 0, NULL, 0);// Find the first video streamint videoStream = -1;for (int i = 0; i < avFormatContext->nb_streams; i++)if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {videoStream = i;break;}if (videoStream == -1)return ; // Didn't find a video streamAVCodecContext* avCodecContext = avcodec_alloc_context3(NULL);result = avcodec_parameters_to_context(avCodecContext, avFormatContext->streams[videoStream]->codecpar);if (result < 0){assert(false);}avCodecContext->pkt_timebase = avFormatContext->streams[videoStream]->time_base;AVCodec* codec = avcodec_find_decoder(avCodecContext->codec_id);avCodecContext->codec_id = codec->id;result = avcodec_open2(avCodecContext, codec, nullptr);if (result < 0){msgBoxFFmpegError(result);assert(false);}m_sdlThread = std::move(std::thread([this, avCodecContext]() {m_sdlWindow = SDL_CreateWindow("sdl display", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, avCodecContext->width, avCodecContext->height, SDL_WINDOW_OPENGL);assert(m_sdlWindow);m_sdlRender = SDL_CreateRenderer(m_sdlWindow, -1, 0);assert(m_sdlRender);m_sdlTexture = SDL_CreateTexture(m_sdlRender, SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGB24, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING, avCodecContext->width, avCodecContext->height);assert(m_sdlTexture);SDL_Event event;while (1){SDL_WaitEvent(&event);if (event.type == REFRESH_EVENT) {SDL_UpdateTexture(m_sdlTexture, nullptr, m_Frame->data[0], avCodecContext->width * 3);SDL_Rect sdlRect = {};sdlRect.x = 0;sdlRect.y = 0;sdlRect.h = avCodecContext->height;sdlRect.w = avCodecContext->width;SDL_RenderClear(m_sdlRender);SDL_RenderCopy(m_sdlRender, m_sdlTexture, nullptr, &sdlRect);SDL_RenderPresent(m_sdlRender);}else if (event.type == SDL_WINDOWEVENT) {}else if (event.type == SDL_QUIT) {}else if (event.type == BREAK_EVENT) {break;}}}));AVFrame* pFrame = nullptr;pFrame = av_frame_alloc();// 我们最终的目的是将原始视频帧存储为24-bit RGB 视频。AVFrame* pFrameRGB24 = av_frame_alloc();uint8_t* bufferRawRGB24 = nullptr;const int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, avCodecContext->width, avCodecContext->height, 1);bufferRawRGB24 = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));av_image_fill_arrays(pFrameRGB24->data, pFrameRGB24->linesize, bufferRawRGB24, AV_PIX_FMT_RGB24, avCodecContext->width, avCodecContext->height, 1);// 读取数据// sws_ctx 定义了从pix_fmt 到 AV_PIX_FMT_RGB24的转换struct SwsContext* sws_ctx = NULL;int frameFinished;// initialize SWS context for software scalingsws_ctx = sws_getContext(avCodecContext->width,avCodecContext->height,avCodecContext->pix_fmt,avCodecContext->width,avCodecContext->height,AV_PIX_FMT_RGB24,SWS_BILINEAR,NULL,NULL,NULL);int i = 0;AVPacket packet;bool bReadEof = false;while (true){Sleep(33);int readResult = -1;if (!bReadEof){readResult = av_read_frame(avFormatContext, &packet);if (readResult < 0) {::MessageBoxA(0, 0, GetFFmpegErorString(readResult).c_str(), 0);bReadEof = true;}else if (readResult == 0) {static int iCnt = 0;if (packet.stream_index == videoStream) {++iCnt;}CString str;str.Format(L"cunt[%d]\r\n", iCnt);OutputDebugStringW(str.GetBuffer());}}if (bReadEof){// 需要给刷空avcodec_send_packet(avCodecContext, NULL);}else{// Is this a packet from the video stream?if (packet.stream_index == videoStream) {// Decode video frameavcodec_send_packet(avCodecContext, &packet);}}int receiveResult = avcodec_receive_frame(avCodecContext, pFrame);// Did we get a video frame?if (receiveResult == 0) {// Convert the image from its native format to RGBsws_scale(sws_ctx, (uint8_t const* const*)pFrame->data,pFrame->linesize, 0, avCodecContext->height,pFrameRGB24->data, pFrameRGB24->linesize);++i;SaveFrame(pFrameRGB24, avCodecContext->width,avCodecContext->height, i);}else if (receiveResult == AVERROR_EOF){::MessageBoxA(0, 0, "read eof", 0);SDL_Event event;event.type = BREAK_EVENT;SDL_PushEvent(&event);break;}else if(receiveResult == AVERROR(EAGAIN)){if (bReadEof) {break;}else {}}else {msgBoxFFmpegError(receiveResult);}// Free the packet that was allocated by av_read_frameif(readResult == 0)av_packet_unref(&packet);}SDL_Quit();av_frame_free(&pFrame);av_frame_free(&pFrameRGB24);av_free(bufferRawRGB24);avcodec_close(avCodecContext);avformat_close_input(&avFormatContext);
}

完整项目ffmpegsdl2.0rgb24player-互联网文档类资源-CSDN下载

ffmpeg4.4 学习笔记 -(2)读取视频文件并用SDL 显示相关推荐

  1. Java-马士兵设计模式学习笔记-观察者模式-读取properties文件改成单例模式

    一.概述 1.目标:读取properties文件改成单例模式 二.代码 1.Test.java 1 class WakenUpEvent{ 2 3 private long time; 4 priva ...

  2. 学习笔记_ncl_读取nc文件中的变量_制作nc文件的方法

    由于生成的文件较大较多,在超算上不方便使用matlab,只好向ncl势力低头 目的是在超算上对相关结果进行第一步简单处理后,下载到pc上再进行其他处理 ncl新手 如果是从已有的nc文件中读取变量到新 ...

  3. python学习笔记:读取xyz文件

    在药学的Ai研发过程中,经常要制作清洗文件和处理各种文件格式.利用openBabel这样功能能够大大减轻转换过程的麻烦.然而偶然也需要应对一下场景下处理xyz文件抽取相关的坐标体系去计算小分子之间的作 ...

  4. python使用opencv保存视频_Pythone OpenCV学习笔记之:视频文件读取与保存

    # -*- coding: utf-8 -*- # 读取和保存视频 import cv2 as cv import numpy as np def decode_fourcc(v): v = int( ...

  5. [云炬Python学习笔记] Python读取指定文件夹下的文件

    1 # -*- coding: utf-8 -*-2 import csv3 import os4 import pandas as pd5 #提取文件夹下的地址+文件名,源文件设定排序规则6 def ...

  6. json字段顺序读取 python_如何利用Python批量读取视频文件的时间长度?

    本期的主题是利用Python来实现对视频文件时间长度的读取. 在学习编程语言时,相比较于通过书本来学习知识,我更喜欢通过观看学习视频的方式来进行学习,通过主讲老师的讲解,我能很直观且快速的了解一些知识 ...

  7. Linux学习笔记(二)——文件与磁盘系统

    Linux学习笔记(二)--文件与磁盘系统 文件操作 ls [选项][目录名]-l :列出长数据串,包含文件的属性与权限数据等-a :列出全部的文件,连同隐藏文件(开头为.的文件)一起列出来(常用)- ...

  8. VideoCapture 读取视频文件,显示视频(帧)信息

    #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <ope ...

  9. [python教程入门学习]python学习笔记(CMD执行文件并传入参数)

    本文章向大家介绍python学习笔记(CMD执行文件并传入参数),主要包括python学习笔记(CMD执行文件并传入参数)使用实例.应用技巧.基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋 ...

最新文章

  1. 算法对建筑业的影响,不仅仅是画图
  2. Double Precision Format(DPF)
  3. 2017-2018-2 20165211 实验五《网络编程与安全》实验报告
  4. mysql 索引 lt =,当同时使用gt; =和lt; =时如何索引MySQL表?
  5. 病毒及攻击防御手册之八
  6. jQuery Mobile中对话框dialog的data-*选项
  7. 排序算法(java)——— 堆排序
  8. WARN [org.springframework.web.servlet.PageNotFound] No mapping found for HTTP request with URI
  9. Logback设置property参数
  10. 在钉钉如何愉快地给老师点很多赞
  11. 香油和一个生鸡蛋,干咳偏方
  12. 两种降维方法的总结——LLE(局部线性嵌入),ISOMAP(等度量映射)
  13. Java实现单向链表基本功能
  14. Gershgorin圆盘定理
  15. SVPWM中直流电压利用率的问题
  16. 你和那些优秀的人差距在哪里?
  17. Packet Tracer 思科模拟器练习8
  18. 苦孩子传奇,卖冰棍的穷学生成了月入八千的中国高校第一IT撰稿人
  19. sql 使用汇总(PQSQL)
  20. vue.js 利用canvas绘制仪表盘圆环进度条-带动画

热门文章

  1. 大数据重新定义未来,2018 中国大数据技术大会(BDTC)豪华盛宴抢先看!
  2. 为什么选择体育·棒球运动
  3. SDIO wifi Marvell8801/Marvell88w8801 介绍(三) ---- Marvell8801/Marvell88w8801寄存器介绍
  4. Linux操作系统(详解及配置操作)
  5. 直流电机的电流、转速、电压的关系
  6. html中去除浮漂有什么作用,各种浮漂用途介绍及选择
  7. DS18B20数字温度传感器
  8. Brendan Gregg ----Linux Performance Tools NEWS
  9. 计算机系统集成双代号网络图讲解,系统集成项目管理工程师双代号时标网络图知识解读...
  10. Colored Rectangles【简单DP】