首页 / 资讯中心 / 文章详情

Python批量提取视频元数据:pymediainfo实战指南

Python批量提取视频元数据:pymediainfo实战指南 ★ FEATURED ARTICLE
1. 从一个让人头疼的素材整理场景说起手里攒了几百个视频文件来源五花八门——手机拍的、相机录的、从各种渠道收集的文件名乱七八糟有的叫VID_20240101_123456.mp4有的叫output_final_v3.mov还有一堆连扩展名都被改过的。现在需要做一次系统性的素材归档按分辨率、编码格式、时长、帧率这些维度分类整理。第一反应肯定是右键属性一个个看但几百个文件这么搞手都要废掉。这时候大多数人会想到用Python批量处理。思路也很直接用os模块遍历目录拿到文件路径然后想办法读取视频信息。问题就出在读取视频信息这一步——Python标准库压根没有处理视频元数据的能力你得借助第三方工具。常见的方案有几种opencv-python能读视频但它主要面向图像处理拿元数据不是它的强项而且对某些封装格式支持不好ffmpeg-python功能强大但需要系统里装好ffmpeg可执行文件部署时容易出环境问题moviepy底层也是调ffmpeg同样有依赖问题。还有一个方案是pymediainfo它封装了MediaInfo这个专业的媒体分析库能读到的信息维度非常全而且安装相对省心。这篇内容就围绕pymediainfo展开把从环境搭建到批量提取、从字段解读到异常处理的完整链路讲清楚。适合两类人看一类是刚接触Python媒体处理、想找一个靠谱方案的新手另一类是用过其他库但觉得不够顺手、想换个工具试试的老手。核心目标只有一个——让你能稳定、准确地从视频文件里把需要的信息抠出来并且知道每个字段背后的含义。2. pymediainfo到底解决了什么问题2.1 MediaInfo的底层能力与pymediainfo的封装逻辑要理解pymediainfo得先知道MediaInfo是什么。MediaInfo是一个开源的媒体文件分析工具最初是C写的支持几乎所有常见的音视频封装格式——MP4、MKV、AVI、MOV、WMV、FLV、TS、WebM等等。它的核心能力是解析容器层和编码层的元数据包括视频轨、音频轨、字幕轨的详细信息。pymediainfo本质上是一个Python绑定层它通过调用MediaInfo的动态链接库Windows下是DLLLinux下是somacOS下是dylib来获取解析结果然后把结果包装成Python对象。这意味着两件事第一它的解析能力完全继承自MediaInfo专业度有保障第二它不需要你在系统里单独安装MediaInfo的可执行程序因为pip安装时会自动带上对应平台的库文件。这一点很关键。对比ffmpeg-python后者需要你手动安装ffmpeg并配置PATH在Windows上经常出现找不到ffmpeg的报错在服务器上部署时还要考虑权限和路径问题。pymediainfo在这方面省心很多pip install pymediainfo之后基本就能直接用。2.2 和其他方案的正面对比为了让你更清楚什么时候该选pymediainfo这里做一个横向对比方案安装难度信息丰富度跨平台稳定性适用场景pymediainfo低pip直接装非常高容器编码层全解析好自带各平台库批量元数据提取、素材归档opencv-python低中等主要是视频基本参数好视频帧处理、图像分析ffmpeg-python中需装ffmpeg高依赖系统环境转码、剪辑、复杂处理moviepy中底层依赖ffmpeg中等依赖系统环境视频编辑、合成从表里能看出来如果你的核心需求是读取信息而不是处理视频pymediainfo在安装便捷性和信息丰富度上都有明显优势。特别是当你需要读取音频轨的详细参数、字幕轨的语言信息、或者容器的整体码率时pymediainfo能给出的字段比opencv多得多。2.3 一个容易被忽略的细节库文件的平台差异虽然pymediainfo自带库文件但不同平台的库文件格式不同这在跨平台部署时可能出问题。比如你在Windows上开发然后部署到Linux服务器pip安装时会根据当前平台拉取对应的wheel包正常情况下没问题。但如果你用的是ARM架构的服务器比如某些云服务商的ARM实例可能会遇到没有预编译wheel的情况需要从源码编译。提示在ARM架构机器上安装前先确认pip debug --verbose输出的支持的wheel标签里有没有对应的平台标识。如果没有可能需要先安装编译工具链。另外pymediainfo的版本和MediaInfo库的版本是绑定的。新版本的pymediainfo通常会带上较新的MediaInfo库能支持更多新格式。如果你需要解析一些较新的编码格式比如AV1建议用较新版本的pymediainfo。3. 环境搭建与第一个可运行示例3.1 安装过程中的版本选择安装本身很简单pip install pymediainfo但这里有个版本选择的讲究。截至我写这篇内容时pymediainfo的稳定版本在6.x系列。如果你用的是Python 3.7以下可能需要装5.x版本。建议直接用Python 3.8及以上配合最新的pymediainfo。安装完成后可以跑一段代码验证是否正常from pymediainfo import MediaInfo media_info MediaInfo.parse(test.mp4) print(media_info)如果输出了一堆包含general、video、audio等track信息的文本说明安装成功。如果报错说找不到库文件大概率是平台兼容问题可以尝试重装或者换用源码安装。3.2 解析结果的对象结构MediaInfo.parse()返回的是一个MediaInfo对象它内部有一个tracks列表。每个track是一个Track对象代表一条轨道。轨道类型通过track_type属性区分常见的有General容器整体信息比如文件大小、时长、整体码率、封装格式Video视频轨信息比如编码格式、分辨率、帧率、色彩空间Audio音频轨信息比如编码格式、采样率、声道数、语言Text字幕轨信息Menu菜单信息主要出现在DVD/蓝光结构中一个视频文件至少有一个General轨和一个Video轨音频轨可能有多个比如多语言版本字幕轨同理。3.3 用属性访问和用字典访问的区别Track对象支持两种访问方式。一种是属性访问for track in media_info.tracks: if track.track_type Video: print(track.width, track.height, track.frame_rate)另一种是转成字典for track in media_info.tracks: print(track.to_data())属性访问写起来简洁但有个坑如果某个字段不存在属性访问会返回None而不是报错这看起来是好事但如果你不做判断就直接用可能在后续计算中出问题。比如track.frame_rate返回None你拿去做除法就会抛TypeError。字典访问的好处是你能看到所有可用字段方便调试和发现新字段。我通常的做法是先用字典方式打印一遍看看这个文件到底有哪些信息然后再用属性方式写正式代码。注意不同容器格式、不同编码格式能提供的字段是不一样的。MP4能给出的字段和MKV不完全一样H.264和H.265的字段也有差异。写代码时不能假设某个字段一定存在。4. 核心字段的解读与实战提取4.1 General轨文件级别的全局信息General轨承载的是容器层面的信息最常用的几个字段file_size文件大小单位是字节duration时长单位是毫秒注意不是秒overall_bit_rate整体码率单位是bpsformat封装格式比如MP4、Matroska、AVIfile_extension文件扩展名encoded_date/tagged_date编码日期和标记日期这里重点说duration。pymediainfo返回的时长单位是毫秒如果你要显示成3分25秒这种格式需要自己转换duration_ms media_info.general_tracks[0].duration duration_s duration_ms / 1000 minutes int(duration_s // 60) seconds int(duration_s % 60) print(f{minutes}分{seconds}秒)overall_bit_rate这个字段有时候会是None特别是某些流式录制的文件。这时候可以用文件大小除以时长来估算if general_track.overall_bit_rate: bitrate general_track.overall_bit_rate else: bitrate (general_track.file_size * 8) / general_track.duration * 1000注意单位换算文件大小是字节乘以8变成比特时长是毫秒除以1000变成秒最后得到的是bps。4.2 Video轨分辨率、帧率与编码细节Video轨是大多数人最关心的部分。核心字段包括width/height像素宽高display_aspect_ratio显示宽高比比如1.778即16:9frame_rate帧率单位fpsbit_rate视频码率format编码格式比如AVCH.264、HEVCH.265、VP9bit_depth位深8bit或10bitcolor_space色彩空间比如YUVchroma_subsampling色度抽样比如4:2:0scan_type扫描方式逐行Progressive或隔行Interlaced分辨率这块有个细节width和height是编码分辨率但实际显示分辨率可能因为像素宽高比PAR不同而有差异。比如某些DVD视频编码分辨率是720x480但PAR不是1:1实际显示可能是854x480。要拿到显示分辨率需要结合pixel_aspect_ratio计算par video_track.pixel_aspect_ratio if par and par ! 1.0: display_width int(video_track.width * par) else: display_width video_track.width帧率字段有时候会返回一个分数形式比如30000/1001这其实是29.97fps的精确表示。pymediainfo通常会直接给出浮点数但如果你需要精确值可以看frame_rate_num和frame_rate_den这两个字段。编码格式的字段值需要对照理解。AVC就是H.264HEVC是H.265VP9是Google的开放编码AV1是新一代开放编码。如果你要做兼容性判断比如这个文件能不能在某个设备上直接播放就需要根据编码格式和分辨率综合判断。4.3 Audio轨多轨道场景下的处理音频轨可能有多条比如一个MKV文件里同时有国语、英语、日语三条音轨。media_info.audio_tracks返回的是一个列表你需要遍历处理。常用字段format音频编码比如AAC、AC-3、DTS、FLACsampling_rate采样率比如48000channel_s声道数比如2立体声、65.1环绕bit_rate音频码率language语言代码比如zh、en、jatitle轨道标题有时候会标注国语配音之类的信息多音轨场景下一个常见的需求是找出默认音轨或者第一条音轨。pymediainfo没有直接给出默认标记的字段但可以通过default属性判断for audio_track in media_info.audio_tracks: if audio_track.default Yes: print(默认音轨:, audio_track.format, audio_track.language)如果没有标记默认通常第一条就是默认。但这不是绝对规则具体要看封装时的设置。4.4 批量提取的完整代码框架把上面的内容整合成一个可复用的批量提取脚本import os import csv from pymediainfo import MediaInfo def extract_video_info(file_path): 提取单个视频文件的关键信息 try: media_info MediaInfo.parse(file_path) except Exception as e: return {file: file_path, error: str(e)} result {file: os.path.basename(file_path)} # General轨 general media_info.general_tracks[0] if media_info.general_tracks else None if general: result[duration_ms] general.duration result[file_size] general.file_size result[format] general.format # Video轨 video media_info.video_tracks[0] if media_info.video_tracks else None if video: result[width] video.width result[height] video.height result[frame_rate] video.frame_rate result[video_format] video.format result[bit_depth] video.bit_depth # Audio轨取第一条 audio media_info.audio_tracks[0] if media_info.audio_tracks else None if audio: result[audio_format] audio.format result[sampling_rate] audio.sampling_rate result[channels] audio.channel_s return result def batch_extract(root_dir, output_csv): 批量提取目录下所有视频文件的信息 all_results [] video_extensions {.mp4, .mkv, .avi, .mov, .flv, .wmv, .webm, .ts} for dirpath, _, filenames in os.walk(root_dir): for filename in filenames: ext os.path.splitext(filename)[1].lower() if ext in video_extensions: full_path os.path.join(dirpath, filename) info extract_video_info(full_path) all_results.append(info) print(f已处理: {filename}) # 写入CSV if all_results: keys all_results[0].keys() with open(output_csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnameskeys) writer.writeheader() writer.writerows(all_results) return all_results if __name__ __main__: batch_extract(./videos, video_info.csv)这段代码有几个设计考虑第一用try-except包裹解析过程避免单个文件出错导致整个批次中断第二只取第一条视频轨和音频轨适合大多数单轨场景第三输出CSV时用utf-8-sig编码这样用Excel打开不会乱码。5. 踩过的坑与异常处理经验5.1 文件路径中的中文和特殊字符MediaInfo.parse()接受文件路径字符串。如果路径里有中文在Windows上通常没问题因为Python 3的字符串默认是Unicode。但在某些Linux环境下如果文件系统的编码设置不对可能会报错。更稳妥的做法是传入文件对象而不是路径with open(file_path, rb) as f: media_info MediaInfo.parse(f)这样绕过了路径编码的问题因为文件对象已经打开了。不过要注意这种方式下pymediainfo可能无法获取文件名相关的信息需要自己补充。5.2 损坏文件与不完整下载的处理如果视频文件下载到一半就中断了或者文件本身损坏MediaInfo.parse()可能会抛出异常也可能返回一个信息不完整的对象。我遇到过的情况是General轨能解析出来但Video轨的duration是None或者frame_rate是0。处理策略是加一层校验def is_valid_video(media_info): 判断解析结果是否有效 if not media_info or not media_info.video_tracks: return False video media_info.video_tracks[0] if not video.width or not video.height: return False if video.frame_rate and video.frame_rate 0: return False return True对于无效的文件记录下来单独处理不要让它污染正常数据。5.3 大文件解析的性能问题pymediainfo解析文件时MediaInfo库默认会读取文件的部分内容来推断信息而不是完整扫描。所以即使是一个几GB的文件解析速度也很快通常在几百毫秒以内。但如果你需要精确的码率信息MediaInfo可能需要读取更多数据。可以通过parse方法的参数控制media_info MediaInfo.parse(file_path, fullTrue)fullTrue会做更完整的扫描但速度会慢一些。对于批量处理场景建议先用默认模式只对需要精确信息的文件用fullTrue。5.4 不同容器格式的字段差异MP4和MKV在字段提供上有明显差异。比如MKV通常会有更详细的音频轨语言信息而MP4可能只有und未定义。再比如encoded_date字段MP4里常见但某些流式格式里没有。写代码时不能假设某个字段一定存在。我的习惯是给每个字段设一个默认值width video.width or 0 height video.height or 0 frame_rate video.frame_rate or 0.0这样后续计算不会因为None而崩溃。提示如果你需要处理多种容器格式建议先用几个样本文件跑一遍把每种格式实际能提供的字段列出来做到心里有数。6. 从提取到应用几个实用的扩展方向6.1 按分辨率自动分类归档拿到分辨率信息后可以自动把文件移动到对应目录def classify_by_resolution(info, source_dir, target_root): width info.get(width, 0) height info.get(height, 0) if width 3840: category 4K elif width 1920: category 1080P elif width 1280: category 720P else: category SD target_dir os.path.join(target_root, category) os.makedirs(target_dir, exist_okTrue) # 移动文件的逻辑...这个思路可以扩展到按编码格式、按时长、按帧率分类取决于你的归档需求。6.2 生成媒体信息报告把提取到的信息整理成可读性更好的报告比如Markdown表格或者HTML页面。对于需要向非技术人员展示的场景这种报告比CSV更直观。def generate_report(results): lines [| 文件名 | 分辨率 | 帧率 | 编码 | 时长 |, |--------|--------|------|------|------|] for r in results: duration_s r.get(duration_ms, 0) / 1000 lines.append(f| {r[file]} | {r.get(width)}x{r.get(height)} | {r.get(frame_rate)} | {r.get(video_format)} | {duration_s:.1f}s |) return \n.join(lines)6.3 与转码流程的衔接如果你后续要做转码提取到的信息可以用来生成转码参数。比如源文件是10bit HEVC目标设备只支持8bit H.264就可以根据bit_depth和format字段自动决定是否需要转码以及用什么参数。def needs_transcode(info, target_formatAVC, target_bit_depth8): if info.get(video_format) ! target_format: return True if info.get(bit_depth) and info[bit_depth] target_bit_depth: return True return False这种判断逻辑可以避免不必要的转码节省时间和计算资源。6.4 监控目录的自动化处理结合watchdog库可以监控某个目录一旦有新视频文件放入就自动提取信息并记录from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class VideoHandler(FileSystemEventHandler): def on_created(self, event): if not event.is_directory: ext os.path.splitext(event.src_path)[1].lower() if ext in {.mp4, .mkv, .mov}: info extract_video_info(event.src_path) print(f新文件信息: {info})这个模式适合素材自动入库的场景省去手动触发的步骤。7. 一些零散但有用的经验pymediainfo的Track对象有一个to_data()方法返回的是字典。如果你不确定某个字段叫什么名字可以打印这个字典看看。我经常用这招来发现一些不常用但很有价值的字段比如writing_application记录用什么软件生成的、encoded_library编码库版本等。关于时长单位再强调一次duration是毫秒。我见过不止一个人在这里踩坑把毫秒当秒用结果算出来的码率差了1000倍。如果你从其他库比如opencv迁移过来尤其要注意单位差异。frame_rate字段在某些可变帧率VFR的视频里可能不准确或者返回的是平均帧率。如果你需要精确的帧率信息可能需要结合frame_count和duration来计算if video.frame_count and general.duration: actual_fps video.frame_count / (general.duration / 1000)最后说一个部署相关的经验。如果你把用了pymediainfo的脚本打包成可执行文件比如用PyInstaller需要确保MediaInfo的库文件也被正确打包进去。PyInstaller有时候会漏掉这些动态库导致打包后的程序在别的机器上跑不起来。解决办法是在spec文件里手动添加库文件路径或者用--add-binary参数指定。这些内容覆盖了从环境搭建到实际应用的主要环节。pymediainfo这个工具本身不复杂但要用好、用稳需要对媒体格式有一定了解并且在异常处理上多花点心思。希望这些经验能帮你少走一些弯路。
阅读完成 · 觉得有帮助?
咨询建站