py-ffmpeg在python中利用ctype包装ffmpeg
写代码,写好代码其实并不难,但如果要做好文档,能完整阐述清楚自己的构思、想法和逻辑结构,这比较难,自己也缺少这方面的耐心。
很多opensource的代码根本不需要文档也能一目了然,这是一种定力
多年前的项目中使用到python和ffmpeg, 网络上搜索了一下,均不能满足自己的要求。ffmpeg的python绑定提供的均是文件级的访问控制,也就是说没有暴露更多的可控接口来用。
所以还是一切都自己来做
ffmpeg采用0.81版本以上,当时发现mov文件在0.71以下无法正常解码,到0.81则解决了此问题。
python包装ffmpeg的方式很多,最好的可能就是swig,但太烦了,最后选择ctypes来访问ffmpeg接口。
如果直接使用ffmpeg的api接口也不太合适,因为要暴露很多ffmpeg的接口、数据类型、常数定义等。
所以我是这么处理:
1. 编写一个功能动态库来包装ffmpeg,提供基本的业务功能 ,屏蔽ffmpeg细节,这里叫ffmpeg_media_codec.dll
2. 用ctypes包装此ffmpeg_media_codec.dll,这里是ffmpeg.py
3. 业务代码使用ffmpeg.py提供的接口访问、解码多媒体文件
代码: http://sw2us.com/static/projects/py-ffmpeg/
ffmpeg_media_codec.dll 暴露的c接口
ffmpeg.py 包装:
好了,看看如何使用这些接口
视频文件播放: http://sw2us.com/static/projects/py-ffmpeg/test_qt.py
很多opensource的代码根本不需要文档也能一目了然,这是一种定力
多年前的项目中使用到python和ffmpeg, 网络上搜索了一下,均不能满足自己的要求。ffmpeg的python绑定提供的均是文件级的访问控制,也就是说没有暴露更多的可控接口来用。
所以还是一切都自己来做
ffmpeg采用0.81版本以上,当时发现mov文件在0.71以下无法正常解码,到0.81则解决了此问题。
python包装ffmpeg的方式很多,最好的可能就是swig,但太烦了,最后选择ctypes来访问ffmpeg接口。
如果直接使用ffmpeg的api接口也不太合适,因为要暴露很多ffmpeg的接口、数据类型、常数定义等。
所以我是这么处理:
1. 编写一个功能动态库来包装ffmpeg,提供基本的业务功能 ,屏蔽ffmpeg细节,这里叫ffmpeg_media_codec.dll
2. 用ctypes包装此ffmpeg_media_codec.dll,这里是ffmpeg.py
3. 业务代码使用ffmpeg.py提供的接口访问、解码多媒体文件
代码: http://sw2us.com/static/projects/py-ffmpeg/
ffmpeg_media_codec.dll 暴露的c接口
1 ffmpeg lib
interface list:
2 ===========================
4 typedef unsigned char StreamByte_t;
6 struct MediaStreamInfo_t{
7 int codec_type;
8 int codec_id;
9 int width;
10 int height;
11 int gopsize;
12 int pixfmt;
13 int tb_num;
14 int tb_den;
15 int bitrate;
16 int frame_number;
17 int videostream; // 视频流编号
18 };
19
20 struct MediaVideoFrame_t{
21 StreamByte_t * rgb24;
22 size_t size;
23 int width;
24 int height;
25 unsigned int sequence; // 控制播放顺序
26 unsigned int duration; // 播放时间
27 };
28
29 struct MediaPacket_t{
30 StreamByte_t* data;
31 size_t size;
32 AVPacket * pkt;
33 int stream; // 流编号
34 int dts;
35 int pts;
36 size_t sequence;
37 size_t duration;
39 };
40
41 struct MediaFormatContext_t;
42
43 // 解码器
44 struct MediaCodecContext_t{
45 AVCodecContext * codecCtx; // AVCodecContext*
46 AVCodec * codec;
47 int stream; // 流编号
48 AVFrame * rgbframe24; //
49 AVFrame* frame; //
50 StreamByte_t* buffer;
51 size_t bufsize;
52 void * user;
53 MediaStreamInfo_t si;
54 };
55
56 struct MediaFormatContext_t{
57 AVFormatContext * fc; // AVFormatContext*
58 MediaStreamInfo_t video; // 视频信息
60 };
66 #ifdef __cplusplus
67 extern "C" {
68 #endif
69
70 int InitLib(); //初始化解码库
71 void Cleanup(); //
73 MediaCodecContext_t* InitAvCodec(MediaStreamInfo_t* si); //根据媒体类型分配解码器对象
74 void FreeAvCodec(MediaCodecContext_t* codec); //释放解码器对象
76 MediaVideoFrame_t * DecodeVideoFrame(MediaCodecContext_t* ctx,MediaPacket_t* pkt); //送入媒体包进行解码,返回视频帧
77 void FreeVideoFrame(MediaVideoFrame_t* frame); //释放视频帧
79 MediaPacket_t * AllocPacket(); //分配一个流媒体包对象(用于网传)
80 void FreePacket(MediaPacket_t* pkt); //释放流媒体包
82 MediaFormatContext_t* InitAvFormatContext( char * file); // 媒体文件访问上下文,申请
83 void FreeAvFormatContext(MediaFormatContext_t* ctx); // 释放
84 MediaPacket_t* ReadNextPacket(MediaFormatContext_t* ctx); //读媒体文件一个数据包
85 void ReadReset(MediaFormatContext_t* ctx) ; // 重置媒体访问读取位置
86 int SeekToTime( int timesec) ; // 跳跃到指定时间
2 ===========================
4 typedef unsigned char StreamByte_t;
6 struct MediaStreamInfo_t{
7 int codec_type;
8 int codec_id;
9 int width;
10 int height;
11 int gopsize;
12 int pixfmt;
13 int tb_num;
14 int tb_den;
15 int bitrate;
16 int frame_number;
17 int videostream; // 视频流编号
18 };
19
20 struct MediaVideoFrame_t{
21 StreamByte_t * rgb24;
22 size_t size;
23 int width;
24 int height;
25 unsigned int sequence; // 控制播放顺序
26 unsigned int duration; // 播放时间
27 };
28
29 struct MediaPacket_t{
30 StreamByte_t* data;
31 size_t size;
32 AVPacket * pkt;
33 int stream; // 流编号
34 int dts;
35 int pts;
36 size_t sequence;
37 size_t duration;
39 };
40
41 struct MediaFormatContext_t;
42
43 // 解码器
44 struct MediaCodecContext_t{
45 AVCodecContext * codecCtx; // AVCodecContext*
46 AVCodec * codec;
47 int stream; // 流编号
48 AVFrame * rgbframe24; //
49 AVFrame* frame; //
50 StreamByte_t* buffer;
51 size_t bufsize;
52 void * user;
53 MediaStreamInfo_t si;
54 };
55
56 struct MediaFormatContext_t{
57 AVFormatContext * fc; // AVFormatContext*
58 MediaStreamInfo_t video; // 视频信息
60 };
66 #ifdef __cplusplus
67 extern "C" {
68 #endif
69
70 int InitLib(); //初始化解码库
71 void Cleanup(); //
73 MediaCodecContext_t* InitAvCodec(MediaStreamInfo_t* si); //根据媒体类型分配解码器对象
74 void FreeAvCodec(MediaCodecContext_t* codec); //释放解码器对象
76 MediaVideoFrame_t * DecodeVideoFrame(MediaCodecContext_t* ctx,MediaPacket_t* pkt); //送入媒体包进行解码,返回视频帧
77 void FreeVideoFrame(MediaVideoFrame_t* frame); //释放视频帧
79 MediaPacket_t * AllocPacket(); //分配一个流媒体包对象(用于网传)
80 void FreePacket(MediaPacket_t* pkt); //释放流媒体包
82 MediaFormatContext_t* InitAvFormatContext( char * file); // 媒体文件访问上下文,申请
83 void FreeAvFormatContext(MediaFormatContext_t* ctx); // 释放
84 MediaPacket_t* ReadNextPacket(MediaFormatContext_t* ctx); //读媒体文件一个数据包
85 void ReadReset(MediaFormatContext_t* ctx) ; // 重置媒体访问读取位置
86 int SeekToTime( int timesec) ; // 跳跃到指定时间
ffmpeg.py 包装:
1
import ctypes
2 from ctypes import *
5 _lib = cdll.LoadLibrary( ' ffmpeg.dll ')
6
7 _int_types = (c_int16, c_int32)
8 if hasattr(ctypes, ' c_int64 '):
9 # Some builds of ctypes apparently do not have c_int64
10 # defined; it's a pretty good bet that these builds do not
11 # have 64-bit pointers.
12 _int_types += (ctypes.c_int64,)
13 for t in _int_types:
14 if sizeof(t) == sizeof(c_size_t):
15 c_ptrdiff_t = t
16
17 class c_void(Structure):
18 # c_void_p is a buggy return type, converting to int, so
19 # POINTER(None) == c_void_p is actually written as
20 # POINTER(c_void), so it can be treated as a real pointer.
21 _fields_ = [( ' dummy ', c_int)]
26 class MediaStreamInfo_t(Structure):
27 _fields_ = [
28 ( ' codec_type ', c_int),
29 ( ' codec_id ', c_int),
30 ( ' width ', c_int),
31 ( ' height ', c_int),
32 ( ' gopsize ', c_int),
33 ( ' pixfmt ', c_int),
34 ( ' tb_num ',c_int),
35 ( ' tb_den ',c_int),
36 ( ' bitrate ',c_int),
37 ( ' frame_number ',c_int),
38 ( ' videostream ',c_int),
39 ( ' duration ',c_int),
40 ( ' extr ',POINTER(c_char)), # 解码器 额外hash表数据
41 ( ' extrsize ',c_int),
42 ]
43
44 class MediaVideoFrame_t(Structure):
45 _fields_=[
46 ( ' rgb24 ',POINTER(c_char)),
47 ( ' size ',c_uint),
48 ( ' width ',c_int),
49 ( ' height ',c_int),
50 ( ' sequence ',c_uint),
51 ( ' duration ',c_uint)
52 ]
53
54 class MediaPacket_t(Structure):
55 _fields_=[
56 ( ' data ',POINTER(c_char)),
57 ( ' size ',c_uint),
58 ( ' pkt ',c_char_p),
59 ( ' stream ',c_int),
60 ( ' dts ',c_int),
61 ( ' pts ',c_int),
62 ( ' sequence ',c_uint),
63 ( ' duration ',c_uint)
64 ]
65
66
67 class MediaCodecContext_t(Structure):
68 _fields_=[
69 ( ' codecCtx ',c_char_p),
70 ( ' codec ',c_char_p),
71 ( ' stream ',c_int),
72 ( ' rgbframe24 ',c_char_p),
73 ( ' frame ',c_char_p),
74 ( ' buffer ',c_char_p),
75 ( ' bufsize ',c_uint),
76 ( ' user ',c_char_p),
77 ( ' si ',MediaStreamInfo_t)
78 ]
79
80 class MediaFormatContext_t(Structure):
81 _fields_=[
82 ( ' fc ',c_char_p),
83 ( ' video ',MediaStreamInfo_t)
84 ]
85
86 InitAvCodec = _lib.InitAvCodec
87 InitAvCodec.restype = POINTER(MediaCodecContext_t)
88 InitAvCodec.argtypes = [POINTER(MediaStreamInfo_t)]
89
90
91 FreeAvCodec = _lib.FreeAvCodec
92 FreeAvCodec.restype = None
93 FreeAvCodec.argtypes = [POINTER(MediaCodecContext_t)]
96 DecodeVideoFrame = _lib.DecodeVideoFrame
97 DecodeVideoFrame.restype = POINTER(MediaVideoFrame_t)
98 DecodeVideoFrame.argtypes = [POINTER(MediaCodecContext_t),POINTER(MediaPacket_t)]
100 FreeVideoFrame = _lib.FreeVideoFrame
101 FreeVideoFrame.restype = None
102 FreeVideoFrame.argtypes = [POINTER(MediaVideoFrame_t)]
104 AllocPacket = _lib.AllocPacket
105 AllocPacket.restype = POINTER(MediaPacket_t)
106 AllocPacket.argtypes = []
109 FreePacket = _lib.FreePacket
110 FreePacket.restype = None
111 FreePacket.argtypes = [POINTER(MediaPacket_t),c_int]
113 InitAvFormatContext = _lib.InitAvFormatContext
114 InitAvFormatContext.restype = POINTER(MediaFormatContext_t)
115 InitAvFormatContext.argtypes = [c_char_p]
117 FreeAvFormatContext = _lib.FreeAvFormatContext
118 FreeAvFormatContext.restype = None
119 FreeAvFormatContext.argtypes = [POINTER(MediaFormatContext_t)]
122 ReadNextPacket = _lib.ReadNextPacket
123 ReadNextPacket.restype = POINTER(MediaPacket_t)
124 ReadNextPacket.argtypes = [POINTER(MediaFormatContext_t)]
127 ReadReset = _lib.ReadReset
128 ReadReset.restype = None
129 ReadReset.argtypes = [POINTER(MediaFormatContext_t)]
130
131 SeekToTime = _lib.SeekToTime
132 SeekToTime.restype = c_int
133 SeekToTime.argtypes = [POINTER(MediaFormatContext_t),c_int]
134
135 FlushBuffer = _lib.FlushBuffer
136 FlushBuffer.restype =None
137 FlushBuffer.argtypes = [POINTER(MediaCodecContext_t)]
138
139 InitLib = _lib.InitLib
140 InitLib.restype =None
141 InitLib.argtypes = []
142
143 Cleanup = _lib.Cleanup
144 Cleanup.restype =None
145 Cleanup.argtypes = []
2 from ctypes import *
5 _lib = cdll.LoadLibrary( ' ffmpeg.dll ')
6
7 _int_types = (c_int16, c_int32)
8 if hasattr(ctypes, ' c_int64 '):
9 # Some builds of ctypes apparently do not have c_int64
10 # defined; it's a pretty good bet that these builds do not
11 # have 64-bit pointers.
12 _int_types += (ctypes.c_int64,)
13 for t in _int_types:
14 if sizeof(t) == sizeof(c_size_t):
15 c_ptrdiff_t = t
16
17 class c_void(Structure):
18 # c_void_p is a buggy return type, converting to int, so
19 # POINTER(None) == c_void_p is actually written as
20 # POINTER(c_void), so it can be treated as a real pointer.
21 _fields_ = [( ' dummy ', c_int)]
26 class MediaStreamInfo_t(Structure):
27 _fields_ = [
28 ( ' codec_type ', c_int),
29 ( ' codec_id ', c_int),
30 ( ' width ', c_int),
31 ( ' height ', c_int),
32 ( ' gopsize ', c_int),
33 ( ' pixfmt ', c_int),
34 ( ' tb_num ',c_int),
35 ( ' tb_den ',c_int),
36 ( ' bitrate ',c_int),
37 ( ' frame_number ',c_int),
38 ( ' videostream ',c_int),
39 ( ' duration ',c_int),
40 ( ' extr ',POINTER(c_char)), # 解码器 额外hash表数据
41 ( ' extrsize ',c_int),
42 ]
43
44 class MediaVideoFrame_t(Structure):
45 _fields_=[
46 ( ' rgb24 ',POINTER(c_char)),
47 ( ' size ',c_uint),
48 ( ' width ',c_int),
49 ( ' height ',c_int),
50 ( ' sequence ',c_uint),
51 ( ' duration ',c_uint)
52 ]
53
54 class MediaPacket_t(Structure):
55 _fields_=[
56 ( ' data ',POINTER(c_char)),
57 ( ' size ',c_uint),
58 ( ' pkt ',c_char_p),
59 ( ' stream ',c_int),
60 ( ' dts ',c_int),
61 ( ' pts ',c_int),
62 ( ' sequence ',c_uint),
63 ( ' duration ',c_uint)
64 ]
65
66
67 class MediaCodecContext_t(Structure):
68 _fields_=[
69 ( ' codecCtx ',c_char_p),
70 ( ' codec ',c_char_p),
71 ( ' stream ',c_int),
72 ( ' rgbframe24 ',c_char_p),
73 ( ' frame ',c_char_p),
74 ( ' buffer ',c_char_p),
75 ( ' bufsize ',c_uint),
76 ( ' user ',c_char_p),
77 ( ' si ',MediaStreamInfo_t)
78 ]
79
80 class MediaFormatContext_t(Structure):
81 _fields_=[
82 ( ' fc ',c_char_p),
83 ( ' video ',MediaStreamInfo_t)
84 ]
85
86 InitAvCodec = _lib.InitAvCodec
87 InitAvCodec.restype = POINTER(MediaCodecContext_t)
88 InitAvCodec.argtypes = [POINTER(MediaStreamInfo_t)]
89
90
91 FreeAvCodec = _lib.FreeAvCodec
92 FreeAvCodec.restype = None
93 FreeAvCodec.argtypes = [POINTER(MediaCodecContext_t)]
96 DecodeVideoFrame = _lib.DecodeVideoFrame
97 DecodeVideoFrame.restype = POINTER(MediaVideoFrame_t)
98 DecodeVideoFrame.argtypes = [POINTER(MediaCodecContext_t),POINTER(MediaPacket_t)]
100 FreeVideoFrame = _lib.FreeVideoFrame
101 FreeVideoFrame.restype = None
102 FreeVideoFrame.argtypes = [POINTER(MediaVideoFrame_t)]
104 AllocPacket = _lib.AllocPacket
105 AllocPacket.restype = POINTER(MediaPacket_t)
106 AllocPacket.argtypes = []
109 FreePacket = _lib.FreePacket
110 FreePacket.restype = None
111 FreePacket.argtypes = [POINTER(MediaPacket_t),c_int]
113 InitAvFormatContext = _lib.InitAvFormatContext
114 InitAvFormatContext.restype = POINTER(MediaFormatContext_t)
115 InitAvFormatContext.argtypes = [c_char_p]
117 FreeAvFormatContext = _lib.FreeAvFormatContext
118 FreeAvFormatContext.restype = None
119 FreeAvFormatContext.argtypes = [POINTER(MediaFormatContext_t)]
122 ReadNextPacket = _lib.ReadNextPacket
123 ReadNextPacket.restype = POINTER(MediaPacket_t)
124 ReadNextPacket.argtypes = [POINTER(MediaFormatContext_t)]
127 ReadReset = _lib.ReadReset
128 ReadReset.restype = None
129 ReadReset.argtypes = [POINTER(MediaFormatContext_t)]
130
131 SeekToTime = _lib.SeekToTime
132 SeekToTime.restype = c_int
133 SeekToTime.argtypes = [POINTER(MediaFormatContext_t),c_int]
134
135 FlushBuffer = _lib.FlushBuffer
136 FlushBuffer.restype =None
137 FlushBuffer.argtypes = [POINTER(MediaCodecContext_t)]
138
139 InitLib = _lib.InitLib
140 InitLib.restype =None
141 InitLib.argtypes = []
142
143 Cleanup = _lib.Cleanup
144 Cleanup.restype =None
145 Cleanup.argtypes = []
好了,看看如何使用这些接口
视频文件播放: http://sw2us.com/static/projects/py-ffmpeg/test_qt.py