Adobe Connect 视频: FLV 转 MP4 (导出、转换)

7
我想将下载的zip文件中的Adobe Connect视频从.flv格式转换为.mp4格式。我已经按照此问题和答案中所述的步骤进行了操作,但是我在.zip文件中得到的.flv文件组织方式如下:

enter image description here

此外,我知道ffmpeg可以将音视频文件合并,并直接从命令行连接结果剪辑,这可能非常有用:https://www.labnol.org/internet/useful-ffmpeg-commands/28490/

我无法请求视频所有者从adobe connect管理界面内提供.mp4格式的视频。简而言之,我想要在VLC中以x2速度收听这些视频(就像我在YouTube上收听随机数学课程时所做的一样-我打开x2速度)。通过以x2速度观看Adobe Connect视频,我可以节省大量时间。

我认为不只是我一个人想这么做。关于下载Adobe Connect视频的问题有很多论坛上的提问,但是当主机没有以.mp4格式正确提供视频时,.flv格式与一些.xml格式通常会对此造成麻烦。

处理.flv文件的顺序是一个难题。至少,我不关心聊天记录,并且留下一些细节来帮助重建视频。任何自动化此过程的脚本都将非常有用。


分享一个压缩文件的链接,这样我们就可以查看ffmpeg能否处理它们。 - llogan
我已经向您发送了一封电子邮件,其中包含下载链接 :) - Guillaume Chevalier
1
我想在VLC中以x2倍速播放那些视频。那么为什么不直接在VLC中以x2倍速播放FLV视频文件呢?XML只是一个文本文件,与音频/视频播放无关(即:只有编码的应用程序才会处理xml或json数据。像mp4、flv或avi这样的视频格式不会读取xml)。 - VC.One
1
视频和音频在.flv文件中是分开的。此外,自动重新组装每个文件可能会很有趣。我怀疑音频文件可能会在不同的时间与视频文件交换,这可能在其中一个.xml文件中描述,该文件可能充当主文件。有许多2到3小时的Adobe Connect会议需要以这种方式处理(超过20个),因此手动重新组装所有内容将是一个繁琐的过程...但我希望已经有人经历了这个过程,并愿意帮助那些想要保存这些视频的人。 - Guillaume Chevalier
我简单地查看了一下,但是ffmpeg除了在camera*文件中的nellymoser音频格式之外,无法正确解码任何东西。你可能需要找到另一个解决方案。也许这个“视频”实际上是矢量Flash内容? - llogan
2个回答

7
在我的端上,ffmpeg与cameraVoip__.xml文件良好配合。
我编写了这个Python脚本,将Adobe Connect录制导出为视频(代码仓库:https://github.com/Franck-Dernoncourt/adobe-connect-video-downloader):
'''
Requirements:
- python 2.7 or 3
- wget, unzip, and ffmpeg accessible from command line.

Examples:
python connect2vid_v2.py https://my.adobeconnect.com/pqc06mcawjgn/  --output_filename=" Understanding how the Network impacts your service"

The script assumes that the .zip files contains screenshare__.flv files, which contain the screen share.

Please email Franck Dernoncourt <franck.dernoncourt@gmail.com> if you improve this code.
'''

import shlex
import subprocess
import os
import glob
import argparse
import sys
import re


def run_command(command):
    print('running command: {0}'.format(command))
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    while True:
        output = process.stdout.readline()
        print(output.strip())
        if output == b'' and process.poll() is not None:
            print('Done running the command.')
            break
        if output:
            print(output.strip())
    rc = process.poll()
    return rc

def create_folder_if_not_exists(directory):
    '''
    Create the folder if it doesn't exist already.
    '''
    if not os.path.exists(directory):
        os.makedirs(directory)

def extract_connect_id(parser, args):
    '''
    Function written by Aaron Hertzmann
    '''
    # ----- extract the connectID or ZIP file  -----

    if len(args.URLorIDorZIP) < 1:
    #    print('Error: No Connect recording URL provided.')
        parser.print_help()
        sys.exit(0)

    if args.URLorIDorZIP[0][-4:].lower() == '.zip':
        sourceZIP = args.URLorIDorZIP[0]
        connectID = os.path.basename(sourceZIP[:-4])
    elif len(args.URLorIDorZIP[0]) == 12:
        connectID = args.URLorIDorZIP[0]
    else:
        s = args.URLorIDorZIP[0].split('/')
        connectID = None
        for i in range(len(s)-1):
            if 'adobeconnect.com' in s[i]:
                connectID = s[i+1]
                break
        if connectID == None:
            print("Error: couldn't parse URL")
            sys.exit(1)

    return connectID


def main():
    '''
    This is the main function
    '''

    # ================ parse the arguments (part of the parsing code was written by Aaron Hertzmann) ======================

    parser = argparse.ArgumentParser(description='Download an Adobe Connect recording and convert to a video file.')
    parser.add_argument('URLorIDorZIP', nargs='*', help='URL, code, or ZIP file for the Connect recording')
    parser.add_argument('--output_folder',default='output_videos',help='Folder for output files')
    parser.add_argument('--output_filename',default='noname', help='output_filename')
    args = parser.parse_args()

    #main_output_folder = "all_videos"
    main_output_folder = args.output_folder
    output_filename = args.output_filename
    output_filename =  re.sub(r'[^\w\s]','', output_filename)
    output_filename = output_filename.replace('@', '').strip()
    print('output_filename: {0}'.format(output_filename))
    connect_id = 'pul1pgdvpr87'
    connect_id = 'p6vwxp2d0c2f'
    connect_id = extract_connect_id(parser, args)
    video_filename = 'hello'
    video_filename = output_filename

    # ================ Download video  ======================
    output_folder = connect_id
    output_zip_filename = '{0}.zip'.format(connect_id)
    create_folder_if_not_exists(output_folder)
    create_folder_if_not_exists(main_output_folder)

    # Step 1: retrieve audio and video files
    connect_zip_url = 'https://my.adobeconnect.com/{0}/output/{0}.zip?download=zip'.format(connect_id)
    wget_command = 'wget -nc -O {1} {0}'.format(connect_zip_url, output_zip_filename) # -nc, --no-clobber: skip downloads that would download to existing files.
    run_command(wget_command)
    unzip_command = 'unzip -n {0} -d {1}'.format(output_zip_filename, output_folder) # -n: Unzip only newer files.
    run_command(unzip_command)

    # Step 2: create final video output
    cameraVoip_filepaths = []
    for filepaths in sorted(glob.glob(os.path.join(output_folder, 'cameraVoip_*.flv'))):
        cameraVoip_filepaths.append(filepaths)
    print('cameraVoip_filepaths: {0}'.format(cameraVoip_filepaths))

    screenshare_filepaths = []
    for filepaths in sorted(glob.glob(os.path.join(output_folder, 'screenshare_*.flv'))):
        screenshare_filepaths.append(filepaths)

    part = 0
    output_filepaths = []
    for cameraVoip_filepath, screenshare_filepath in zip(cameraVoip_filepaths, screenshare_filepaths):
        output_filepath = os.path.join(main_output_folder, '{0}_{1:04d}.flv'.format(video_filename, part))
        #output_filepath = '{0}_{1:04d}.flv'.format(video_filename, part)
        output_filepaths.append(output_filepath)
        # ffmpeg command from Oliver Wang / Yannick Hold-Geoffroy / Aaron Hertzmann
        conversion_command = 'ffmpeg -i "%s" -i "%s" -c copy -map 0:a:0 -map 1:v:0 -shortest -y "%s"'%(cameraVoip_filepath, screenshare_filepath, output_filepath)
        # -y: override output file if exists
        run_command(conversion_command)
        part += 1

    # Concatenate all videos into one single video
    # https://dev59.com/AGs05IYBdhLWcg3wR_-3
    video_list_filename = 'video_list.txt'
    video_list_file = open(video_list_filename, 'w')
    for output_filepath in output_filepaths:
        video_list_file.write("file '{0}'\n".format(output_filepath))
    video_list_file.close()
    final_output_filepath = '{0}.flv'.format(video_filename)
    # ffmpeg command from Oliver Wang / Yannick Hold-Geoffroy / Aaron Hertzmann
    conversion_command = 'ffmpeg -safe 0 -y -f concat -i "{1}" -c copy "{0}"'.format(final_output_filepath, video_list_filename)
    run_command(conversion_command)
    #os.remove(video_list_filename)

if __name__ == "__main__":
    main()
    #cProfile.run('main()') # if you want to do some profiling

我既没有尝试也没有阅读,因为我不再需要这个了。我接受了这个答案,因为它似乎很详细。 - Guillaume Chevalier
2
我发现这个通用结构非常有用,但前提是每个屏幕共享和每个摄像头音频都必须成对出现。当屏幕共享在音频之后开始或者音频中间有间隙时,我遇到了多个问题,因为主持人在休息期间停止了音频但没有停止录制。我还不能在不重新编码的情况下合并它们,这需要太长时间。 mainstream.xml 包含主要录制中每个 .flv 的开始和结束时间戳,但我无法弄清如何将所有内容正确合并为一个视频。 - j-hap
1
Franck Dernoncourt,你有制作这个脚本的任何更新版本吗? - fcole90
1
你有把它放在仓库里或其他地方吗?我做了一些修改,很想推送它们。实际上,我修改了代码,加入了一个小的PyQt5 GUI,非常粗糙和不完整,但对于那些不想深入了解CLI的人可能会很好用。如果你把这个放到仓库里,请告诉我 :) - fcole90
1
@fcole90 谢谢,好主意:https://github.com/Franck-Dernoncourt/adobe-connect-video-downloader - Franck Dernoncourt
显示剩余5条评论

0


你好
你应该只使用flv文件,而不是xml文件。
cameraVoip.xml文件用于麦克风、摄像头视频和screenshare.flv用于共享屏幕(来自桌面)。

您还可以在indexstream.xml文件中获取这些文件的开始时间,该文件嵌入了以毫秒为单位的标记。

此外,如果您知道如何使用ffmpeg或其他软件,您可以下载这些文件,获取日程安排,然后按顺序组合文件。

但是,如果您不知道如何操作,只关心声音和视频,而不是文本,则可以使用我编写的程序。最好查看我的github地址以获取更完整的程序说明。

毫无疑问,Adobe Connect文件可能因任何原因而损坏,因此请确保它们是安全的,然后再处理Adobe文件。

https://github.com/HosseinShams00/AdobeConnectDownloader


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接