音视频-SDL播放BMP
自己造一张YUV格式的图片
jpg源文件
将JPG图片格式转为YUV
PS G:\Resource> ffmpeg -i .\in.jpg -pix_fmt yuv420p .\out_yuv420p.yuv
ffmpeg version 4.3.2 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 10.2.0 (Rev6, Built by MSYS2 project)
configuration: --prefix=/usr/local/ffmpeg --enable-shared --disable-static --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libx264
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
Input #0, image2, from '.\in.jpg':
Duration: 00:00:00.04, start: 0.000000, bitrate: 20505 kb/s
Stream #0:0: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 512x512 [SAR 192:192 DAR 1:1], 25 tbr, 25 tbn, 25 tbc
Stream mapping:
Stream #0:0 -> #0:0 (mjpeg (native) -> rawvideo (native))
Press [q] to stop, [?] for help
[swscaler @ 0000000002b1f000] deprecated pixel format used, make sure you did set range correctly
Output #0, rawvideo, to '.\out_yuv420p.yuv':
Metadata:
encoder : Lavf58.45.100
Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 512x512 [SAR 1:1 DAR 1:1], q=2-31, 78643 kb/s, 25 fps, 25 tbn, 25 tbc
Metadata:
encoder : Lavc58.91.100 rawvideo
frame= 1 fps=0.0 q=-0.0 Lsize= 384kB time=00:00:00.04 bitrate=78643.2kbits/s speed=2.11x
video:384kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%
主要信息 yuv420p, 512x512
使用FFmpeg播放YUV格式文件
ffplay -i .\out_yuv420p.yuv -pix_fmt yuv420p -s 512x512
ps : 终端拉长了,为了截图,正常输出是512x512
YUV数据有了, 使用SDL播放YUV
SDL播放YUV和 播放BMP思路差不多,差别在于传入的文件格式内容, 纹理的处理不同
/**
* \brief Create a texture for a rendering context.
*
* \param renderer The renderer.
* \param format The format of the texture.
* \param access One of the enumerated values in ::SDL_TextureAccess.
* \param w The width of the texture in pixels.
* \param h The height of the texture in pixels.
*
* \return The created texture is returned, or NULL if no rendering context was
* active, the format was unsupported, or the width or height were out
* of range.
*
* \note The contents of the texture are not defined at creation.
*
* \sa SDL_QueryTexture()
* \sa SDL_UpdateTexture()
* \sa SDL_DestroyTexture()
*/
extern DECLSPEC SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
Uint32 format,
int access, int w,
int h);
关于SDL_TextureAccess
/**
* \brief The access pattern allowed for a texture.
*/
typedef enum
{
SDL_TEXTUREACCESS_STATIC, /**< Changes rarely, not lockable */
SDL_TEXTUREACCESS_STREAMING, /**< Changes frequently, lockable */
SDL_TEXTUREACCESS_TARGET /**< Texture can be used as a render target */
} SDL_TextureAccess;
关于SDL_PixelFormatEnum
/* Note: If you modify this list, update SDL_GetPixelFormatName() */
typedef enum
{
SDL_PIXELFORMAT_UNKNOWN,
SDL_PIXELFORMAT_INDEX1LSB =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_4321, 0,
1, 0),
SDL_PIXELFORMAT_INDEX1MSB =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_1234, 0,
1, 0),
SDL_PIXELFORMAT_INDEX4LSB =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_4321, 0,
4, 0),
SDL_PIXELFORMAT_INDEX4MSB =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_1234, 0,
4, 0),
SDL_PIXELFORMAT_INDEX8 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1),
SDL_PIXELFORMAT_RGB332 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED8, SDL_PACKEDORDER_XRGB,
SDL_PACKEDLAYOUT_332, 8, 1),
SDL_PIXELFORMAT_XRGB4444 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
SDL_PACKEDLAYOUT_4444, 12, 2),
SDL_PIXELFORMAT_RGB444 = SDL_PIXELFORMAT_XRGB4444,
SDL_PIXELFORMAT_XBGR4444 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,
SDL_PACKEDLAYOUT_4444, 12, 2),
SDL_PIXELFORMAT_BGR444 = SDL_PIXELFORMAT_XBGR4444,
SDL_PIXELFORMAT_XRGB1555 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
SDL_PACKEDLAYOUT_1555, 15, 2),
SDL_PIXELFORMAT_RGB555 = SDL_PIXELFORMAT_XRGB1555,
SDL_PIXELFORMAT_XBGR1555 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,
SDL_PACKEDLAYOUT_1555, 15, 2),
SDL_PIXELFORMAT_BGR555 = SDL_PIXELFORMAT_XBGR1555,
SDL_PIXELFORMAT_ARGB4444 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,
SDL_PACKEDLAYOUT_4444, 16, 2),
SDL_PIXELFORMAT_RGBA4444 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,
SDL_PACKEDLAYOUT_4444, 16, 2),
SDL_PIXELFORMAT_ABGR4444 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,
SDL_PACKEDLAYOUT_4444, 16, 2),
SDL_PIXELFORMAT_BGRA4444 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,
SDL_PACKEDLAYOUT_4444, 16, 2),
SDL_PIXELFORMAT_ARGB1555 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,
SDL_PACKEDLAYOUT_1555, 16, 2),
SDL_PIXELFORMAT_RGBA5551 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,
SDL_PACKEDLAYOUT_5551, 16, 2),
SDL_PIXELFORMAT_ABGR1555 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,
SDL_PACKEDLAYOUT_1555, 16, 2),
SDL_PIXELFORMAT_BGRA5551 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,
SDL_PACKEDLAYOUT_5551, 16, 2),
SDL_PIXELFORMAT_RGB565 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
SDL_PACKEDLAYOUT_565, 16, 2),
SDL_PIXELFORMAT_BGR565 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,
SDL_PACKEDLAYOUT_565, 16, 2),
SDL_PIXELFORMAT_RGB24 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_RGB, 0,
24, 3),
SDL_PIXELFORMAT_BGR24 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_BGR, 0,
24, 3),
SDL_PIXELFORMAT_XRGB8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB,
SDL_PACKEDLAYOUT_8888, 24, 4),
SDL_PIXELFORMAT_RGB888 = SDL_PIXELFORMAT_XRGB8888,
SDL_PIXELFORMAT_RGBX8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBX,
SDL_PACKEDLAYOUT_8888, 24, 4),
SDL_PIXELFORMAT_XBGR8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XBGR,
SDL_PACKEDLAYOUT_8888, 24, 4),
SDL_PIXELFORMAT_BGR888 = SDL_PIXELFORMAT_XBGR8888,
SDL_PIXELFORMAT_BGRX8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRX,
SDL_PACKEDLAYOUT_8888, 24, 4),
SDL_PIXELFORMAT_ARGB8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
SDL_PACKEDLAYOUT_8888, 32, 4),
SDL_PIXELFORMAT_RGBA8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA,
SDL_PACKEDLAYOUT_8888, 32, 4),
SDL_PIXELFORMAT_ABGR8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR,
SDL_PACKEDLAYOUT_8888, 32, 4),
SDL_PIXELFORMAT_BGRA8888 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRA,
SDL_PACKEDLAYOUT_8888, 32, 4),
SDL_PIXELFORMAT_ARGB2101010 =
SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
SDL_PACKEDLAYOUT_2101010, 32, 4),
/* Aliases for RGBA byte arrays of color data, for the current platform */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_RGBA8888,
SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_ARGB8888,
SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_BGRA8888,
SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_ABGR8888,
#else
SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_ABGR8888,
SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_BGRA8888,
SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_ARGB8888,
SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_RGBA8888,
#endif
SDL_PIXELFORMAT_YV12 = /**< Planar mode: Y + V + U (3 planes) */
SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'),
SDL_PIXELFORMAT_IYUV = /**< Planar mode: Y + U + V (3 planes) */
SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'),
SDL_PIXELFORMAT_YUY2 = /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'),
SDL_PIXELFORMAT_UYVY = /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'),
SDL_PIXELFORMAT_YVYU = /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */
SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U'),
SDL_PIXELFORMAT_NV12 = /**< Planar mode: Y + U/V interleaved (2 planes) */
SDL_DEFINE_PIXELFOURCC('N', 'V', '1', '2'),
SDL_PIXELFORMAT_NV21 = /**< Planar mode: Y + V/U interleaved (2 planes) */
SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1'),
SDL_PIXELFORMAT_EXTERNAL_OES = /**< Android video texture format */
SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ')
} SDL_PixelFormatEnum;
刚刚转出来的是yuv420p的格式。 用的是IYUV
跟播放BMP的不一样的地方是纹理的加载方式, 因为目前读取的是源文件, 所以用QFile打开文件,读取文件数据, 更新到render中
// 贴图纹理
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, IMG_W, IMG_H);
END(!texture, SDL_CreateTextureFromSurface);
// YUV420p ==> yyyyuu
qDebug() << "texture" << texture;
// 打开文件
openRet = infile.open(QFile::ReadOnly);
END(!openRet, infile.open);
qDebug() << "openRet" << openRet;
// 将YUV数据更新到render中
renderUpdateRet = SDL_UpdateTexture(texture, nullptr, infile.readAll().data(), IMG_W);
END(renderUpdateRet, SDL_RenderCopy);
// 拷贝纹理数据到渲染目标(默认是window)
renderCopyRet = SDL_RenderCopy(renderer, texture, nullptr, nullptr);
END(renderCopyRet, SDL_RenderCopy);
实现
#include "playthread.h"
#include
#include
extern "C" {
#include
}
#define FILENAME "G:/Resource/out_yuv420p.yuv"
#define IMG_W 512
#define IMG_H 512
// 出错了就执行goto end
#define END(judge, func) \
if (judge) { \
qDebug() << #func << "Error" << SDL_GetError(); \
goto end; \
}
Playthread::Playthread(QObject *parent) : QThread(parent) {
// 创建信号连接
connect(this, &Playthread::finished,
this, &Playthread::deleteLater);
}
Playthread::~Playthread() {
// 断开所有的连接
disconnect();
// 内存回收之前,正常结束线程
requestInterruption();
// 安全退出
quit();
wait();
qDebug() << this << "Playthread 析构(内存被回收)";
}
void Playthread::run() {
if(SDL_Init(SDL_INIT_VIDEO)) {
qDebug() << "SDL_Init error : " << SDL_GetError();
return;
}
// 创建一个SDL Window窗口
SDL_Window *window = nullptr;
// 像素数据
SDL_Surface *surface = nullptr;
// 纹理(直接跟特定驱动程序相关的像素数据)
SDL_Texture *texture = nullptr;
// 渲染上下文
SDL_Renderer *renderer = nullptr;
int renderUpdateRet;
int renderCopyRet;
int openRet;
//YUV文件输入
QFile infile(FILENAME);
window = SDL_CreateWindow("SDL播放BMP",
100, 100,
600, 600,
SDL_WINDOW_SHOWN);
END(!window, SDL_CreateWindow);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) { // 说明开启硬件加速失败
renderer = SDL_CreateRenderer(window, -1, 0);
}
END(!renderer, SDL_CreateRenderer);
// 贴图纹理
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, IMG_W, IMG_H);
END(!texture, SDL_CreateTextureFromSurface);
// YUV420p ==> yyyyuu
qDebug() << "texture" << texture;
// 打开文件
openRet = infile.open(QFile::ReadOnly);
END(!openRet, infile.open);
qDebug() << "openRet" << openRet;
// 将YUV数据更新到render中
renderUpdateRet = SDL_UpdateTexture(texture, nullptr, infile.readAll().data(), IMG_W);
END(renderUpdateRet, SDL_RenderCopy);
// 拷贝纹理数据到渲染目标(默认是window)
renderCopyRet = SDL_RenderCopy(renderer, texture, nullptr, nullptr);
END(renderCopyRet, SDL_RenderCopy);
// 将此前的所有需要渲染的内容更新到屏幕上
SDL_RenderPresent(renderer);
// 延迟3秒退出
SDL_Delay(3000);
end:
SDL_DestroyWindow(window);
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
// 清除所有子系统
SDL_Quit();
}
void Playthread::setStop(bool stop) {
_stop = stop;
}
播放YUV视频
和播放图片差不多,核心处理的代码 , 将一帧的数据渲染到屏幕上,现在这里是播放yuv,输入的帧率,1s多少帧的填充刷新,我们现在是读取文件的内容传入
下载一个自己的YUV数据流, 或者自己录制一个
下载地址 :http://trace.eas.asu.edu/yuv/index.html
视频内容 : Big Buck Bunny
像素格式 :yuv420p
分辨率 :352X288
帧率 :24
核心代码
// 打开文件
openRet = infile.open(QFile::ReadOnly);
END(!openRet, infile.open);
qDebug() << "openRet" << openRet;
while(infile.read(data, imgSize) > 0) {
// 将YUV数据更新到render中
renderUpdateRet = SDL_UpdateTexture(texture, nullptr, data, IMG_W);
END(renderUpdateRet, SDL_RenderCopy);
// 拷贝纹理数据到渲染目标(默认是window)
renderCopyRet = SDL_RenderCopy(renderer, texture, nullptr, nullptr);
END(renderCopyRet, SDL_RenderCopy);
// 将此前的所有需要渲染的内容更新到屏幕上
SDL_RenderPresent(renderer);
}