C# 实现SDL2进行视频播放窗口截图和字幕添加

使用SDL2进行视频播放窗口截图和字幕添加操作

SDL API查看:https://wiki.libsdl.org/APIByCategory

视频截图

我就废话不多说了,大家还是直接看代码吧~

 /// <summary>
 /// SDL2截图操作类
 /// </summary>
 public unsafe class SDLScreenshot
 {
  IntPtr window;// 窗口对象
  IntPtr renderer;// 播放窗口的渲染器(来自于已初始化的播放窗口渲染器)
  public SDLScreenshot(IntPtr window, IntPtr renderer)
  {
   this.window = window;
   this.renderer = renderer;
  }
  /// <summary>
  /// 保存截图
  /// </summary>
  /// <param name="width"></param>
  /// <param name="height"></param>
  /// <param name="path"></param>
  public void SaveBMP(int width, int height,string path)
  {
   // 判断渲染器是否初始化
   if (renderer == IntPtr.Zero)
   {
    Console.WriteLine("renderer is null ,please call Init() method.");
    return;
   }
   uint Rmask=0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0x00000000;
   // 获取图像数据
   SDL.SDL_Surface* surface= (SDL.SDL_Surface*)SDL.SDL_CreateRGBSurface(0, width, height, 32, Rmask, Gmask, Bmask, Amask);
   //设置纹理的数据
   SDL.SDL_Rect destrect;
   destrect.x = 0;
   destrect.y = 0;
   destrect.w = width;
   destrect.h = height; 

   // 读取并渲染图像数据
   SDL.SDL_RenderReadPixels(renderer, ref destrect, SDL.SDL_PIXELFORMAT_ARGB8888, surface->pixels, surface->pitch); 

   //保存图片
   int i = SDL.SDL_SaveBMP((IntPtr)surface, path);
   if (i != 0)
   {
    Console.WriteLine("screenshot failed." + SDL.SDL_GetError());
   } 

   SDL.SDL_FreeSurface((IntPtr)surface);
   //SDL.SDL_RenderClear(renderer);
   //SDL.SDL_DestroyRenderer(renderer);
  } 

  /// <summary>
  /// 加载截图
  /// </summary>
  /// <param name="width"></param>
  /// <param name="height"></param>
  /// <param name="path"></param>
  public void LoadBMP(int width, int height, string path)
  {
   // 判断渲染器是否初始化
   if (renderer == IntPtr.Zero)
   {
    Console.WriteLine("renderer is null ,please call Init() method.");
    return;
   }
   // 加载图片
   IntPtr surface = SDL.SDL_LoadBMP(path);
   if (surface == IntPtr.Zero)
   {
    Console.WriteLine("load bmp failed." + SDL.SDL_GetError());
    return;
   }
   IntPtr texture = SDL.SDL_CreateTextureFromSurface(renderer, surface);
   if (texture == IntPtr.Zero)
   {
    Console.WriteLine("create texture failed." + SDL.SDL_GetError());
    return;
   }
   SDL.SDL_FreeSurface(surface); 

   //设置纹理的数据
   SDL.SDL_Rect destrect;
   destrect.x = 0;
   destrect.y = 0;
   destrect.w = width;
   destrect.h = height; 

   SDL.SDL_Rect srcrect = destrect; 

   //SDL.SDL_RenderClear(renderer);
   SDL.SDL_RenderCopy(renderer, texture, ref srcrect, ref destrect);
   SDL.SDL_RenderPresent(renderer);

   //SDL.SDL_Delay(20);
   SDL.SDL_DestroyTexture(texture);
   //SDL.SDL_DestroyRenderer(renderer);
   //SDL.SDL_DestroyWindow(screen);
   //Quit SDL
   //SDL.SDL_Quit();
  }
 }

播放测试代码:

 if (isSaveScreenshot)
       {
        SDLScreenshot screenshot = new SDLScreenshot(sdlVideo.window, sdlVideo.sdlrenderer);
        screenshot.SaveBMP(nvVideoframe.VideoFrame->width, nvVideoframe.VideoFrame->height, "screenshot.bmp");
        isSaveScreenshot = false;
     }

测试效果图:

注:此处截图是直接获取的播放窗口的图像像素来实现的。

视频字幕

 /// <summary>
 /// SDL2字幕显示类
 /// </summary>
 public unsafe class SDLTTF
 {
  IntPtr renderer;// 播放窗口的渲染器(来自于已初始化的播放窗口渲染器)

  public SDLTTF(IntPtr renderer)
	{
   this.renderer = renderer;
  }

  /// <summary>
  /// 展示字幕文字
  /// </summary>
  /// <param name="text"></param>
  public void ShowText(string ttfPath, int fontSize,string text)
  {
   // 初始化 ttf
   if (SDL_ttf.TTF_Init() < 0)
   {
    Console.WriteLine("SDL_ttf.TTF_Init() failed.");
    return;
   }
   // 是否初始化完成
   int was_init = SDL_ttf.TTF_WasInit();

   if (was_init == 1)
    // SDL_ttf was already initialized
    Console.WriteLine("SDL_ttf was already initialized");
   else if (was_init == 0)
    // SDL_ttf was not already initialized
    Console.WriteLine("SDL_ttf was not already initialized");

   // 判断是否初始化
   if (renderer == IntPtr.Zero)
   {
    Console.WriteLine("Not initialized by SDL_ttf.TTF_Init() ,please call Init() method.");
    return;
   }

   //如:打开ttfPath=simfang.ttf 字库,设字体为fontSize=20号
   IntPtr font = SDL_ttf.TTF_OpenFont(ttfPath, fontSize);
   if (font == IntPtr.Zero)
   {
    Console.WriteLine("open font failed." + SDL.SDL_GetError());
    return;
   }

   // 设置文字颜色
   SDL.SDL_Color color;
   color.a = 255;
   color.r = 255;
   color.g = 255;
   color.b = 255;

   // 渲染文字效果
   //IntPtr surface = SDL_ttf.TTF_RenderUTF8_Blended(font, text, color);
   IntPtr surface = SDL_ttf.TTF_RenderUNICODE_Blended(font, text, color);
   if (surface == IntPtr.Zero)
   {
    Console.WriteLine("show surface failed." + SDL.SDL_GetError());
   }
   IntPtr texture = SDL.SDL_CreateTextureFromSurface(renderer, surface);
   if (texture == IntPtr.Zero)
   {
    Console.WriteLine("create texture failed." + SDL.SDL_GetError());
   }
   SDL.SDL_FreeSurface(surface);
   // 关闭字体
   SDL_ttf.TTF_CloseFont(font);

   // 停止显示
   SDL_ttf.TTF_Quit();

   //设置纹理的数据
   SDL.SDL_Rect destrect;
   destrect.x = 0;
   destrect.y = 0;
   destrect.w = text.Length * 20;
   destrect.h = 20;

   SDL.SDL_Rect srcrect = destrect;
   SDL.SDL_RenderClear(renderer);
   SDL.SDL_RenderCopy(renderer, texture, ref srcrect, ref destrect);
   SDL.SDL_RenderPresent(renderer);
   SDL.SDL_DestroyTexture(texture);
   SDL.SDL_DestroyRenderer(renderer);
  }
 }

事件测试字幕添加:

需要的引用库下载:https://www.libsdl.org/projects/SDL_ttf/

 /// <summary>
  /// 字幕叠加****需要添加三个dll库:SDL2_ttf.dll 、libfreetype-6.dll 、zlib1.dll
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void mbtnAddFontText_Click(object sender, EventArgs e)
  {
   Console.WriteLine("叠加字幕...............");

   sdlTTF = new SDLTTF(sdlVideo.sdlrenderer);
   // 中英文都需要兼容
   string text = "Hello 世界!";
   // 设置一个字体库并设置字体大小和显示文字内容
   sdlTTF.ShowText("simkai.ttf",12, text);
  }

测试效果图:

如果是播放过程中显示字幕一定要在视频渲染完成后渲染字幕,如下面工具类的方法:

/// <summary>
  /// 播放视频
  /// </summary>
  /// <param name="width"></param>
  /// <param name="height"></param>
  /// <param name="pixels"></param>
  /// <param name="pixelsSize"></param>
  /// <param name="pitch"></param>
  /// <returns></returns>
  public int SDL_Display(int width, int height, IntPtr pixels, int pixelsSize,
   int pitch)
  {
   lock (this)
   {
    while (isPause)
    {
     SDL.SDL_Delay(20);//延迟播放
    }

    #region SDL 视频数据渲染播放
    //设置纹理的数据
    sdlrect.x = 0;
    sdlrect.y = 0;
    sdlrect.w = width;
    sdlrect.h = height;
    SDL.SDL_UpdateTexture(sdltexture, ref sdlrect, pixels, pitch);
    //SDL.SDL_UpdateTexture(sdltexture, IntPtr.Zero, pixels, pitch);//此处代码导致播放窗口绿色阴影
    //复制纹理信息到渲染器目标
    SDL.SDL_RenderClear(sdltexture);
    //SDL.SDL_Rect srcRect = sdlrect;
    //SDL.SDL_RenderCopy(sdlrenderer, sdltexture, ref srcRect, ref sdlrect);

    SDL.SDL_RenderCopy(sdlrenderer, sdltexture, IntPtr.Zero, IntPtr.Zero);
    //字幕渲染显示-特别提醒:此处必须放置于视频渲染之后,否则字幕不会显示
    if (ttfText!=null&&!ttfText.Equals(""))
    {
     RenderToShowTTF(ttfText);
    }
    //else
    //{
    // RenderToShowTTF( "未设置字幕内容");
    //}
    //视频渲染显示
    SDL.SDL_RenderPresent(sdlrenderer);
    //SDL.SDL_Delay(40);
    //SDL.SDL_PollEvent(out sdlevent);
    //switch (sdlevent.type)
    //{
    // case SDL.SDL_EventType.SDL_QUIT:
    //  SDL.SDL_Quit();
    //  return -1;
    // default:
    //  break;
    //}
    return 0;
   } 

   //SDL.SDL_RenderClear(sdlrenderer);
   //SDL.SDL_RenderCopy(sdlrenderer, sdltexture, ref srcRect, ref sdlrect);
   //SDL.SDL_RenderPresent(sdlrenderer);
   Delay 40ms
   //SDL.SDL_Delay(40);
   #endregion 

   //#region SDL 视频数据渲染播放
   //设置纹理的数据
   sdlrect.x = 0;
   sdlrect.y = 0;
   sdlrect.w = width;
   sdlrect.h = height;
   SDL.SDL_UpdateTexture(sdltexture, ref sdlrect, pixels, pitch);
   //复制纹理信息到渲染器目标
   SDL.SDL_Rect srcRect = sdlrect;
   SDL.SDL_RenderCopy(sdlrenderer, sdltexture, ref srcRect, ref sdlrect);
   //视频渲染显示
   SDL.SDL_RenderPresent(sdlrenderer);
   //SDL.SDL_Delay(40);
   SDL.SDL_PollEvent(out sdlevent);
   switch (sdlevent.type)
   {
    case SDL.SDL_EventType.SDL_QUIT:
     SDL.SDL_Quit();
     return -1;
    default:
     break;
   }
   return 0;
   //#endregion
  }

  /// <summary>
  /// 设置字幕显示内容
  /// </summary>
  /// <param name="ttfPath"></param>
  /// <param name="fontSize"></param>
  public void SDL_TTF_TEXT(string ttfPath, string text, int fontSize)
  {
   this.ttfPath = ttfPath;
   this.ttfText = text;
   this.ttfFontSize = fontSize;
  }

  /// <summary>
  /// 渲染字幕
  /// </summary>
  /// <param name="text"></param>
  private void RenderToShowTTF(string text)
  {
   // 初始化 ttf
   if (SDL_ttf.TTF_Init() < 0)
   {
    Console.WriteLine("SDL_ttf.TTF_Init() failed.");
    return;
   }
   // 是否初始化完成
   int was_init = SDL_ttf.TTF_WasInit();

   if (was_init == 1)
    // SDL_ttf was already initialized
    Console.WriteLine("SDL_ttf was already initialized");
   else if (was_init == 0)
    // SDL_ttf was not already initialized
    Console.WriteLine("SDL_ttf was not already initialized");

   //如:打开ttfPath=simfang.ttf 字库,设字体为fontSize=20号
   IntPtr font = SDL_ttf.TTF_OpenFont(ttfPath, ttfFontSize);
   if (font == IntPtr.Zero)
   {
    Console.WriteLine("open font failed." + SDL.SDL_GetError());
    return;
   }

   // 设置文字字体
   SDL_ttf.TTF_SetFontStyle(font, SDL_ttf.TTF_STYLE_BOLD);

   // 设置文字颜色
   SDL.SDL_Color color;
   color.a = 255;
   color.r = 255;
   color.g = 255;
   color.b = 255;

   // 渲染文字效果
   //IntPtr surface = SDL_ttf.TTF_RenderText_Blended(font, text, color);
   IntPtr surface = SDL_ttf.TTF_RenderUTF8_Blended(font, text, color);
   //IntPtr surface = SDL_ttf.TTF_RenderUNICODE_Blended(font, text, color);
   if (surface == IntPtr.Zero)
   {
    Console.WriteLine("show surface failed." + SDL.SDL_GetError());
   }
   IntPtr texture = SDL.SDL_CreateTextureFromSurface(sdlrenderer, surface);
   if (texture == IntPtr.Zero)
   {
    Console.WriteLine("create texture failed." + SDL.SDL_GetError());
   }

   SDL.SDL_FreeSurface(surface);
   // 关闭字体
   SDL_ttf.TTF_CloseFont(font);

   // 停止显示
   SDL_ttf.TTF_Quit();

   // 计算合适的宽度和高度
   int texWidth = 0;
   int texHeight = 0;
   uint format = 0;
   int access = 0;
   // 下面这行代码解决字体虚浮不清问题
   SDL.SDL_QueryTexture(texture, out format, out access, out texWidth, out texHeight);
   //设置纹理的数据
   SDL.SDL_Rect destrect;
   destrect.x = 0;
   destrect.y = 0;
   destrect.w = texWidth;
   destrect.h = texHeight;

   SDL.SDL_Rect srcrect = destrect;
   SDL.SDL_RenderCopy(sdlrenderer, texture, ref srcrect, ref destrect);
  }

效果就会好很多:

请看这里的“中华人民共和国”

注意:

常用中英文ttf字体包中包含了:times new roman,中山行书百年纪念版,calibri,Christopherhand,DejaVuSansMono,方正兰亭黑,James Fajardo,Monaco,微软雅黑,仿宋,黑体,楷体,宋体,yahei_mono,仿宋_GB2312,楷体_GB2312,迷你简行楷碑等。

本文使用的是simkai.ttf。

下面是部分字体文件名:

bb1550.ttf

calibri.ttf

calibrib.ttf

calibrii.ttf

calibriz.ttf

comesinhandy.ttf

DejaVuSansMono-Bold.ttf

DejaVuSansMono-BoldOblique.ttf

DejaVuSansMono-Oblique.ttf

DejaVuSansMono.ttf

DroidSansFallback.ttf

James_Fajardo.ttf

Monaco.ttf

msyh.ttf

msyhbd.ttf

simfang.ttf

simhei.ttf

simkai.ttf

simsun.ttc

times.ttf

timesbd.ttf

timesbi.ttf

timesi.ttf

yahei_mono.ttf

如果懒得下载,Windows里面有字体,在C:\Windows\Fonts目录下。

以上这篇C# 实现SDL2进行视频播放窗口截图和字幕添加就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

时间: 2020-12-05

C#窗体程序实现全屏及取消全屏步骤

由于项目需要,需要用vs窗体程序实现播放视频的窗口的全屏和取消全屏. 具体实现界面如图: 这是初始状态,视频框的右上角就是控制全屏的按钮 这是全屏后的状态,此时全屏按钮变成了取消全屏的样式 注:为了界面的美观我的全屏并没有把左边的那些控件也盖住,但是是可以设置的,下边代码部分我会进行讲解. 1.首先说明一下我所用的控件及我的项目中控件的名称,以便大家理解. 显示视频的黑框是一个picturebox即代码中的VideoPlayWnd,全屏/取消全屏是一个button即代码中的button4 2.具

C# 视频播放类

复制代码 代码如下: using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; ///

java创建简易视频播放器

最近有个多媒体的作业,要求使用visualC++和OpenCV编写一个简易的视频播放器,对于C/C++残疾者而言是不可能的,于是萌生了用Java编写的想法.具体经验分享一下. 目标:制作简易视频播放器 开发工具:eclipse4.5.1:VLC2.2.1 具体内容:完成了视频的加载.播放.退出的功能:实现了视频播放过程中控制播放进程:实现播放过程中控制暂停. 最终程序效果图如下: 开发过程参考学习资源: 极客学院视屏教程 Java framework for the vlc media play

Python应用03 使用PyQT制作视频播放器实例

最近研究了Python的两个GUI包,Tkinter和PyQT.这两个GUI包的底层分别是Tcl/Tk和QT.相比之下,我觉得PyQT使用起来更加方便,功能也相对丰富.这一篇用PyQT实现一个视频播放器,并借此来说明PyQT的基本用法.  视频播放器 先把已经完成的代码放出来.代码基于Python 3.5: import time import sys from PyQt4 import QtGui, QtCore from PyQt4.phonon import Phonon class Po

Android编程视频播放API之MediaPlayer用法示例

本文实例讲述了Android编程视频播放API之MediaPlayer用法.分享给大家供大家参考,具体如下: 现在项目要做音视频这块功能,这正是我相对比较薄弱的地方,项目中要用到,赶紧抓紧时间学习.前面有一编介绍不错的文章http://www.jb51.net/article/121881.htm,值得学习. 这里给出一个较为简单的示例: /** * Android API MediaPlayer示例 * * @description: * @author ldm * @date 2016-4-

android多媒体类VideoView使用方法详解

一.概述 VideoView类将视频的显示和控制集于一身,我们可以借助它完成一个简易的视频播放器.VideoView和MediaPlayer也比较相似. 二.VideoView的使用方法 它主要有以下几种常用方法 步骤: 1.指定视频文件的路径, 2.接下来调用start()方法就可以开始播放视频,pause()方法就会暂停播放,resume()方法就会重新播放 注:获取视频文件也需要运行时权限,所有相关逻辑也需要写.       最后不要忘记在AndroidManifest.xml文件中声明用

教你轻松制作java视频播放器

前言 跳过废话,直接看正文 当年入坑Java是因为它的跨平台优势.那时我认为,"编写一次,处处运行."这听上去多么牛逼,应该是所有语言发展的终极之道,java势必会一统天下. 然而事实证明,那时的我还是太年轻. 正所谓鱼和熊掌不可兼得,若要享受跨平台带来的方便,便不可避免地要接受性能上的不足.事实上,java一直在致力于提高虚拟机的性能(JIT等技术),但面对对实时计算性能要求很高或涉及到用硬件优化的任务(视频的硬件编码.解码)时,仍远远比不上c或c++.因此,很少能够看到有人用jav

Android多媒体之VideoView视频播放器

本文实例为大家分享了视频播放器的两种方式,供大家参考,具体内容如下 1).SurfaceView 在布局文件中 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/too

Android View类与SurfaceView类详解

Android游戏开发中主要的类除了控制类就是显示类,比较重要也很复杂的就是显示和游戏逻辑的处理.在J2ME中可以通过Display和Canvas来实现显示,而Android中处理显示的是View类.下面为大家简单介绍android.view.View和android.view.SurfaceView. SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView,

详解iOS视频播放方式

多媒体这整个系列的文章自己也准备好开始整理了,先从视频音频最简单也是最常用的播放出发慢慢的往下深究,探索到底层的编码解码等等,这篇文章就从视频的播放这个最简单的说起. iOS的视频播放方式有几种?其实要是只是简单的想播放一段视频并且对UI没什么要求的话的确比较简单,很容易搞定,但我相信这种情况除了你的Demo一般是不会出现的,对播放UI的定义以及可能有各种不同的需求对应着你是不能随便写个播放器就没事了的. 最原始的播放 要不是刚接触iOS开发的同学应该是知道MediaPlayer这个框架的,要是

Opencv实现视频播放与进度控制

视频画面本质上是由一帧一帧的连续图像组成的,播放视频其实就是在播放窗口把一系列连续图像按一定的时间间隔一幅幅贴上去实现的. 人眼在连续图像的刷新最少达到每秒24帧的时候,就分辨不出来图像间的闪动了,使人感觉呈现出来的是连续的画面,视频的播放就是利用了这一点.我们知道,电影被称为"24帧的艺术",意思就是电影画面每秒刷新24帧,即每秒显示24副图像,它的帧率(Frames per Second,简称:FPS)为24. 一般情况下帧率要大于等于24,以下先测试一下两个视频文件的帧率,一个是