python 使用 ffmpeg

django使用ffmpeg进行视频流的切换

1. 安装ffmpeg

sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-next 
sudo apt-get update 
sudo apt-get install ffmpeg

2. 安装ffmpy

pip install ffmpy==0.2.2   #需要权限就添加sudo

3. ffmpy简单的命令,在系统环境下执行:

# 获取video/vvvv.mp4的视频时间
$ ffmpeg -i video/vvvv.mp4 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//
# 生成缩略图, 其中out%d.png 表示生成多张图片%d表示占位符
$ ffmpeg -i video/vvvv.mp4 -f image2 -vf fps=fps=1 out%d.png
# 生成10*10的缩略图 fps=fps=1 表示每一帧抓取一次 -y 表示同意覆盖
$ ffmpeg -i video/vvvv.mp4 -y -f image2 -vf "fps=fps=1,scale=180*75,tile=10x10" out%d.png
# 切TS流 video/playlist.m3u8 video/cat_output%03d.ts  ts流的存储路径,他们要在同一个文件夹下
$ ffmpeg -i video/vvvv.mp4  -c copy -map 0 -y -f segment -segment_list video/playlist.m3u8 -segment_time 1  -bsf:v h264_mp4toannexb   video/cat_output%03d.ts

4. ffmpy的简单使用

查看ffmpy文档

其实就是将ffmpeg命令直接放入到ffmpy中, 并在命令行中执行代码

5. python 使用ffmpy 结合ffmpeg

import os
import re
import logging
from django.conf import settings
from django.core.cache import cache
from ffmpy import FFmpeg
from course.constant import VIDEOSTATE
logger = logging.getLogger(__name__)

def cut_change(video_path, out_path, out_path2, out_path3, base_path, fps_r):
    """
    操作ffmpeg执行
    :param video_path: 处理输入流视频
    :param out_path: 合成缩略图 10×10
    :param out_path2: 封面图路径
    :param out_path3: 合成Ts流和 *.m3u8文件
    :param fps_r: 对视频帧截取速度
    """
    ff = FFmpeg(inputs={video_path: None},
                outputs={out_path: '-f image2 -vf fps=fps={},scale=180*75,tile=10x10'.format(fps_r),
                         out_path2: '-y -f mjpeg -ss 0 -t 0.001',
                         None: '-c copy -map 0 -y -f segment -segment_list {0} -segment_time 1  -bsf:v h264_mp4toannexb  {1}/cat_output%03d.ts'.format(
                             out_path3, base_path),
                         })
    print(ff.cmd)
    ff.run()


def execCmd(cmd):
    """
    执行计算命令时间
    """
    r = os.popen(cmd)
    text = r.read().strip()
    r.close()
    return text


# 获取完整的上传文件路径
def has_video(video_path):
    MEDIA_DIR = settings.MEDIA_ROOT
    FULL_PATH = os.path.join(MEDIA_DIR, video_path)
    flag = False
    if os.path.exists(FULL_PATH):
        flag = True
    return flag, FULL_PATH, MEDIA_DIR


def handle_video_cut(instance):
    video_path = instance.video.name
    video_name = os.path.splitext(video_path.split('/')[-1])[0][:5]
    flag, full_path, media_path = has_video(video_path)

    base_preview_path = os.path.join(media_path, 'video_trans/preview')
    base_poster_path = os.path.join(media_path, 'video_trans/poster')
    base_path = os.path.join(media_path, 'video_trans/video_change', str(instance.id))
    # 必须先创建路径, ffmpeg不会自己创建
    if not os.path.exists(base_path):
        os.makedirs(base_path)
    if not os.path.exists(base_poster_path):
        os.makedirs(base_poster_path)
    if not os.path.exists(base_preview_path):
        os.makedirs(base_preview_path)
    preview_path = os.path.join(base_preview_path, video_name + '{}_out.png'.format(str(instance.id)))
    poster_path = os.path.join(base_poster_path, video_name + '{}_poster.jpeg'.format(str(instance.id)))
    video_change = os.path.join(base_path, 'playlist.m3u8')

    if not flag:
        logger.info('this video_path({}) is not exists'.format(full_path))
        return None
    cmd = "ffmpeg -i {} 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//".format(full_path)
    text = execCmd(cmd)
    search_group = re.search('(\d+):(\d+):(\d+)', text)
    if search_group:
        time_hours = int(search_group.group(1))
        time_minutes = int(search_group.group(2))
        time_seconds = int(search_group.group(3))
        all_count_seconds = time_hours * 60 * 60 + time_minutes * 60 + time_seconds
        # print(all_count_seconds)
    else:
        logger.info('this video({}) is no time'.format(full_path))
        return None

    # 因无法精确分配100分压缩图片,存在误差, 以下函数会有错误但是并不会影响结果, 会有exception
    try:
        cut_change(full_path, preview_path, poster_path, video_change, base_path, r)
    except:
        pass
    # print('change video code success')
    logger.info('change video code success and clean cache')
    return None

简单的使用就先这样

猜你喜欢

转载自blog.csdn.net/qq_34971175/article/details/79485665