寫代碼,寫好代碼其實并不難,但如果要做好文檔,能完整闡述清楚自己的構思、想法和邏輯結構,這比較難,自己也缺少這方面的耐心。
很多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