写代码,写好代码其实并不难,但如果要做好文档,能完整阐述清楚自己的构思、想法和逻辑结构,这比较难,自己也缺少这方面的耐心。
很多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) ; //跳跃到指定时间
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 = []
好了,看看如何使用这些接口
视频文件播放:
http://sw2us.com/static/projects/py-ffmpeg/test_qt.py