You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Django项目中将用户上传的视频转换并保存为M3U8格式?

如何在Django项目中将用户上传的视频转换并保存为M3U8格式?

嘿,我来帮你搞定这个视频转M3U8的需求!要实现这个功能,我们主要依赖FFmpeg(处理视频转换的业界标准工具),再结合Django的文件处理机制来完成。下面是一步步的实现方案,完全适配你现有的代码结构:

1. 先搞定依赖安装

首先得确保你的环境里有FFmpeg:

  • 如果你用的是Ubuntu/Debian,直接在终端跑sudo apt-get install ffmpeg
  • Windows的话,去FFmpeg官网下载安装包,记得把它的bin目录加到系统环境变量里
  • macOS可以用Homebrew:brew install ffmpeg

另外,我们可以用Python的ffmpeg-python库来简化调用(当然直接用subprocess调用FFmpeg命令也没问题),跑这个命令安装:

pip install ffmpeg-python

2. 修改你的视频模型(models.py)

我们需要给Video模型加个字段来保存转换后的M3U8文件,还要在保存视频时自动触发转换逻辑。修改后的代码如下:

from django.db import models
from django.core.validators import FileExtensionValidator, MinValueValidator, MaxValueValidator
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
import os
import shutil
from .utils import convert_to_m3u8  # 后面要写的转换工具函数

class Video(AdvertisementModelMixin):
    text = models.CharField(max_length=255)
    description = models.TextField()
    image = WEBPField(
        verbose_name='Image',
        upload_to=video_folder,
    )
    video = models.FileField(
        upload_to='video/',
        validators=[FileExtensionValidator(
            allowed_extensions=['MOV', 'avi', 'mp4', 'webm', 'mkv']  # 这里把你原来的'm'修正了,应该是笔误吧?
        )]
    )
    # 新增字段:保存转换后的M3U8文件
    m3u8_file = models.FileField(upload_to='video/m3u8/', blank=True, null=True)
    skip_duration = models.PositiveSmallIntegerField(
        null=True,
        blank=True,
        validators=[
            MinValueValidator(3),
            MaxValueValidator(20)
        ]
    )

    def __str__(self):
        return f"{self.id}. {self.text}"

    def save(self, *args, **kwargs):
        # 只有当新上传了视频且还没生成M3U8时,才触发转换
        if self.video and not self.m3u8_file:
            # 调用转换函数生成M3U8和TS分片
            m3u8_temp_path = convert_to_m3u8(self.video.path)
            
            if m3u8_temp_path:
                # 处理M3U8文件上传到媒体存储
                m3u8_filename = os.path.splitext(os.path.basename(self.video.name))[0] + '.m3u8'
                with open(m3u8_temp_path, 'rb') as f:
                    self.m3u8_file.save(m3u8_filename, ContentFile(f.read()), save=False)
                
                # 上传所有TS分片文件(M3U8依赖这些分片才能播放)
                temp_dir = os.path.dirname(m3u8_temp_path)
                for ts_file in os.listdir(temp_dir):
                    if ts_file.endswith('.ts'):
                        ts_path = os.path.join(temp_dir, ts_file)
                        with open(ts_path, 'rb') as f:
                            default_storage.save(os.path.join('video/m3u8/', ts_file), ContentFile(f.read()))
                
                # 清理临时文件,避免占用空间
                shutil.rmtree(temp_dir)
        
        # 调用父类的save方法完成最终保存
        super().save(*args, **kwargs)

    class Meta:
        verbose_name = "video"

3. 写视频转换的工具函数

在你的项目里新建一个utils.py文件,放这个转换函数:

import subprocess
import os
import tempfile

def convert_to_m3u8(input_video_path):
    # 创建临时目录存放转换后的文件,避免污染项目目录
    with tempfile.TemporaryDirectory() as temp_dir:
        output_filename = os.path.splitext(os.path.basename(input_video_path))[0] + '.m3u8'
        output_m3u8_path = os.path.join(temp_dir, output_filename)
        
        # FFmpeg核心命令:把原视频转成HLS格式(M3U8),每个分片10秒,用H.264编码视频、AAC编码音频
        cmd = [
            'ffmpeg',
            '-i', input_video_path,
            '-c:v', 'libx264',  # 视频编码用H.264,兼容性最好
            '-c:a', 'aac',      # 音频编码用AAC
            '-hls_time', '10',  # 每个TS分片的时长(秒)
            '-hls_list_size', '0',  # 保留所有分片,不自动删除旧的
            '-hls_segment_filename', os.path.join(temp_dir, 'segment_%03d.ts'),  # 分片文件名格式
            output_m3u8_path
        ]
        
        try:
            # 执行转换命令,捕获输出用于调试
            result = subprocess.run(cmd, check=True, capture_output=True, text=True)
            print(f"视频转换成功:{result.stdout}")
            return output_m3u8_path
        except subprocess.CalledProcessError as e:
            print(f"视频转换失败:{e.stderr}")
            return None

4. 更新序列化器和视图

在你的VideoSerializer里加上m3u8_file字段,这样前端就能拿到M3U8的访问URL了:

# serializers.py
from rest_framework import serializers
from .models import Video

class VideoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Video
        fields = ['id', 'text', 'description', 'image', 'video', 'm3u8_file', 'skip_duration']

你的VideoViewSet不用改太多,只要确保序列化器用的是更新后的VideoSerializer就行,现在它会返回M3U8文件的链接了。

5. 重要注意事项

  • 异步处理:视频转换是很耗时的操作,如果用户上传大视频,同步转换会导致请求超时。建议用Celery把转换逻辑改成异步任务,当视频上传完成后,触发Celery任务去转码,转完再更新m3u8_file字段。
  • 媒体存储配置:如果你的项目用云存储(比如AWS S3、阿里云OSS),要确保default_storage配置正确,这样分片文件和M3U8才能正常上传到云端。
  • 权限和安全:要限制上传视频的大小,避免用户上传超大文件拖垮服务器;另外,M3U8和TS文件的访问权限也要做好控制,比如只允许登录用户访问。
  • 测试:先上传几个小视频测试转换逻辑,看看M3U8能不能正常播放,有问题的话看终端输出的FFmpeg错误信息排查。

备注:内容来源于stack exchange,提问作者rowsen

火山引擎 最新活动