Video playback library based on ffmpeg and SDL

Due to work needs, the self-encapsulated ffmpeg-based video codec library shows that the SDL library is used. Can play local files or network streams, support multi-port playback, support text overlay, screenshots, video recording, etc.

Header file code:

#pragma once
#ifdef __DLLEXPORT
#define __DLL_EXP _declspec(dllexport)
#else
#define __DLL_EXP _declspec(dllimport)
#endif

#include <stdio.h>
#include <string>

using namespace std;
//recording status
enum RecordStatus
{
Idle,
Recording,
stop
};

//Video storage format
enum VideoFormat
{
MP4,
AVI,
};

//Image storage format
enum PictureFormat
{
JPEG,
PNG,
BMP,
};

//player state
enum PlayStatus
{
Close,
Playing,
Pause
};

// filter type
enum FilterType
{
Time,
Reticle,
Text,
other
};

//color
struct RGBAColor
{
BYTE R;
BYTE G;
BYTE B;
BYTE A;
};

//OSD font attribute structure
 struct OSDParameter
{
char* text;
int fontSize; //Chinese character font size
int fontFamily; //0=Black, 1=Italic, 2=Arial 3:Arial
RGBAColor fontColor; // font color rgb representation
RGBAColor backColor; // font background color rgb representation
float x; //font coordinates x: width percentage 0-1
float y; //font coordinates y: height percentage 0-1
bool visible; //Is it visible
};

 //Format
 enum FrameFormat
 {
AUDIO16,
RGB32,
YV12,
UVVY,
YUVJ420P
 };

 struct FrameInfo
{
int width; //screen width, unit pixel. 0 if it is audio data;
int height; //screen height. 0 if it is audio data;
int stamp; //time stamp information, in milliseconds.
FrameFormat format; //data type, 0: AUDIO16, 1: RGB32, 2: (YUV420P) YV12, 3: YUVJ420P
int frameRate; //Real-time frame rate.
};

/**
* Function description: open video
* @param nPort: playback channel number [0,31]
* @param url: URL of video file or video stream
* @param data: Image control handle, when recording in the background, assign NULL
*
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_OpenVideo(int nPort, const char* url, const void* hWnd);

/**
* Function description: start playing
* @param nPort: playback channel number [0,31]
* @param isRealTimePlay: Real-time playback, open when real-time playback is required, such as playing camera streams. When false, it will play at the frame rate.
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_StartPlay(int nPort, bool isRealTimePlay);

/**
* Function description: Pause playback
* @param nPort: playback channel number [0,31]
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_PausePlay(int nPort);


/**
* Function description: close the video to release resources
* @param nPort: playback channel number [0,31]
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_CloseVideo(int nPort);

/**
* Function description: set the relative position of the file play pointer (percentage)
* @param nPort: playback channel number [0,31]
* @param pos: playback time position 0-100
* @param fReleatePos: 0-100
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetPlayPos(int nPort, float fReleatePos);

/**
* Function description: playback speed control
* @param nPort: playback channel number [0,31]
* @param speed: speed 0: normal playback 1: 1.25x speed 2: 1.5x speed 3: 1.75x speed 4: 2x speed 5: 0.75x speed 6: 0.5x speed 7: 0.25x speed
*/
extern "C" __declspec(dllexport) void _stdcall VideoPlayer_ChangeSpeed(int nPort, int speed);

/**
* Function description: Get the current playing time
* @param nPort: playback channel number [0,31]
* @return playback time unit: second
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_GetPlayedTime(int nPort);

/**
* Function description: Play window zoom
* @param nPort: playback channel number [0,31]
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_ResizeWindow(int nPort);

/**
* Function description: set recording parameters
* @param nPort: playback channel number [0,31]
* @param fps: video frame rate
* @param quality: Image quality (0-10) The higher the quality, the better the memory usage. When set to 0, it is the automatic code rate
* @param duration: the duration of a single file (min), the default is 60 minutes
* @param frameExtractionMode: Frame extraction mode 0: No frame extraction, encoding in fps; 1: 1/2 frame rate, frame extraction in fps/2 (guarantee fps/2 is an integer, otherwise frame loss will occur)
* @param format: storage format, the default is mp4
*
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetRecordParameter(int nPort,int fps, float quality = 5, int duration = 60, int frameExtractionMode = 0, VideoFormat format= VideoFormat::MP4);

/**
* Function description: start recording
* @param nPort: playback channel number [0,31]
* @param path: The video save path contains the file name
*
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_StartRecord(int nPort, const char* path);

/**
* Function description: end recording
* @param nPort: playback channel number [0,31]
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_StopRecord(int nPort);

/**
* Function description: video screenshot
* @param nPort: playback channel number [0,31]
* @param path: The path where the image is saved, without the suffix name
* @param format: image format, the default is jpeg
*
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_ShotImage(int nPort, const char* path, PictureFormat format= PictureFormat::JPEG);


/**
* Function description: set the OSD superimposed text of a certain line
* @param nPort: playback channel number [0,31]
* @param osdID: OSD text serial number
* @param para: osd attribute
*
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetOSDText(int nPort, int osdID, OSDParameter osdParam);

/**
* Function description: set all OSD texts (20 lines in total)
* @param nPort: playback channel number [0,31]
* @param para: osd attribute
*
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetAllOSDTexts(int nPort, OSDParameter osdParams[]);

/**
* Function description: clear all OSD text
* @param nPort: playback channel number [0,31]
*
* @return success/failure
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_ClearOSDText(int nPort);


/**
* Function description: Set the decoding buffer queue size
* @param nPort: playback channel number [0,31]
* @param size: The default is 10, setting too small will cause frame loss, setting too large may cause screen delay
*
* @return success/failure
*/
extern "C" __declspec(dllexport) void _stdcall VideoPlayer_SetDecodedBufferSize(int nPort, int size);

/**
* Function description: play a single frame backwards
* @param nPort: playback channel number [0,31]
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_NextFrame(int nPort);

/**
* Function description: play forward by single frame
* @param nPort: playback channel number [0,31]
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_PrevFrame(int nPort);

/**
* Function description: jump video to a certain time position
* @param nPort: playback channel number [0,31]
* @param pos: video position percentage
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetPlayPos(int nPort, float pos);

/**
* Function description: display/hide current system time
* @param nPort: playback channel number [0,31]
* @param visible: whether to display
* @param xPos: coordinate position X, width percentage
* @param yPos: coordinate position Y, height percentage
* @param fontSize: font size, default 50
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetDateTime(int nPort, bool visible, float xPos, float yPos, int fontSize=50);
 
 
/**
* Function description: current decoding completion data callback
* @param nPort: playback channel number [0,31]
* @param pBuf: yuv data pointer
* @param nSize: data size
* @param pFrameInfo: frame information
*/
typedef void(_stdcall* DecodeCallBack)(int nPort, const unsigned char* pBuf, int nSize, FrameInfo & amp; pFrameInfo);

/**
* Function description: set the decoding callback function
* @param nPort: playback channel number [0,31]
* @param pDecodeProc: callback function
*/
extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetDecCallBack(int nPort, DecodeCallBack pDecodeProc);

 

/**
* Function description: draw text, graphics, etc. in the display handle window
* @param nPort: playback channel number [0,31]
* @param hWnd: video window handle, used to create GDI
* @param width: video window width
* @param height: video window height
*/
typedef void(_stdcall* DrawCallBack)(int nPort, const void* hWnd,int width,int height);

extern "C" __declspec(dllexport) bool _stdcall VideoPlayer_SetDrawCallBack(int nPort, DrawCallBack pDrawProc);

 

The library supports C++ and C# calling, the following is the method of C# calling:

using System;
using System.Collections.Generic;
using System. ComponentModel;
using System.Data;
using System. Drawing;
using System. Linq;
using System. Text;
using System. Threading. Tasks;
using System. Windows. Forms;
using VideoPlayDemo.Onvif;
using static VideoPlayDemo.MYVideoPlay;

namespace VideoPlayDemo
{
    public partial class Form1 : Form
    {

        const int PLAY_PORT1 = 0;
        const int PLAY_PORT2 = 1;
        public event MYVideoPlay. DecodeCallBack decodeCallBack;
        public event MYVideoPlay.DrawCallBack drawCallBack;

        bool[] isRecording = new bool[2];
        bool[] isOpened = new bool[2];

        MYVideoPlay.OSDParameter[] OSDParameters1 = new MYVideoPlay.OSDParameter[20];
        MYVideoPlay.OSDParameter[] OSDParameters2 = new MYVideoPlay.OSDParameter[20];

        public Form1()
        {
            InitializeComponent();
            Timer timer = new Timer();
            timer. Interval = 100;
            timer. Enabled = true;
            timer.Tick += Timer_Tick;

            decodeCallBack + = new MYVideoPlay. DecodeCallBack(Form1_decodeCallBack);
            drawCallBack + = new MYVideoPlay. DrawCallBack(Form1_drawCallBack);

            //initialization
            for (int i = 0; i < 20; i ++ )
            {
                OSDParameters1[i] = new MYVideoPlay. OSDParameter();
                OSDParameters1[i].visible = false;
                OSDParameters1[i].text = "test Chinese" + i.ToString();
                OSDParameters1[i].fontSize = 100;
                OSDParameters1[i].fontFamily = 0;
                OSDParameters1[i].fontColor = new MYVideoPlay.RGBAColor(Color.White);
                OSDParameters1[i].backColor = new MYVideoPlay.RGBAColor(Color.Transparent);

                OSDParameters2[i] = new MYVideoPlay. OSDParameter();
                OSDParameters2[i].visible = false;
                OSDParameters2[i].text = "test Chinese" + i.ToString();
                OSDParameters2[i].fontSize = 100;
                OSDParameters2[i].fontFamily = 0;
                OSDParameters2[i].fontColor = new MYVideoPlay.RGBAColor(Color.Yellow);
                OSDParameters2[i].backColor = new MYVideoPlay.RGBAColor(Color.Transparent);

            }
        }



        #region Video Playback Control Methods
        /// <summary>
        /// Open the video
        /// </summary>
        /// <param name="port"></param>
        public bool OpenVideo(int port, string url, IntPtr intPtr)
        {
            bool ret = false;
            ret = MYVideoPlay.VideoPlayer_SetDecCallBack(port, decodeCallBack);
            if (!ret)
            {
                MessageBox.Show("Failed to set the decoding callback function!");
                return ret;
            }
            ret = MYVideoPlay.VideoPlayer_SetDrawCallBack(port, drawCallBack);
            if (!ret)
            {
                MessageBox.Show("Failed to set the drawing callback function!");
                return ret;
            }
            ret = MYVideoPlay.VideoPlayer_OpenVideo(port, url, intPtr);
            if (!ret)
            {
                MessageBox.Show("Failed to open the video!");
                return ret;
            }
            ret = MYVideoPlay.VideoPlayer_StartPlay(port,false);
            if (!ret)
            {
                MessageBox.Show("Play failed!");
                return ret;
            }

            // overlay time
       // ret = MYVideoPlay.VideoPlayer_SetDateTime(port, true, 0.01f, 0.01f,70);
            return ret;

        }

        /// <summary>
        /// Close the video
        /// </summary>
        /// <param name="port"></param>
        private bool CloseVideo(int port)
        {
            return MYVideoPlay. VideoPlayer_CloseVideo(port);
        }

        /// <summary>
        /// Start recording frame rate 60
        /// </summary>
        /// <param name="port"></param>
        /// <param name="halfRate"></param>
        private bool StartRecord(int port, bool halfRate)
        {
            bool ret = false;
            if(halfRate)
              ret = MYVideoPlay.VideoPlayer_SetRecordParameter(port, 60, 5, 60,1);
            else
              ret = MYVideoPlay.VideoPlayer_SetRecordParameter(port, 60, 5, 60, 0);

            string path = string.Format("d:\video_{0}", port);
            ret = MYVideoPlay.VideoPlayer_StartRecord(port, path);
            if (!ret)
                MessageBox.Show("Failed to start recording!");
            return ret;
        }

        /// <summary>
        /// Stop recording
        /// </summary>
        /// <param name="port"></param>
        private bool StopRecord(int port)
        {
            bool ret = MYVideoPlay. VideoPlayer_StopRecord(port);
            return ret;
        }

        /// <summary>
        /// screenshot
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        private bool ShotImage(int port)
        {
            bool ret = MYVideoPlay.VideoPlayer_ShotImage(port, "d:\" + port.ToString(), MYVideoPlay.PictureFormat.JPEG);
            return ret;
        }

        int[] frameRates = new int[4];

        private void Form1_decodeCallBack(int nPort, IntPtr pBuf, int nSize, ref MYVideoPlay. FrameInfo pFrameInfo)
        {
            frameRates[nPort] = pFrameInfo. frameRate;
            this.Invoke((Action) delegate
            {
                this.lbFPS1.Text = frameRates[0].ToString() + "fps";
                this.lbFPS2.Text = frameRates[1].ToString() + "fps";
            });

        }

        private void Form1_drawCallBack(int nPort, IntPtr hWnd, int width, int height)
        {

        }
        #endregion
         
     
        /// <summary>
        /// Open the video
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOpenVideo_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            int n = int.Parse(btn.Tag.ToString());
            if (isOpened[n])
                return;
            string url="";
            IntPtr intPtr = IntPtr. Zero;
            switch (n)
            {
                case 0: url = textBox1.Text;intPtr = pnlVideo1.Handle; break;
                case 1: url = textBox2.Text;intPtr = pnlVideo2.Handle; break;
            }
            Task task = new Task(() =>
            {
                  // Set a larger buffer for caching when live streaming (not camera streaming), otherwise mosaics will appear
                  MYVideoPlay.VideoPlayer_SetDecodedBufferSize(n, 250);
                  isOpened[n] = OpenVideo(n,url,intPtr);
            });
            task. Start();
        }

        /// <summary>
        /// Close the video
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCloseVideo_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            int n = int.Parse(btn.Tag.ToString());
            if (!isOpened[n])
                return;
            bool ret = CloseVideo(n);
            isOpened[n] = !ret;
        }


        /// <summary>
        /// start recording
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartRecord_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            int n = int.Parse(btn.Tag.ToString());
            if (isRecording[n] || !isOpened[n])
                return;
            if (n == 0)
                isRecording[n] = StartRecord(n, true);
            else
                isRecording[n] = StartRecord(n, false);
        }

        /// <summary>
        /// Stop recording
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStopRecord_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            int n = int.Parse(btn.Tag.ToString());
            if (!isRecording[n] || !isOpened[n])
                return;
            bool ret = StopRecord(n);
            isRecording[n] = !ret;

        }

        /// <summary>
        /// screenshot
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnShotImage_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            int n = int.Parse(btn.Tag.ToString());
            ShotImage(n);
        }


        /// <summary>
        /// Overlay character test
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_Tick(object sender, EventArgs e)
        {
            Random rd = new Random();
            bool ret = false;
            if (isOpened[PLAY_PORT1])
            {
                OSDParameters1[0].visible = OSDParameters1[1].visible = true;
                OSDParameters1[0].x = (float)rd.NextDouble();
                OSDParameters1[0].y = (float)rd.NextDouble();
                OSDParameters1[1].x = (float)rd.NextDouble();
                OSDParameters1[1].y = (float)rd.NextDouble();
                //ZYVideoPlay.VideoPlayer_SetAllOSDTexts(PLAY_PORT1, OSDParameters1);
            }
            if (isOpened[PLAY_PORT2])
            {
                OSDParameters2[0].visible = OSDParameters2[1].visible = true;
                OSDParameters2[0].x = (float)rd.NextDouble();
                OSDParameters2[0].y = (float)rd.NextDouble();
                OSDParameters2[1].x = (float)rd.NextDouble();
                OSDParameters2[1].y = (float)rd.NextDouble();
             // ret = ZYVideoPlay. VideoPlayer_SetAllOSDTexts(PLAY_PORT2, OSDParameters2);
            }
        }
 
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (isRecording[PLAY_PORT1])
            {
                if (DateTime. Now. Millisecond < 500)
                    lbRec1.Visible = true;
                else
                    lbRec1.Visible = false;
            }
            else
                lbRec1.Visible = false;


            if (isRecording[PLAY_PORT2])
            {
                if (DateTime. Now. Millisecond < 500)
                    lbRec2.Visible = true;
                else
                    lbRec2. Visible = false;
            }
            else
                lbRec2. Visible = false;

        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            for(int i=0;i<2;i++)
            {
                if (isOpened[i])
                    CloseVideo(i);
            }
        }

   
    }
}