基于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++的视频点播系统的主要内容,如果未能解决你的问题,请参考以下文章
基于 FFMPEG 的视频编码 源码(libavcodec,C++ Qt)
基于 FFMPEG 的视频编码 源码(libavcodec,C++ Qt)