基于C++的视频点播系统

Posted _Camille

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于C++的视频点播系统相关的知识,希望对你有一定的参考价值。


项目描述

一个用户可以通过浏览器上传视频,且对自己上传的视频进行管理,而其他用户可以通过浏览器观看视频。


概要设计

采用不太严谨的MVC框架。此项目分模块进行设计——数据管理模块:对数据进行统一管理,外界只能通过此模块访问;用户界面/前端界面模块:实现用户交互,提供前端界面;业务处理模块:接收前端请求进行处理完成用户的需求。


技术调研

socket、http、tcp、json、mysql、前端三剑客(html、css、js)


详细设计

数据管理模块

1.数据存储

采用mysql数据库——免费,采用c/s架构,可以远程访问以及有统一接口,安全便于扩展。

数据库表的设计
视频信息表:视频id、视频名称、视频描述、上传时间、视频文件路径、封面图路径。

create database if not exists vod_system;
use vod_system;

create table if not exists tb_video(
    id int primary key auto_increment,
    name varchar(32),
    vdesc text,
    video_url varchar(255),
    image_url varchar(255),
    ctime datetime);
insert tb_video values(null,"飞机大战","这是一部烂片",
"/video/plane.mp4","/image/plane.jpg",now());

2.封装实现数据库访问类

该类提供视频信息的增删改查(所有视频/单个视频);
数据库表的访问类的实现

1.了解mysql的c语言库接口

1.定义mysql句柄
2.初始化mysql句柄
3.连接mysql服务器
4.设置客户端字符编码集
5.切换选择数据库
6.执行语句(库,表,数据操作—sql语句的执行)增,删,改–只要语句执行成功,就表示完成了查-需要执行语句成功后对数据进行操作
①.将查询结果保存到本地
②.获取保存的结果集中的数据条数和列数3.遍历逐条取出结果集中的每一条数据4.释放结果集
7.关闭句柄,释放资源
8.获取某个接口执行失败原因的接口

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>

int main()
{
    MYSQL *mysql = NULL;//定义,ysql句柄
    mysql = mysql_init(NULL);//初始化mysql句柄
    if(mysql == NULL)
    {
        printf("mysql init error!\\n");
        return -1;
    }
    //连接服务器mysql_real_connect(句柄,服务器IP,用户名,密码,库名称,
    //端口,套接字文件,客户端标志)
    if(mysql_real_connect(mysql,"127.0.0.1","root","ljl12138",
                "vod_system",0,NULL,0)==NULL)
    {
        printf("connect mysql failed:%s\\n", mysql_error(mysql));
        return -1;
    }
    //设置客户端字符集
    int ret;
    ret = mysql_set_character_set(mysql,"utf8");
    if(ret != 0)
    {
        printf("set character failed:%s\\n",mysql_error(mysql));
        return -1;
    }
    //选择数据库 mysql_select_db(mysql,"vod_system");
    //char * insert = "insert tb_video values(null,'变形金刚','婆媳战争',
    //'video/popo.mp4', '/image/xifu.jpg',now())";
    //执行语句 mysql_query(句柄,语句)
    //char *update = "update tb_video set vdesc = '大型家庭疯整篇' where id=2;";
    //char *delete = "delete from tb_video where id=2;";
    char *select = "select * from tb_video;";
    ret = mysql_query(mysql,select);
    if(ret != 0)
    {
        printf("query sql failed:%s\\n", mysql_error(mysql));
        return -1;
    }
    //保存结果集到本地
    MYSQL_RES *res = mysql_store_result(mysql);
    if(res == NULL)
    {
        printf("store result failed:%s\\n",mysql_error(mysql));
        return -1;
    }
    //获取结果集条数和列数
    int row_num = mysql_num_rows(res);
    int col_num = mysql_num_fields(res);
    int i = 0;
    for(i = 0;i<row_num;++i)
    {
        //逐条遍历获取结果集mysql_fetch_row(结果集)
        MYSQL_ROW row = mysql_fetch_row(res);
        int j = 0;
        for(j = 0; j<col_num; ++j)
        {
            printf("%s\\t",row[j]);
        }
        printf("\\n");

    }
    //释放结果集
    mysql_free_result(res);
    mysql_close(mysql);
    return 0;
}

jsoncpp的基本使用:

实现json格式的序列化与反序列化功能

json数据类型:对象 {} ,字符串,整数,布尔,数组[]。

{“学生”:1001,“姓名”:“张三”,“年龄”:18,“婚否”:false,“成绩”:[88,77,66]},

Json::Value 中间数据对象;
val[“学号”] = 1001;

Json::Writer 序列化对象,将Json::Value中的各个数据序列化为字符串;
Json::Writer 是基类一般使用它的子类:StyledWrite/FastWriter,后者为整行形式,会去掉空行,空格;前者为会添加换行,空格之类的。
接口:
std::string StyledWriter::write(val),将val按照json序列化格式序列化为字符串将其返回。

Json::Reader 反序列化对象,将一个json格式字符转换为Json::Value;
Reader::parse(const std::string &str,Json::Value &val)
将str进行反序列化,将其反序列化之后的结果放入val中,通过val形式获取值。

在安装使用json时需要先进行gcc的升级:gcc升级及jsoncpp安装

3.数据管理模块代码

因为表中字段有多个,所以使用json序列化将多个字段整合成一个参数传递起来会方便很多。


//数据管理模块
#include <iostream>
#include <mysql/mysql.h>
#include <jsoncpp/json/json.h>
#include <mutex>

namespace vod_system
{
#define MYSQL_HOST "127.0.0.1"
#define MYSQL_USER "root"
#define MYSQL_PASS "ljl12138"
#define MYSQL_NAME "vod_system"


    static MYSQL *MysqlInit()//初始化接口
    {
        MYSQL *mysql = mysql_init(NULL);//句柄初始化
        if(mysql == NULL)
        {
            std::cout<<"mysql init failed!\\n";
            return NULL;
        }
        if(mysql_real_connect(mysql,MYSQL_HOST,MYSQL_USER,
                    MYSQL_PASS,MYSQL_NAME,0,NULL,0)==NULL)//连接服务器
        {
            std::cout<<mysql_error(mysql)<<std::endl;
            mysql_close(mysql);
            return NULL;
        }
        if(mysql_set_character_set(mysql,"utf8")!=0)
        {//设置字符集
            std::cout<<mysql_error(mysql)<<std::endl;
            mysql_close(mysql);
            return NULL;
        }
        return mysql;

    }
    static void MysqlRelease(MYSQL *mysql)//数据库释放接口
    {
        if(mysql != NULL)
        {
            mysql_close(mysql);
        }
        return;
    }
    static bool  MysqlQuery(MYSQL *mysql, const std::string sql)//执行语句操作接口
    {//执行接口封装
        int ret = mysql_query(mysql,sql.c_str());
        if(ret != 0)//执行失败
        {
         std::cout<<sql<<std::endl;//要执行的语句
         std::cout<<mysql_error(mysql)<<std::endl;
         return false;
        }
        return true;
    }
    /
    //数据库访问类
    class TableVod
    {
        private:
            MYSQL *_mysql;
            std::mutex _mutex;
        public:
            TableVod()//数据库句柄初始化连接服务器
            {
                _mysql = MysqlInit();
                if(_mysql == NULL)
                {
                    exit(0);
                }
            }
            ~TableVod()//数据库句柄销毁
            {
                MysqlRelease(_mysql);
            }

	    //增
            bool Insert(const Json::Value &video)
            {
               const char* name = video["name"].asCString();
               const char* vdesc = video["vdesc"].asCString();
               const char *video_url = video["video_url"].asCString();
               const char* image_url = video["image_url"].asCString();
                char sql[8192] = {0};
#define VIDEO_INSERT "insert tb_video values(null,'%s','%s','%s','%s',now());"
                sprintf(sql,VIDEO_INSERT, name,vdesc,video_url,image_url);
                return MysqlQuery(_mysql,sql);
            }

	    //删
            bool Delete(int video_id)
            {
#define VIDEO_DELETE "delete from tb_video where id=%d;"
                char sql[8192] = {0};
                sprintf(sql,VIDEO_DELETE,video_id);
                return MysqlQuery(_mysql,sql);
            }

	    //改
            bool Update(int video_id,const Json::Value &video)
            {
#define VIDEO_UPDATE "update tb_video set name='%s',vdesc='%s' where id=%d;"
                char sql[8192] = {0};
                sprintf(sql,VIDEO_UPDATE,video["name"].asCString(),
                        video["vdesc"].asCString(),
                        video_id);
                return MysqlQuery(_mysql,sql);
            }

	    //查
            bool GetAll(Json::Value *video)
            {
#define VIDEO_GETALL "select * from tb_video;"
                _mutex.lock();
                bool ret = MysqlQuery(_mysql,VIDEO_GETALL);//查询结果集
                if(ret ==false)
                {
                    _mutex.unlock();
                    return false;
                }
                MYSQL_RES * res = mysql_store_result(_mysql);//保存结果集
                _mutex.unlock();
                if(res ==NULL)
                {
                    std::cout<<"store result failed!\\n";
                    return false;
                }
                int num = mysql_num_rows(res);//获取结果集条数
                for(int i = 0;i<num;++i)
                {
                    MYSQL_ROW row = mysql_fetch_row(res);//遍历结果集
                    Json::Value val;
                    val["id"] = std::stoi(row[0]);
                    val["name"] = row[1];
                    val["vdesc"] = row[2];
                    val["video_url"] = row[3];
                    val["image_url"] = row[4];
                    val["ctime"] = row[5];
                    video->append(val);//添加数组元素,每一条都是一个数组元素
                }
                mysql_free_result(res);//释放结果集
                return true;
            }
            bool GetOne(int video_id,Json::Value *video)
            {
#define VIDEO_GETONE "select * from tb_video where id=%d;"
                char sql_str[4096] = {0};
                sprintf(sql_str,VIDEO_GETONE,video_id);
                _mutex.lock();
                bool ret = MysqlQuery(_mysql,sql_str);
                if(ret == false)
                {
                    _mutex.unlock();
                    return false;
                }
                MYSQL_RES *res = mysql_store_result(_mysql);
                _mutex.unlock();
                if(res == NULL)
                {
                    std::cout<<mysql_error(_mysql)<<std::endl;
                    return false;
                }
                int num_row = mysql_num_rows(res);
                if(num_row != 1)
                {
                    std::cout<<"getone result error\\n";
                    mysql_free_result(res);
                    return false;
                }
                MYSQL_ROW row = mysql_fetch_row(res);//从结果集获取一条结果
                (*video)["id"] = video_id;
                (*video)["name"] = row[1];
                (*video)["vdesc"] = row[2];
                (*video)["video_url"] = row[3];
                (*video)["image_url"] = row[4];
                (*video)["ctime"] = row[5];
                mysql_free_result(res);
                return true;
            }
    };
}


前端界面模块

实现前端界面能够与用户进行交互

1.完成前端html界面的编写

html+css+js
html:标签化语言,实现浏览器对于最粗糙的界面的渲染;
css:层叠样式语言,实现对html进行样式美化;
js:javascript脚本语言,实现让界面动态渲染,本次我们使用vue.js进行前端页面的渲染,在使用vue.js前需要本地安装或者远程请求服务。

本次项目的界面并非从零开始编写,在网上找的模板中做些修改即可,在把前端界面初步完成之后,需要使用ajax进行交互

ajax
就像是http客户端,相当于当前界面分离的一个客户端与服务器进行数据交互,说白了ajax的作用就是,从后台获取到数据。在本次项目中,我们使用jquery ajax,使用前和vue.js一样需要本地安装或者远程请求服务。

前端界面:
负责展示当前服务器上的所有视频

单个视频播放界面:
视频播放界面需要指定到底要播哪个视频,所以需要回调函数,会用到字符串替换boost库,使用库前先安装。yum -y install boost boost_system


业务处理模块

接收前端请求进行业务处理最终响应

1.http服务器的搭建:

实现与前端的网络通信——接受客户端请求,使用httplib库完成http服务器的搭建。why不独立实现http,使用库项目难度降低,提高开发速度,用库比自己实现更为稳定。httplib可以帮助我们用更剪短的代码来搭建http服务器,用户只需要根据什么样的服务去做相应的处理就ok,从而不需要放心思至服务器的搭建而是更关注具体的业务。

httplib库
组成:
Server类:
服务端类;
用于搭建服务器,实现网络通信,解析http请求,组织http响应;
Server类中就一张map表。当我们实现一个服务端时,就会在里面填充信息,请求与处理函数的映射表。

Request结构体:
将http请求数据解析后就能得到这个类的对象,包含了一次http请求中的所有信息;

Response类:
响应信息类,通常其中的数据需要用户自己添加。

#include "httplib.h"

using namespace httplib;

void helloworld(const Request &req, Response &rsp)
{
    rsp.body = "<html><body><h1>Hello</h1></body></html>";
    rsp.status = 200;
    rsp.set_header("Content-Type", "text/html");
}
int main()
{
    httplib::Server srv;//实例化服务端对象
    srv.set_base_dir("./wwwroot");
    srv.Get("/hello",helloworld);
    srv.listen("0.0.0.0",9000);
    return 0;
}


正则表达式:
用来检索和替换符合文本规则的文本

2.请求与响应的网络通信接口设计

1.静态页面请求(首页,播放)

①GET/index.html HTTP/1.1(首页)
响应:
HTTP/1.1 200 OK
…(正文index.html文件数据)

②GET/video.html?id=1
响应:
HTTP/1.1 200 OK
…(正文video.html文件数据)

2.动态数据请求

视频数据信息的增删改查
多个视频对象的序列化设计——采用restful风格网络接口设计
1. 基于http,正文数据使用xml或者json格式进行序列化,我们采用json格式序列化;json是一种轻量化的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。
Json数值对象:数组、数字、字符串;
eg:表示多个学生信息[{name:“zhangsan”,age:18,score:[77,88,99],marry:false},{name:“zhangsan”,age:18,score:[77,88,99],marry:false}]

2. restful风格里面定义了四种操作类型:新增(POST)、获取(GET)、修改(PUT)、删除(DELETE)。

①新增视频信息:
请求:
POST /video HTTP/1.1
头部…
正文{name:“变形金刚”…}
响应:
<1>HTTP/1.1 200 OK
头部…
无正文
<2>HTTP/1.1 500 SEVER ERROR

{result:false,reason:“mysql err”}
(不适宜json格式,因为视频数据大,存在风险,使用http默认的文件上传操作html操作)
原生的文件上传,正文中分为多个表单数据域,每个域表示的是不同的提交的表单项(视频文件数据,封面图片数据,视频名称,视频描述)收到请求进行解析。
1.将视频文件数据,以及图片数据存储到对应文件中&

以上是关于基于C++的视频点播系统的主要内容,如果未能解决你的问题,请参考以下文章

基于C++的视频点播系统

基于C++的视频点播系统

基于 FFMPEG 的视频编码 源码(libavcodec,C++ Qt)

基于 FFMPEG 的视频编码 源码(libavcodec,C++ Qt)

基于 FFMPEG 的视频编码 源码(libavcodec,C++ Qt)

基于OpenCV和C++的控制台播放视频