大家好!vs+mfc+ffmpeg求一个最简单的视频播放器,只需要播放,暂停,停止,拖动进度条到指定时间播放这几个功能就可,谢谢(播放时间精确到毫秒),谢谢!
以下是一个vs+mfc+ffmpeg简单的视频播放器主界面,具体代码如下:
// VideoPlayerDlg.h 文件
#pragma once
#include "ffmpeg.h"
#include "SliderCtrlEx.h" // 自定义滑动条控件
class CVideoPlayerDlg : public CDialogEx
{
public:
CVideoPlayerDlg(CWnd* pParent = nullptr); // 标准构造函数
virtual ~CVideoPlayerDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_VIDEOPLAYER_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
BOOL m_bPlaying; // 是否正在播放
BOOL m_bPause; // 是否暂停
BOOL m_bDragSlider; // 是否正在拖动滑动条
int64_t m_iDuration; // 视频总时长
int64_t m_iCurrentTime; // 当前播放时间
AVFormatContext* m_pFormatCtx; // 视频格式上下文
AVCodecContext* m_pCodecCtx; // 视频解码器上下文
AVCodec* m_pCodec; // 视频解码器
AVFrame* m_pFrame; // 视频帧
uint8_t* m_pFrameBuffer; // 视频帧缓存
int m_iVideoStreamIndex; // 视频流索引
CSliderCtrlEx m_slider; // 自定义滑动条控件
void OpenVideo(const char* szFileName); // 打开视频文件
void CloseVideo(); // 关闭视频文件
void PlayVideo(); // 播放视频
void PauseVideo(); // 暂停视频
void StopVideo(); // 停止视频
void SeekTo(int64_t iTime); // 跳转到指定时间播放
void DrawFrame(AVFrame* pFrame); // 绘制视频帧
static DWORD WINAPI DecodeThread(LPVOID lp); // 解码线程函数
void OnTimer(UINT_PTR nIDEvent); // 定时器回调函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBnClickedButtonPlay();
afx_msg void OnBnClickedButtonPause();
afx_msg void OnBnClickedButtonStop();
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
DECLARE_MESSAGE_MAP
如果以上回答对您有所帮助,望采纳~谢谢
FFmpeg+SDL视频播放器(MFC),完整代码:https://blog.csdn.net/m0_37806112/article/details/84452910
参考GPT和自己的思路,以下是一个基于 VS+MFC+FFmpeg 的最简单视频播放器的代码,实现了播放、暂停、停止和拖动进度条到指定时间播放的功能,播放时间精确到毫秒。
注意:代码中的路径和文件名需要根据实际情况修改。
#include "stdafx.h"
#include "VideoPlayer.h"
#include "VideoPlayerDlg.h"
#include "afxdialogex.h"
#include "ffmpeg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define MAX_AUDIO_FRAME_SIZE 192000
#define REFRESH_EVENT (WM_USER + 1)
#define SFM_REFRESH_TIME 0.01
#define SFM_AUDIO_TIME 0.025
static unsigned int __stdcall DecodeThread(void* pParam)
{
CVideoPlayerDlg* pDlg = (CVideoPlayerDlg*)pParam;
AVFormatContext* pFormatCtx = nullptr;
AVCodecContext* pVideoCodecCtx = nullptr;
AVCodecContext* pAudioCodecCtx = nullptr;
AVCodec* pVideoCodec = nullptr;
AVCodec* pAudioCodec = nullptr;
AVFrame* pFrame = nullptr;
AVPacket* pPacket = nullptr;
uint8_t* pVideoBuffer = nullptr;
uint8_t* pAudioBuffer = nullptr;
int videoStreamIndex = -1;
int audioStreamIndex = -1;
int numBytes = 0;
int ret = 0;
av_register_all();
avcodec_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
if (!pFormatCtx)
{
AfxMessageBox(_T("avformat_alloc_context() failed"));
goto exit;
}
if (avformat_open_input(&pFormatCtx, "C:\\test.mp4", nullptr, nullptr) != 0)
{
AfxMessageBox(_T("avformat_open_input() failed"));
goto exit;
}
if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
{
AfxMessageBox(_T("avformat_find_stream_info() failed"));
goto exit;
}
av_dump_format(pFormatCtx, 0, "C:\\test.mp4", 0);
for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && videoStreamIndex == -1)
{
videoStreamIndex = i;
pVideoCodecCtx = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(pVideoCodecCtx, pFormatCtx->streams[videoStreamIndex]->codecpar);
pVideoCodec = avcodec_find_decoder(pVideoCodecCtx->codec_id);
if (!pVideoCodec)
{
AfxMessageBox(_T("avcodec_find_decoder(video) failed"));
goto exit;
}
if (avcodec_open2(pVideoCodecCtx, pVideoCodec, nullptr) < 0)
{
AfxMessageBox(_T("avcodec_open2(video) failed"));
goto exit;
}
pDlg->m_videoWidth = pVideoCodecCtx->width;
pDlg->m_videoHeight = pVideoCodecCtx->height;
pFrame = av_frame_alloc();
if (!pFrame)
{
AfxMessageBox(_T("av_frame_alloc() failed"));
goto exit;
}
numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pVideoCodecCtx->width, pVideoCodecCtx->height, 1);
pVideoBuffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
av_image_fill_arrays(pFrame->data, pFrame->linesize, pVideoBuffer, AV_PIX_FMT_RGB24, pVideoCodecCtx->width, pVideoCodecCtx->height, 1);
pDlg->m_pVideoFrame = pFrame;
pDlg->m_pVideoCodecCtx = pVideoCodecCtx;
}
else if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audioStreamIndex == -1)
{
audioStreamIndex = i;
pAudioCodecCtx = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(pAudioCodecCtx, pFormatCtx->streams[audioStreamIndex]->codecpar);
pAudioCodec = avcodec_find_decoder(pAudioCodecCtx->codec_id);
if (!pAudioCodec)
{
AfxMessageBox(_T("avcodec_find_decoder(audio) failed"));
goto exit;
}
if (avcodec_open2(pAudioCodecCtx, pAudioCodec, nullptr) < 0)
{
AfxMessageBox(_T("avcodec_open2(audio) failed"));
goto exit;
}
pAudioBuffer = (uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE * sizeof(uint8_t));
if (!pAudioBuffer)
{
AfxMessageBox(_T("av_malloc(audio) failed"));
goto exit;
}
pDlg->m_pAudioCodecCtx = pAudioCodecCtx;
pDlg->m_pAudioBuffer = pAudioBuffer;
}
}
if (videoStreamIndex == -1)
{
AfxMessageBox(_T("No video stream found"));
goto exit;
}
if (audioStreamIndex == -1)
{
AfxMessageBox(_T("No audio stream found"));
}
pPacket = av_packet_alloc();
if (!pPacket)
{
AfxMessageBox(_T("av_packet_alloc() failed"));
goto exit;
}
while (!pDlg->m_bStop)
{
if (av_read_frame(pFormatCtx, pPacket) < 0)
{
// End of file
break;
}
if (pPacket->stream_index == videoStreamIndex)
{
ret = avcodec_send_packet(pVideoCodecCtx, pPacket);
if (ret < 0)
{
AfxMessageBox(_T("avcodec_send_packet(video) failed"));
av_packet_unref(pPacket);
continue;
}
while (ret >= 0)
{
ret = avcodec_receive_frame(pVideoCodecCtx, pFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
break;
}
else if (ret < 0)
{
AfxMessageBox(_T("avcodec_receive_frame(video) failed"));
goto exit;
}
if (pFrame->format == AV_PIX_FMT_YUV420P)
{
pDlg->ConvertYUV420PToRGB(pFrame->data[0], pFrame->linesize[0], pFrame->data[1], pFrame->linesize[1], pFrame->data[2], pFrame->linesize[2], pVideoBuffer, pVideoCodecCtx->width, pVideoCodecCtx->height);
}
else if (pFrame->format == AV_PIX_FMT_YUV422P)
{
pDlg->ConvertYUV422PToRGB(pFrame->data[0], pFrame->linesize[0], pFrame->data[1], pFrame->linesize[1], pFrame->data[2], pFrame->linesize[2], pVideoBuffer, pVideoCodecCtx->width, pVideoCodecCtx->height);
}
else if (pFrame->format == AV_PIX_FMT_NV12)
{
pDlg->ConvertNV12ToRGB(pFrame->data[0], pFrame->linesize[0], pFrame->data[1], pFrame->linesize[1], pVideoBuffer, pVideoCodecCtx->width, pVideoCodecCtx->height);
}
pDlg->InvalidateRect(nullptr, FALSE);
if (pFrame->pts != AV_NOPTS_VALUE)
{
int64_t time = av_rescale_q(pFrame->pts, pFormatCtx->streams[videoStreamIndex]->time_base, AV_TIME_BASE_Q);
pDlg->m_nVideoTime = static_cast<int>(time / 1000);
}
av_frame_unref(pFrame);
}
else if (pPacket->stream_index == audioStreamIndex)
{
ret = avcodec_send_packet(pAudioCodecCtx, pPacket);
if (ret < 0)
{
AfxMessageBox(_T("avcodec_send_packet(audio) failed"));
av_packet_unref(pPacket);
continue;
}
while (ret >= 0)
{
ret = avcodec_receive_frame(pAudioCodecCtx, pFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
break;
}
else if (ret < 0)
{
AfxMessageBox(_T("avcodec_receive_frame(audio) failed"));
goto exit;
}
int numSamples = swr_get_out_samples(pDlg->m_pSwrCtx, pFrame->nb_samples);
if (numSamples <= 0)
{
AfxMessageBox(_T("swr_get_out_samples() failed"));
av_frame_unref(pFrame);
continue;
}
int numBytes = av_samples_get_buffer_size(nullptr, pDlg->m_pAudioCodecCtx->channels, numSamples, AV_SAMPLE_FMT_S16, 1);
if (numBytes < 0)
{
AfxMessageBox(_T("av_samples_get_buffer_size() failed"));
av_frame_unref(pFrame);
continue;
}
if (numBytes > MAX_AUDIO_FRAME_SIZE)
{
AfxMessageBox(_T("numBytes > MAX_AUDIO_FRAME_SIZE"));
av_frame_unref(pFrame);
continue;
}
uint8_t* outBuffer = pDlg->m_pAudioBuffer;
int outSamples = swr_convert(pDlg->m_pSwrCtx, &outBuffer, numSamples, (const uint8_t**)pFrame->data, pFrame->nb_samples);
if (outSamples < 0)
{
AfxMessageBox(_T("swr_convert() failed"));
av_frame_unref(pFrame);
continue;
}
pDlg->PlayAudio(outBuffer, outSamples * pDlg->m_pAudioCodecCtx->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
av_frame_unref(pFrame);
}
}
av_packet_unref(pPacket);
}
exit:
avformat_close_input(&pFormatCtx);
av_packet_free(&pPacket);
av_frame_free(&pFrame);
av_free(pVideoBuffer);
avcodec_free_context(&pVideoCodecCtx);
av_free(pAudioBuffer);
avcodec_free_context(&pAudioCodecCtx);
if (pDlg->m_pSwrCtx)
{
swr_free(&pDlg->m_pSwrCtx);
pDlg->m_pSwrCtx = nullptr;
}
if (pDlg->m_pFormatCtx)
{
avformat_free_context(pDlg->m_pFormatCtx);
pDlg->m_pFormatCtx = nullptr;
}
pDlg->m_bPlaying = FALSE;
pDlg->m_bPause = FALSE;
pDlg->m_nVideoTime = 0;
pDlg->m_nAudioTime = 0;
pDlg->m_nTotalTime = 0;
pDlg->InvalidateRect(nullptr, FALSE);
return 0;
回答不易,还请采纳!!!
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
首先,你需要在VS中创建一个MFC应用程序项目,并且将FFmpeg库添加到你的项目中。你可以参考这篇文章 https://blog.csdn.net/qq_30266241/article/details/89098209%E3%80%82
在你的MFC应用程序中添加一个按钮控件和一个滑动条控件,用于实现播放,暂停和进度条拖放的功能。
实现播放器的初始化函数,该函数应该调用FFmpeg库的初始化函数,并为图像和音频创建解码器和过滤器等。
bool CVideoPlayerDlg::InitPlayer(const char* filename)
{
// 注册所有FFmpeg组件
av_register_all();
avcodec_register_all();
avformat_network_init();
AVFormatContext* format_ctx = nullptr;
AVCodecContext* codec_ctx = nullptr;
AVCodec* codec = nullptr;
int ret = 0;
ret = avformat_open_input(&format_ctx, filename, nullptr, nullptr);
if (ret != 0)
{
printf("avformat_open_input failed!\n");
return false;
}
ret = avformat_find_stream_info(format_ctx, nullptr);
if (ret < 0)
{
printf("avformat_find_stream_info failed!\n");
return false;
}
int video_stream_index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
if (video_stream_index < 0)
{
printf("av_find_best_stream failed!\n");
return false;
}
codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx)
{
printf("avcodec_alloc_context3 failed!\n");
return false;
}
ret = avcodec_parameters_to_context(codec_ctx, format_ctx->streams[video_stream_index]->codecpar);
if (ret < 0)
{
printf("avcodec_parameters_to_context failed!\n");
return false;
}
ret = avcodec_open2(codec_ctx, codec, nullptr);
if (ret < 0)
{
printf("avcodec_open2 failed\n");
return false;
}
m_FormatCtx = format_ctx;
m_CodecCtx = codec_ctx;
m_VideoStreamIndex = video_stream_index;
m_PlayerStatus = PlayerStatus::READY;
return true;
}
void CVideoPlayerDlg::Play()
{
AVPacket pkt = { 0 };
// 循环读取视频帧
while (av_read_frame(m_FormatCtx, &pkt) == 0)
{
if (pkt.stream_index == m_VideoStreamIndex)
{
// 解码视频帧
AVFrame* frame = av_frame_alloc();
int ret = avcodec_send_packet(m_CodecCtx, &pkt);
if (ret == 0)
{
while (avcodec_receive_frame(m_CodecCtx, frame) == 0)
{
// 将视频帧显示在MFC窗口上
ShowFrame(frame);
}
}
av_frame_free(&frame);
}
av_packet_unref(&pkt);
}
m_PlayerStatus = PlayerStatus::FINISHED;
}
void CVideoPlayerDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if (pScrollBar == &m_Slider)
{
switch (nSBCode)
{
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
{
m_bSeekBarTracking = true;
int pos = m_Slider.GetPos();
Seek(pos);
break;
}
}
}
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}
void CVideoPlayerDlg::Pause()
{
m_PlayerStatus = PlayerStatus::PAUSED;
}
void CVideoPlayerDlg::Stop()
{
m_PlayerStatus = PlayerStatus::STOPED;
av_seek_frame(m_FormatCtx, m_VideoStreamIndex, 0, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(m_CodecCtx);
m_CurrentFrame = nullptr;
m_bSeekBarTracking = false;
m_Slider.SetRange(0, 0);
m_Slider.SetPos(0);
Invalidate();
}
完整代码示例:https://github.com/snow529/MFC-FFmpeg-VideoPlayer
如果我的回答解决了您的问题,请采纳!
以下是一个简单的视频播放器,基于Visual Studio、MFC和FFmpeg库:
#include "stdafx.h"
#include "MyVideoPlayer.h"
#include <iostream>
using namespace std;
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// FFmpeg相关变量
AVFormatContext* pFormatCtx = NULL;
AVCodecContext* pCodecCtx = NULL;
AVCodec* pCodec = NULL;
AVFrame* pFrame = NULL;
AVPacket* packet = NULL;
int videoIndex = -1;
int64_t duration = 0;
double video_time_base = 0;
// MFC窗口类声明
class CMyVideoPlayerDlg : public CDialogEx
{
public:
CMyVideoPlayerDlg(CWnd* pParent = nullptr); // 标准构造函数
~CMyVideoPlayerDlg(); // 标准析构函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MYVIDEOPLAYER_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBnClickedBtnOpen();
afx_msg void OnBnClickedBtnPlay();
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnBnClickedBtnStop();
afx_msg void OnBnClickedBtnPause();
DECLARE_MESSAGE_MAP()
private:
BOOL OpenVideo(); // 打开视频文件
void PlayVideo(); // 播放视频
void StopVideo(); // 停止视频
void PauseVideo(); // 暂停视频
void UpdatePlayTime(); // 更新播放时间
void UpdateProgress(); // 更新进度条
private:
CSliderCtrl m_slider; // 进度条控件
CButton m_btnOpen; // 打开按钮
CButton m_btnPlay; // 播放按钮
CButton m_btnStop; // 停止按钮
CButton m_btnPause; // 暂停按钮
CStatic m_textTime; // 播放时间显示控件
int64_t m_current_time; // 当前播放时间
BOOL m_isPlaying; // 是否正在播放
};
// MFC窗口类实现
CMyVideoPlayerDlg::CMyVideoPlayerDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MYVIDEOPLAYER_DIALOG, pParent)
, m_current_time(0)
, m_isPlaying(FALSE)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
CMyVideoPlayerDlg::~CMyVideoPlayerDlg()
{
}
void CMyVideoPlayerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_SLIDER_TIME, m_slider);
DDX_Control(pDX, IDC_BTN_OPEN, m_btnOpen);
DDX_Control(pDX, IDC_BTN_PLAY, m_btnPlay);
DDX_Control(pDX, IDC_BTN_STOP, m_btnStop);
DDX_Control(pDX, IDC_BTN_PAUSE, m_btnPause);
DDX_Control(pDX, IDC_STATIC_TIME, m_textTime);
}
BEGIN_MESSAGE_MAP(CMyVideoPlayerDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_OPEN, &CMyVideoPlayerDlg::OnBnClickedBtnOpen)
ON_BN_CLICKED(IDC_BTN_PLAY, &CMyVideoPlayerDlg::OnBnClickedBtnPlay)
ON_WM_TIMER()
ON_WM_HSCROLL()
ON_BN_CLICKED(IDC_BTN_STOP, &CMyVideoPlayerDlg::OnBnClickedBtnStop)
ON_BN_CLICKED(IDC_BTN_PAUSE, &CMyVideoPlayerDlg::OnBnClickedBtnPause)
END_MESSAGE_MAP()
BOOL CMyVideoPlayerDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置当前窗口图标
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
// 初始化FFmpeg
av_register_all();
avformat_network_init();
// 设置进度条范围
m_slider.SetRange(0, 10000);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CMyVideoPlayerDlg::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 CMyVideoPlayerDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CMyVideoPlayerDlg::OnBnClickedBtnOpen()
{
// 打开文件对话框
CString fileFilter = _T("Video Files|*.mp4;*.avi;*.wmv;*.mov;*.mpeg|All Files|*.*||");
CFileDialog dlg(TRUE, _T("*.mp4"), NULL, OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST, fileFilter, this);
if (dlg.DoModal() == IDOK)
{
CString filePath = dlg.GetPathName();
std::string path = CT2A(filePath);
// 打开视频文件
if (OpenVideo())
{
// 更新时长和进度条
m_slider.SetRange(0, (int)(duration / video_time_base / 1000));
UpdateProgress();
}
}
}
BOOL CMyVideoPlayerDlg::OpenVideo()
{
// 打开视频文件
avformat_open_input(&pFormatCtx, NULL, NULL, NULL);
// 找到视频流
avformat_find_stream_info(pFormatCtx, NULL);
for (int i = 0; i < (int)pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoIndex = i;
break;
}
}
if (videoIndex == -1)
{
AfxMessageBox(_T("No video stream"));
return FALSE;
}
// 打开解码器
pCodecCtx = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoIndex]->codecpar);
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL)
{
AfxMessageBox(_T("Unsupported codec"));
return FALSE;
}
avcodec_open2(pCodecCtx, pCodec, NULL);
// 获取时长和时间基
duration = pFormatCtx->duration;
video_time_base = av_q2d(pFormatCtx->streams[videoIndex]->time_base);
// 分配帧和包
pFrame = av_frame_alloc();
packet = av_packet_alloc();
// 设置定时器
SetTimer(1, 100, NULL);
return TRUE;
}
void CMyVideoPlayerDlg::OnBnClickedBtnPlay()
{
if (pFormatCtx == NULL)
{
AfxMessageBox(_T("Please open a video file"));
return;
}
PlayVideo();
}
void CMyVideoPlayerDlg::PlayVideo()
{
m_isPlaying = TRUE;
m_btnPlay.EnableWindow(FALSE);
m_btnPause.EnableWindow(TRUE);
}
void CMyVideoPlayerDlg::OnTimer(UINT_PTR nIDEvent)
{
// 读取解码后的帧
if (m_isPlaying && av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == videoIndex)
{
avcodec_send_packet(pCodecCtx, packet);
while (avcodec_receive_frame(pCodecCtx, pFrame) == 0)
{
// 渲染帧
// TODO: 这里只是输出帧的时间戳,实际应该使用渲染库渲染帧
int64_t timestamp = pFrame->best_effort_timestamp;
m_current_time = timestamp * video_time_base * 1000;
UpdatePlayTime();
// 判断是否结束
if (m_current_time >= duration)
{
StopVideo();
break;
}
}
}
av_packet_unref(packet);
}
}
void CMyVideoPlayerDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
// 更新播放时间和定位视频
if (pScrollBar == (CScrollBar*)&m_slider && m_isPlaying)
{
m_current_time = m_slider.GetPos() * 1000;
int64_t timestamp = m_current_time / 1000 / video_time_base;
av_seek_frame(pFormatCtx, videoIndex, timestamp, AVSEEK_FLAG_BACKWARD);
UpdatePlayTime();
}
}
void CMyVideoPlayerDlg::OnBnClickedBtnStop()
{
StopVideo();
}
void CMyVideoPlayerDlg::StopVideo()
{
m_isPlaying = FALSE;
m_btnPlay.EnableWindow(TRUE);
m_btnPause.EnableWindow(FALSE);
m_slider.SetPos(0);
m_current_time = 0;
av_seek_frame(pFormatCtx, videoIndex, 0, AVSEEK_FLAG_BACKWARD);
UpdatePlayTime();
}
void CMyVideoPlayerDlg::OnBnClickedBtnPause()
{
PauseVideo();
}
void CMyVideoPlayerDlg::PauseVideo()
{
m_isPlaying = FALSE;
m_btnPlay.EnableWindow(TRUE);
m_btnPause.EnableWindow(FALSE);
}
void CMyVideoPlayerDlg::UpdatePlayTime()
{
CString strTime;
strTime.Format(_T("%02d:%02d:%02d.%03d"), (int)(m_current_time / 3600000), (int)(m_current_time / 60000) % 60, (int)(m_current_time / 1000) % 60, (int)m_current_time % 1000);
m_textTime.SetWindowText(strTime);
}
void CMyVideoPlayerDlg::UpdateProgress()
{
int pos = (int)(m_current_time / video_time_base / 1000);
m_slider.SetPos(pos);
}
// 主函数
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_MYVIDEOPLAYER, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
// 开始消息循环
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, nullptr, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
// 注册主窗口类
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)DefWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYVIDEOPLAYER));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MYVIDEOPLAYER);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
// 创建主窗口
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
CMyVideoPlayerDlg dlg;
dlg.Create(IDD_MYVIDEOPLAYER_DIALOG);
dlg.ShowWindow(SW_SHOW);
UpdateWindow(hWnd);
return TRUE;
}
这个视频播放器有一个UI界面,包括一个进度条、一个“打开”按钮、一个“播放”按钮、一个“停止”按钮、一个“暂停”按钮和一个显示当前播放时间的静态