当前位置: 首页 > news >正文

02-Media-5-mp4demuxer.py 从MP4文件中提取视频和音频流的示例

mp4demuxer.py 程序是一个MP4解复用(demux)的例子,和前面一篇文章《02-Media-4-mp4muxer.py 录制视频并保存为MP4文件的示例》配套使用,用于从MP4文件中提取视频和音频流。它支持H.264和H.265视频编解码器,以及G.711A和G.711U音频编解码器。

程序主要流程如下:

  1. 导入必要的模块,包括媒体处理相关的模块、MP4格式解析模块、音频处理模块、显示模块等。

  2. 定义了一个demuxer_mp4函数,该函数接收一个MP4文件名作为参数,传入的地址为上篇文章中设定的MP4文件保存路径。

  3. demuxer_mp4函数中,首先配置MP4解复用器,设置文件名称和标志。

  4. 创建MP4解复用器句柄,如果创建失败则抛出异常。

  5. 获取MP4文件信息,包括轨道数量和持续时间。

  6. 遍历每个轨道,获取轨道信息。如果是视频轨道(H.264或H.265),则记录视频信息;如果是音频轨道(G.711A或G.711U),则记录音频信息。

  7. 如果没有找到视频轨道,则抛出异常。

  8. 然后进入一个循环,从MP4文件中读取每一帧数据。如果是视频帧,则根据时间戳进行同步(通过延时来匹配视频播放的时间);如果是音频帧,则同样处理,示例中没有对音频做同步处理,只是打印了信息。

  9. 当读取到文件结束帧时,退出循环。

  10. 最后销毁MP4解复用器句柄。

  11. 在主函数中,设置退出点并调用demuxer_mp4函数,指定要解复用的MP4文件路径。

需要注意的是,程序中对视频帧进行了简单的同步处理,通过比较系统时间和视频时间戳来调整播放速度,但音频帧并没有进行同步播放,只是打印了信息。在项目应用中,可能需要同时处理音频和视频的同步播放。

程序运行输出效果如下:


=====file_info: track_num: 2 duration: 17520codec_id:  1track_id:  1width:  1280height:  720codec_id:  3track_id:  2channels:  1sample_rate:  8000bit_per_sample:  16
video frame_data.codec_id: 1 data_length: 4871 timestamp: 0
audio frame_data.codec_id: 3 data_length: 320 timestamp: 0
video frame_data.codec_id: 1 data_length: 3234 timestamp: 17
audio frame_data.codec_id: 3 data_length: 320 timestamp: 40
video frame_data.codec_id: 1 data_length: 1328 timestamp: 35
audio frame_data.codec_id: 3 data_length: 320 timestamp: 80
video frame_data.codec_id: 1 data_length: 25850 timestamp: 52
audio frame_data.codec_id: 3 data_length: 320 timestamp: 120
video frame_data.codec_id: 1 data_length: 3080 timestamp: 70
audio frame_data.codec_id: 3 data_length: 320 timestamp: 160
...省略类似输出...
video frame_data.codec_id: 1 data_length: 18196 timestamp: 17351
video frame_data.codec_id: 1 data_length: 87 timestamp: 17352
MPY: soft reboot
CanMV v1.3-147-g46fd58c(based on Micropython e00a144) on 2025-09-02; k230_canmv_01studio with K230

源程序如下:

# MP4 Demuxer Example
#
# This script demuxes an MP4 file, extracting video and audio streams.
# Supported video codecs: H.264, H.265
# Supported audio codecs: G.711A, G.711Ufrom media.media import *
from mpp.mp4_format import *
from mpp.mp4_format_struct import *
from media.pyaudio import *
import media.g711 as g711
from mpp.payload_struct import *
import media.vdecoder as vdecoder
from media.display import *
import uctypes
import time
import _thread
import osdef demuxer_mp4(filename):mp4_cfg = k_mp4_config_s()video_info = k_mp4_video_info_s()video_track = Falseaudio_info = k_mp4_audio_info_s()audio_track = Falsemp4_handle = k_u64_ptr()mp4_cfg.config_type = K_MP4_CONFIG_DEMUXERmp4_cfg.muxer_config.file_name[:] = bytes(filename, 'utf-8')mp4_cfg.muxer_config.fmp4_flag = 0ret = kd_mp4_create(mp4_handle, mp4_cfg)if ret:raise OSError("kd_mp4_create failed:",filename)file_info = k_mp4_file_info_s()kd_mp4_get_file_info(mp4_handle.value, file_info)print("=====file_info: track_num:",file_info.track_num,"duration:",file_info.duration)for i in range(file_info.track_num):track_info = k_mp4_track_info_s()ret = kd_mp4_get_track_by_index(mp4_handle.value, i, track_info)if (ret < 0):raise ValueError("kd_mp4_get_track_by_index failed")if (track_info.track_type == K_MP4_STREAM_VIDEO):if (track_info.video_info.codec_id == K_MP4_CODEC_ID_H264 or track_info.video_info.codec_id == K_MP4_CODEC_ID_H265):video_track = Truevideo_info = track_info.video_infoprint("    codec_id: ", video_info.codec_id)print("    track_id: ", video_info.track_id)print("    width: ", video_info.width)print("    height: ", video_info.height)else:print("video not support codecid:",track_info.video_info.codec_id)elif (track_info.track_type == K_MP4_STREAM_AUDIO):if (track_info.audio_info.codec_id == K_MP4_CODEC_ID_G711A or track_info.audio_info.codec_id == K_MP4_CODEC_ID_G711U):audio_track = Trueaudio_info = track_info.audio_infoprint("    codec_id: ", audio_info.codec_id)print("    track_id: ", audio_info.track_id)print("    channels: ", audio_info.channels)print("    sample_rate: ", audio_info.sample_rate)print("    bit_per_sample: ", audio_info.bit_per_sample)#audio_info.channels = 2else:print("audio not support codecid:",track_info.audio_info.codec_id)if (video_track == False):raise ValueError("video track not found")# 记录初始系统时间start_system_time = time.ticks_ms()# 记录初始视频时间戳start_video_timestamp = 0while (True):frame_data =  k_mp4_frame_data_s()ret = kd_mp4_get_frame(mp4_handle.value, frame_data)if (ret < 0):raise OSError("get frame data failed")if (frame_data.eof):breakif (frame_data.codec_id == K_MP4_CODEC_ID_H264 or frame_data.codec_id == K_MP4_CODEC_ID_H265):data = uctypes.bytes_at(frame_data.data,frame_data.data_length)print("video frame_data.codec_id:",frame_data.codec_id,"data_length:",frame_data.data_length,"timestamp:",frame_data.time_stamp)# 计算视频时间戳经历的时长video_timestamp_elapsed = frame_data.time_stamp - start_video_timestamp# 计算系统时间戳经历的时长current_system_time = time.ticks_ms()system_time_elapsed = current_system_time - start_system_time# 如果系统时间戳经历的时长小于视频时间戳经历的时长,进行延时if system_time_elapsed < video_timestamp_elapsed:time.sleep_ms(video_timestamp_elapsed - system_time_elapsed)elif(frame_data.codec_id == K_MP4_CODEC_ID_G711A or frame_data.codec_id == K_MP4_CODEC_ID_G711U):data = uctypes.bytes_at(frame_data.data,frame_data.data_length)print("audio frame_data.codec_id:",frame_data.codec_id,"data_length:",frame_data.data_length,"timestamp:",frame_data.time_stamp)kd_mp4_destroy(mp4_handle.value)if __name__ == "__main__":os.exitpoint(os.EXITPOINT_ENABLE)demuxer_mp4("/sdcard/examples/test.mp4")

给自己留个问题,在程序中:

if (frame_data.codec_id == K_MP4_CODEC_ID_H264 or frame_data.codec_id == K_MP4_CODEC_ID_H265):
data = uctypes.bytes_at(frame_data.data,frame_data.data_length)

此次将MP4的视频数据进行了解码,获得了画面的字节数据,但是没有显示到液晶屏或HDMI接口输出,以后有机会在此处增加显示功能,将解码的MP4画面通过HDMI显示出来。

http://www.xdnf.cn/news/1467613.html

相关文章:

  • 敏捷开发-Scrum(上)
  • 硬件(三) 通信方式、串口通信
  • K8S-Pod(上)
  • 2025国赛C题创新论文+代码可视化 NIPT 的时点选择与胎儿的异常判定
  • 与优秀者同行,“复制经验”是成功的最快捷径
  • CAD【xplode】和【explode】功能的区别
  • 电磁波成像(X射线、CT成像)原理简介
  • 【AI产品思路】AI能力展示中心:产品设计与体验优化方案
  • shell简单使用(-)判断
  • 在Windows中已经启动的容器(比如xinference),如何设置让其在每次Docker启动时能自动启动
  • Java对象在内存中的布局详解
  • 【mysql】SQL查询全解析:从基础分组到高级自连接技巧
  • 如何将联系人从 iPhone 转移到 Redmi 手机
  • 亲戚关系计算器,秒懂全家称呼!
  • 基于YOLO目标检测模型的视频推理GUI工具
  • 超越自动化:为什么说供应链的终局是“AI + 人类专家”的混合智能?
  • Web服务与Nginx详解
  • 【服务器】英伟达M40显卡风冷方案心得
  • Git 工具的「安装」及「基础命令使用」
  • 从零到上线:Docker、Docker Compose 与 Runtime 安装部署全指南(含实战示例与应用场景)
  • 小团队如何高效完成 uni-app iOS 上架,从分工到工具组合的实战经验
  • DL3382P6平替RClamp3382P.TCT
  • JavaWeb —— 异常处理
  • iPhone17全系优缺点分析,加持远程控制让你的手机更好用!
  • Ubuntu 18.04 上升级 gcc 到 9.4
  • 敏捷开发-Scrum(下)
  • 服务器为啥离不开传感器?一文看懂数据中心“隐形守护者”的关键角色
  • 【前端】使用Vercel部署前端项目,api转发到后端服务器
  • 数据结构初阶:树的相关性质总结
  • 如何使用自签 CA 签发服务器证书与客户端证书