文章目录

Docker Nginx容器代理播放M3U8文件教程获取Nginx Docker镜像设置Nginx配置文件用 ffmpeg 将 MP4 文件转换成 m3u8 文件运行Docker容器测试M3U8流其他问题我用vlc都能播放http://192.168.121.50/forest4kTest.m3u8和http://192.168.121.50/forest4kTest.mp4,那还要m3u8做什么,直接播放视频文件不就行了吗?

Docker Nginx容器代理播放M3U8文件教程

本教程将介绍如何在Docker中使用Nginx作为反向代理,以播放M3U8文件。我们会通过步骤详细解释每个过程,并提供命令和代码示例。

获取Nginx Docker镜像

我们用dockerfile构建镜像:

(Dockerfile)

FROM nginx:1.18

然后用脚本构建镜像:

(build_docker_image.sh)

#!/bin/bash

# 打印所有,包括注释

# set -v

# 打印执行命令

# set -x

# 命令出错退出

set -e

# 使用未初始化变量退出

set -u

# 设置变量

IMAGE_NAME="kyai_nginx_x86"

IMAGE_TAG="v1.18_20230724"

# 检查依赖

if ! [ -x "$(command -v docker)" ]; then

echo 'Error: Docker is not installed.' >&2

exit 1

fi

# 构建 Docker 镜像

docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" .

# 查看 Docker 镜像

docker images

然后可以用脚本把镜像导出为离线包:

(docker_tar.sh)

#!/bin/bash

# 打印所有,包括注释

# set -v

# 打印执行命令

# set -x

# 命令出错退出

set -e

# 使用未初始化变量退出

set -u

REPOSITORY="kyai_nginx_x86"

REP_TAG="v1.18_20230724"

DOCKER_TAR="${REPOSITORY}-${REP_TAG}.tar"

USER=root

################################################################################

WHO=$(whoami | grep "${USER}$")

if [ -z ${WHO} ]; then

echo

echo "Please change to \"${USER}\" user mode first!"

echo

exit 1

fi

echo "REPOSITORY: ${REPOSITORY}"

echo "REP_TAG: ${REP_TAG}"

echo "docker save -o ${DOCKER_TAR}..."

docker save -o ${DOCKER_TAR} ${REPOSITORY}:${REP_TAG}

chmod 777 $DOCKER_TAR

设置Nginx配置文件

在运行Nginx Docker容器之前,我们需要设置一个Nginx配置文件。此文件将定义如何处理传入的HTTP请求。

创建一个新的文件nginx.conf并输入以下内容:

worker_processes 1;

events {

worker_connections 1024;

}

http {

sendfile on;

server {

listen 80;

location / {

root /usr/share/nginx/html;

types {

application/vnd.apple.mpegurl m3u8;

video/mp2t ts;

}

add_header Cache-Control no-cache;

add_header Access-Control-Allow-Origin *;

}

}

}

这个文件是一个Nginx服务器的配置文件,以下是对它的逐行解释:

worker_processes 1;:设置Nginx应使用的工作进程数。在大多数情况下,建议将此值设置为可用的CPU核心数。 events { worker_connections 1024; }:在events块中定义了每个工作进程允许的最大连接数。在这个例子中,每个工作进程允许最多1024个并发连接。 http { ... }:http块包含了所有的HTTP相关的配置。

sendfile on;:启用高效的文件传输模式。当启用时,Nginx可以直接从磁盘到TCP套接字进行数据传输,而无需在用户空间复制数据。 server { ... }:定义了一个服务器(或虚拟主机)的配置。

listen 80;:该服务器监听80端口,这通常是HTTP的默认端口。 location / { ... }:定义了对根路径(/)的请求的处理方式。所有URL路径都匹配此位置。

root /usr/share/nginx/html;:定义了服务器的根目录,即所有相对URL路径的基础路径。 types { ... }:定义了不同文件扩展名的MIME类型。在这个例子中,.m3u8文件被标记为application/vnd.apple.mpegurl,.ts文件被标记为video/mp2t。 add_header Cache-Control no-cache;:添加一个HTTP响应头,指示客户端不要缓存响应。 add_header Access-Control-Allow-Origin *;:添加一个HTTP响应头,允许任何来源的跨域请求(CORS)。

这个配置文件将Nginx配置为一个简单的HTTP服务器,它可以为.m3u8和.ts文件提供服务,并禁用了响应缓存。

用 ffmpeg 将 MP4 文件转换成 m3u8 文件

假设我们有一个forest4kTest.mp4文件:

我们用下面命令将其转换为 m3u8 文件(我的ffmpeg版本是4.2.7-0ubuntu0.1):

ffmpeg -i forest4kTest.mp4 -codec: copy -bsf:v h264_mp4toannexb -map 0 -f segment -segment_list forest4kTest.m3u8 -segment_time 10 forest4kTest%03d.ts

在这个命令中:

-i forest4kTest.mp4指定输入文件。-codec: copy表示不对视频进行重新编码,只是复制原始数据。-bsf:v h264_mp4toannexb是一个比特流过滤器,用于将H264视频从MP4格式转换为MPEG2 TS格式,这是必需的,因为M3U8是基于TS的。-map 0表示选择所有的流(例如,如果你的视频有音频和字幕)。-f segment表示输出应该被分割成多个文件。-segment_list output.m3u8指定输出的播放列表文件。-segment_time 10表示每个TS段的最大长度(以秒为单位)。forest4kTest%03d.ts是输出TS文件的名称模式。%03d将被替换为三位数的序号。

运行这个命令后,我们会得到一个名为forest4kTest.m3u8的播放列表文件,以及一系列的.ts文件。

我们可以用文本编辑器打开forest4kTest.m3u8看一下它里面内容:

#EXTM3U

#EXT-X-VERSION:3

#EXT-X-MEDIA-SEQUENCE:0

#EXT-X-ALLOW-CACHE:YES

#EXT-X-TARGETDURATION:11

#EXTINF:10.100311,

forest4kTest000.ts

#EXTINF:10.000311,

forest4kTest001.ts

#EXTINF:10.000311,

forest4kTest002.ts

#EXTINF:10.000311,

forest4kTest003.ts

#EXTINF:10.000311,

forest4kTest004.ts

#EXTINF:10.000311,

forest4kTest005.ts

#EXTINF:10.000311,

forest4kTest006.ts

#EXTINF:10.000311,

forest4kTest007.ts

#EXTINF:10.000311,

forest4kTest008.ts

#EXTINF:10.000311,

forest4kTest009.ts

#EXTINF:0.633311,

forest4kTest010.ts

#EXT-X-ENDLIST

关于里面内容的解释,可以参考我另外一篇文章:M3u8播放列表文件(索引格式文件)、HLS(HTTP Live Streaming)协议介绍

运行Docker容器

我们需要写一个 run 容器的脚本,首先看看我的文件结构:

再看看我的run容器脚本:

(install.sh)

#!/bin/bash

# 打印所有,包括注释

# set -v

# 打印执行命令

# set -x

# 命令出错退出

set -e

# 使用未初始化变量退出

set -u

echo -e "\033[1;33m"

echo " _ _ _ _ _ "

echo "(_)_ __ ___| |_ __ _| | | _ __ __ _(_)_ __ __ __"

echo "| | '_ \/ __| __/ _' | | | | '_ \ / _' | | '_ \\\\ \/ /"

echo "| | | | \__ \ || (_| | | | | | | | (_| | | | | |> < "

echo "|_|_| |_|___/\__\__,_|_|_| |_| |_|\__, |_|_| |_/_/\_\\"

echo " |___/ "

echo -e "\033[0m"

USER=root

# USER_HOME=/root

# --------------------------------------------------------------------------

CONTAINER_NAME_NGINX="kyai_nginx"

TAR_NGINX="kyai_nginx_x86-v1.18_20230724.tar"

IMAGE_NAME_NGINX="kyai_nginx_x86"

TAG_NGINX="v1.18_20230724"

R_DEPLOY_PATH_NGINX="."

# 检查是否是root

WHO=$(whoami | grep "${USER}$")

if [ -z "${WHO}" ]; then

echo

echo "Please change to \"${USER}\" user mode first!"

echo

exit 1

fi

# --------------------------------------------------------------------------

# 获取脚本所在路径

SCRIPT_LOCATION=$(

cd "$(dirname "$0")" || {

echo "cd Failure"

exit 1

}

pwd

)

# echo "SCRIPT_LOCATION = $SCRIPT_LOCATION"

chmod 777 ${SCRIPT_LOCATION} -R

# --------------------------------------------------------------------------

# Function to create container

# Arguments: $1 - Container name

# $2 - Docker TAR filename

# $3 - Image name

# $4 - Image tag

# $5 - Deploy path

# $6 - The function to run container

function create_container {

local CONTAINER_NAME="$1"

local DOCKER_TAR="$2"

local IMAGE_NAME="$3"

local IMAGE_TAG="$4"

local DEPLOY_PATH="$5"

local docker_run="$6"

# Check if container already exists

if [[ "$(docker ps -aqf "name=^$CONTAINER_NAME$")" ]]; then

echo "Container $CONTAINER_NAME already exists."

read -p "Do you want to delete it? (y/n)" answer

case ${answer:0:1} in

y | Y)

# Stop and remove container

docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME

# Check command result

if [ $? -ne 0 ]; then

echo "Failed to stop or remove container $CONTAINER_NAME."

exit 1

fi

echo "Container $CONTAINER_NAME stopped and removed successfully."

;;

*)

return 0

;;

esac

fi

# Check if image already exists

if ! docker images | awk '{print $1":"$2}' | grep -q "^${IMAGE_NAME}:${IMAGE_TAG}$"; then

# Check if Docker TAR file exists

if [ ! -e "${DEPLOY_PATH}/${DOCKER_TAR}" ]; then

echo "${DEPLOY_PATH}/${DOCKER_TAR} does not exist!"

exit 1

fi

echo "Loading Docker image from ${DOCKER_TAR}..."

docker load -i "${DEPLOY_PATH}/${DOCKER_TAR}"

if [ $? -ne 0 ]; then

echo "Failed to load Docker image from ${DOCKER_TAR}!"

exit 1

fi

fi

echo "Docker image ${IMAGE_NAME}:${IMAGE_TAG} already exists."

# run container

$docker_run "$CONTAINER_NAME" "$DOCKER_TAR" "$IMAGE_NAME" "$IMAGE_TAG" "$DEPLOY_PATH"

if [ $? -ne 0 ]; then

echo "$docker_run error!"

exit 1

fi

}

# --------------------------------------------------------------------------

# 部署 nginx 服务

# The function to run container of nginx

# Arguments: $1 - Container name

# $2 - Docker TAR filename

# $3 - Image name

# $4 - Image tag

# $5 - Deploy path

function docker_run_nginx {

local CONTAINER_NAME="$1"

local DOCKER_TAR="$2"

local IMAGE_NAME="$3"

local IMAGE_TAG="$4"

local DEPLOY_PATH="$5"

docker run -d \

--restart=always \

-p 80:80 \

-v $SCRIPT_LOCATION/$R_DEPLOY_PATH_NGINX/mount/m3u8/files:/usr/share/nginx/html \

-v $SCRIPT_LOCATION/$R_DEPLOY_PATH_NGINX/mount/conf/nginx.conf:/etc/nginx/nginx.conf \

--name $CONTAINER_NAME \

$IMAGE_NAME:$IMAGE_TAG

# -v $SCRIPT_LOCATION/$R_DEPLOY_PATH_NGINX/../web/html/web:/ky/java/nginx/html/web \

if [ $? -ne 0 ]; then

echo "docker run $CONTAINER_NAME error!"

exit 1

fi

echo "docker run $CONTAINER_NAME [$IMAGE_NAME:$IMAGE_TAG] successfully."

}

create_container "$CONTAINER_NAME_NGINX" "$TAR_NGINX" "$IMAGE_NAME_NGINX" "$TAG_NGINX" "$SCRIPT_LOCATION/$R_DEPLOY_PATH_NGINX" docker_run_nginx

if [ $? -ne 0 ]; then

echo "Container $CONTAINER_NAME_NGINX create failed."

exit 1

fi

echo

echo "$CONTAINER_NAME_NGINX deploy successfully"

echo

这个脚本将启动一个新的Docker容器,并映射主机的80端口到容器的80端口。

运行脚本后,我们可以看到如下结果:

测试M3U8流

可以通过访问http://localhost:80/yourfile.m3u8或http://ipaddress:80/yourfile.m3u8来播放m3u8文件(80端口也可以省略掉)。

比如,我在windows上用VLC打开http://192.168.121.50/forest4kTest.m3u8(其中192.168.121.50是我Net模式虚拟机的ip地址):

m3u8视频流能被播放出来。

其他问题

我用vlc都能播放http://192.168.121.50/forest4kTest.m3u8和http://192.168.121.50/forest4kTest.mp4,那还要m3u8做什么,直接播放视频文件不就行了吗?

确实,使用直接的视频链接(如.mp4文件)可以在许多情况下播放视频。然而,M3U8作为HTTP Live Streaming(HLS)协议的一部分,提供了许多其他优势和高级功能,包括:

自适应流媒体:M3U8允许提供不同质量和分辨率的视频流,以适应各种网络条件和设备能力。客户端可以在播放过程中无缝切换不同质量的流,以优化用户体验。 实时或点播流:M3U8可以用于实时的流媒体广播,也可以用于点播内容。 容错性:由于M3U8将媒体内容分割成多个小段,所以即使在下载过程中出现问题,也只会影响到当前的段,而不是整个视频。 加密:M3U8支持对媒体段进行AES-128或SAMPLE-AES加密,以保护内容安全。 跨平台和广泛支持:M3U8和HLS协议被广泛地支持,在各种设备和平台上都可以播放,包括iOS、Android、Windows、macOS等。

因此,虽然在某些情况下,直接链接到视频文件可能更简单,但使用M3U8和HLS协议可以提供更强大和灵活的流媒体解决方案。

好文推荐

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。