avformat_open_input异常,fmt_ctx返回空指针?
由于老版本ffmpeg缺少av_register_all();或filePath访问不了
AVFormatContext *fmt_ctx = NULL;
av_register_all();
avformat_open_input(&fmt_ctx, filePath, NULL, NULL);
SDL输出纹理宽高范围
SDL_UpdateTexture(texture, NULL, video_buf, video_width);
// 清除当前显示
SDL_RenderClear(renderer);
// 将纹理的数据拷贝给渲染器
SDL_RenderCopy(renderer, texture, NULL, &rect);//末尾参数设置为NULL,渲染充满窗口
// 显示
SDL_RenderPresent(renderer);
纹理更新完毕后会将纹理的数据拷贝给渲染器,交由渲染器进行显示。
在SDL_RenderCopy()函数中,第三个第四个参数需要注意下。
第三个参数:选择输入纹理的一块矩形区域作为输入,设置为null时整个纹理输入。
第四个参数:选择渲染目标的一块矩形区域作为输出,设置为null时整个渲染目标输出。
我们可以理解为纹理就是原视频文件的图像,渲染目标是播放器中显示的输出图像。
因此,一般原视频文件的图像是要全部读取的,而输出的图像大小还需要取决于当前播放器窗口的大小,因此第四个参数通常是可调整的,而第三个参数一般是null。
SDL_Surface vs SDL_Texture
SDL_Surface,它是按照像素存放图像的。我们一般把真彩色的像素称为RGB24数据。
SDL_Texture 与SDL_Surface相似,也是一种缓冲区。只不过它存放的不是真正的像素数据,而是存放的图像的描述信息。当渲染纹理时,SDL以这些描述信息为数据,底层通过OpenGL、D3D 或 Metal操作GPU,最终绘制出与SDL_Surface一样的图形,且效率更高(因为它是GPU硬件计算的)。
SDL_Window 与 SDL_Render
SDL_Window代表的是窗口的逻辑概念,它是存放在主内存中的一个对象。所以当我们调用SDL API 创建窗口后,它并不会被显示出来。
SDL_Render 是渲染器,它也是主存中的一个对象。对Render操作时实际上分为两个阶段:
一、渲染阶段。SDL_RenderCopy(),用户可以画各种图形渲染到SDL_Surface或SDL_Texture 中;
二、显示阶段。以SDL_Texture为数据,通过OpenGL操作GPU,最终将 SDL_Surfce 或SDL_Texture中的数据输出到显示器上
UI主线程中显示modual窗口后,主线程不再响应定时更新事件消息,导致播放暂停
(1)将定时更新渲染数据放到异步事件子线程,则调整窗口尺寸和显示modual窗口后,能持续渲染:
SDL_SetEventFilter(playerEventFilter, ps);//异步事件处理在子线程执行
(2)如果在主线程定时更新渲染,可以考虑将渲染放到QPaintEvent,没有验证?
将AVFrame进行sws_scale后转QImage,在PaintEvent中显示画面
ffmpeg视频帧数据AVFrame转QImage
QImage img = new QImage(w, h, format);
uint8_t *data[AV_NUM_DATA_POINTERS] = { 0 };
data[0] = (uint8_t *)img->bits();
int linesize[AV_NUM_DATA_POINTERS] = { 0 };
sws_scale(swsContext, frame->data, frame->linesize, 0, vCtx->height, data, linesize);//缩放图片
或者
sws_scale(swsContext, (const uint8_t *const *)pframe->data, pframe->linesize, 0, ps->pixel_h,
out_frame.data, out_frame.linesize);
QImage image = new QImage(static_cast<uchar*>(out_frame.data[0]), w, h, QImage::Format_RGB888);
image = image.copy();
// 创建 AVFrame转QImage最佳方法 https://blog.csdn.net/iMatt/article/details/111602372
QImage img (pFrame->width, pFrame->height, QImage::Format_RGB888);
uint8_t* dst[] = { img.bits() };
int dstStride[4];
// AV_PIX_FMT_RGB24 对应于 QImage::Format_RGB888
av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, pFrame->width);
// 转换
sws_scale(imgConvertCtx, pFrame->data, (const int*)pFrame->linesize,
0, pCodecCtx->height, dst, dstStride);
SDL_CreateWindowFrom+SDL_WaitEvent不响应键盘按键事件SDL_KEYDOWN,使用SDL_SetEventFilter也不响应键盘?
SDL_Window* win = SDL_CreateWindowFrom((void*)ui.labelRenderWnd->winId());
SDL_Event sdl_event;
SDL_WaitEvent(&sdl_event);
int main_quit = 0;
SDL_Event event;
for (;;) {
if (main_quit) {
break;
}
// 等待SDL事件,否则阻塞
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_QUIT: //退出
{
main_quit = 1;
}
break;
case SDL_MOUSEBUTTONDOWN://鼠标点击
{
OutputDebugStringW(L"SDL_MOUSEBUTTONDOWN\n");
break;
}
case SDL_KEYDOWN://键盘按键,无法直接响应,由Qt事件转发
{
if (event.key.keysym.sym == SDLK_ESCAPE) {
main_quit = 1;
}
}
break;
case FF_REFRESH_EVENT: //定时器刷新事件
{
video_refresh_timer(event.user.data1);
}
break;
default:
break;
}
}
解决方法:由Qt窗口的按键事件转发给SDL_Event
#include "windows.h"
#include <sstream>
#include "SDL2/SDL.h"
void VideoPlayDialog::keyPressEvent(QKeyEvent *event)
{
std::wostringstream str_cout;
str_cout << __FUNCTION__ << " key=" << event->text().toStdWString() << std::endl;
std::wstring log = str_cout.str();
OutputDebugStringW(log.c_str());
SDL_Event sdl_event;
sdl_event.type = SDL_KEYDOWN;
sdl_event.key.keysym.sym = event->key();
SDL_PushEvent(&sdl_event);
}
void VideoPlayDialog::keyReleaseEvent(QKeyEvent *event)
{
std::wostringstream str_cout;
str_cout << __FUNCTION__ << " key=" << event->text().toStdWString() << std::endl;
std::wstring log = str_cout.str();
OutputDebugStringW(log.c_str());
SDL_Event sdl_event;
sdl_event.type = SDL_KEYUP;
sdl_event.key.keysym.sym = event->key();
SDL_PushEvent(&sdl_event);
}