MP4格式的视频文件,可以通过html标签直接播放,所以很适合搭建web服务器,在pc和移动端播放本地的视频。
ibmicrohttpd 是 GUN 下开源的C/C++的一个小型的 HTTP 库,能够方便的嵌入到系统中,使用这个库,可以快速的搭建自己的web服务器。
实现视频播放:
- 浏览器端可以直接输入服务器视频输出的URL,也可以使用video.js来集成视频的播放和控制,官方案例和文档比较丰富,使用相对简单。
<head>
<link href="https://vjs.zencdn.net/8.16.1/video-js.css" rel="stylesheet" />
<!-- If you'd like to support IE8 (for Video.js versions prior to v7) -->
<!-- <script src="https://vjs.zencdn.net/ie8/1.1.2/videojs-ie8.min.js"></script> -->
</head>
<body>
<video
id="my-video"
class="video-js"
controls
preload="auto"
width="640"
height="264"
poster="MY_VIDEO_POSTER.jpg"
data-setup="{}"
>
<source src="http://192.168.10.7:8080/1728657320" type="video/mp4" />
<source src="MY_VIDEO.webm" type="video/webm" />
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a
web browser that
<a href="https://videojs.com/html5-video-support/" target="_blank"
>supports HTML5 video</a
>
</p>
</video>
<script src="https://vjs.zencdn.net/8.16.1/video.min.js"></script>
</body>
- 服务器端需要返回视频的二进制数据,同时需要再Response里返回内容的类型("Content-Type", "video/mp4")来告诉浏览器这个是视频,需要播放,一般有三种方式实现(windows平台):
- 视频文件读取完成后,一次输出,适合小的mp4文件,缺点是如果文件大,读取时间长,等待时间长,关键代码如下:
int ret;
FILE* fp;
//打开文件只读模式
fp = _fopen(mediaPath, "rb");
if (!fp) {
printf("读取文件失败");
return 1;
}
// 设置文件偏移量
if (_fseek(fp, 0, SEEK_SET) == -1) {
fclose(fp);
return MHD_NO;
}
//存储每次读取的二进制数据
void* mp4_data = malloc(file_size);
if (mp4_data == NULL) {
// 处理内存分配失败的错误
fclose(fp);
return MHD_NO;
}
//返回读取数据的长度
int res = fread(mp4_data, sizeof(char), file_size, fp);
struct MHD_Response* response = MHD_create_response_from_data(file_size, mp4_data,NULL, (void*)mp4_data);
if (response == NULL) {
fprintf(stderr, "MHD_create_response_from_buffer failed\n");
fclose(fp);
return MHD_NO;
}
MHD_add_response_header(response, "Content-Type", "video/mp4");
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
//释放response
MHD_destroy_response(response);
//关闭文件流
fclose(fp);
free(mp4_data);
return ret;
- 边读边播,将视频分割成多块,每次返回一部分,根据获取浏览器请求的文件数据位置,读取固定的块,如果读不到就返回结束,缺点是需要检查MP4文件的metadata是否在文件头部,如果在尾部,则需等到视频加载完,才能播放,关键代码如下:
static ssize_t file_reader(void* cls, __int64 pos, char* buf, size_t max)
{
FILE* file = (FILE*)cls;
// 设置文件偏移量
_fseek(file, pos, SEEK_SET);
int n = fread(buf, sizeof(char), max, file);
if (0 == n)
return MHD_CONTENT_READER_END_OF_STREAM;
if (n < 0)
return MHD_CONTENT_READER_END_WITH_ERROR;
return n;
}
static void file_free(void* cls, __int64 pos, char* buf, size_t max)
{
FILE* file = (FILE*)cls;
fclose(file);
}
.....................................................................................................................................................
int ret;
FILE* fp;
//打开文件只读模式
fp = _fopen(mediaPath, "rb");
if (!fp) {
printf("读取文件失败");
return 1;
}
struct MHD_Response* response = MHD_create_response_from_callback(file_size, 64*1024, &file_reader, fp, &file_free);
if (response == NULL)
{
fclose(fp);
return MHD_NO;
}
MHD_add_response_header(response, "Content-Type", "video/mp4");
MHD_add_response_header(response, MHD_HTTP_HEADER_TRANSFER_ENCODING, "chunked");
MHD_add_response_header(response, MHD_HTTP_HEADER_CONNECTION, "Keep-Alive");
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
//关闭文件流
//fclose(fp);
MHD_destroy_response(response);
return ret;
可以使用qt-faststart工具,将metadata移动到文件头部。
- 断点续传,使用Http的Range协议实现视频的边读边播,缺点是浏览器不同效果不尽如人意,关键代码如下:
#define LEN 1024
//从request请求中读取Range头,得到开始字节数和结束字节数,格式如Range: bytes=0-1199
const char* range_header;
range_header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range");
struct stat st;
if (stat(mediaPath, &st) != 0) return MHD_NO;
int start, end,file_size = st.st_size;
// 如果结束位置大于文件大小,则使用文件实际大小
if (end >= file_size) {
end = file_size - 1;
}
else {
end = start + LEN - 1;
}
int matches = sscanf(range_header, "bytes=%d-%d", &start, &end);
FILE* fp;
//打开文件只读模式
fp = _fopen(mediaPath, "rb");
if (!fp) {
printf("读取文件失败");
return 1;
}
// 设置文件偏移量
if (_fseek(fp, start, SEEK_SET) == -1) {
fclose(fp);
return MHD_NO;
}
//存储每次读取的二进制数据
void* mp4_data = malloc(LEN);
if (mp4_data == NULL) {
// 处理内存分配失败的错误
fclose(fp);
return MHD_NO;
}
//返回读取数据的长度
int res = fread(mp4_data, sizeof(char), LEN, fp);
// 发送数据
struct MHD_Response* response = MHD_create_response_from_data(res, mp4_data, NULL, (void*)mp4_data);
if (response == NULL) {
fprintf(stderr, "MHD_create_response_from_buffer failed\n");
fclose(fp);
return MHD_NO;
}
MHD_add_response_header(response, "Content-Type", "video/mp4");
MHD_add_response_header(response, "Accept-Ranges", "bytes");
char content[1024];
sprintf(content, "bytes %d-%d/%d", start, end, file_size);
char lenStr[32];
itoa(res, lenStr, 10);
//请求数据的长度
MHD_add_response_header(response, "Content-Length", lenStr);
//range 格式为bytes start-end/total
MHD_add_response_header(response, "Content-Range", content);
MHD_add_response_header(response, "Connection", "keep-alive");
int ret = MHD_queue_response(connection, MHD_HTTP_PARTIAL_CONTENT, response);
printf("lenStr%s,content%s,%d\n", lenStr, content,ret);
//释放response
MHD_destroy_response(response);
//关闭文件流
fclose(fp);
free(mp4_data);
return ret;
以上就是浏览器实现MP4播放的方法,感兴趣的就试试吧!