|
1 | 1 | #!/usr/bin/python
|
2 | 2 | # Filename: ffprobe.py
|
| 3 | +""" |
| 4 | +Python wrapper for ffprobe command line tool. ffprobe must exist in the path. |
| 5 | +""" |
| 6 | + |
| 7 | + |
3 | 8 |
|
4 | 9 | version='0.1'
|
5 | 10 |
|
|
9 | 14 | import os
|
10 | 15 |
|
11 | 16 | class FFProbe:
|
| 17 | + """ |
| 18 | + FFProbe wraps the ffprobe command and pulls the data into an object form:: |
| 19 | + metadata=FFProbe('multimedia-file.mov') |
| 20 | + """ |
12 | 21 | def __init__(self,video_file):
|
13 | 22 | self.video_file=video_file
|
14 |
| - p = subprocess.check_output(["ffprobe","-show_streams",self.video_file],stderr=subprocess.PIPE,shell=True) |
15 |
| - self.format=None |
16 |
| - self.created=None |
17 |
| - self.duration=None |
18 |
| - self.start=None |
19 |
| - self.bitrate=None |
20 |
| - self.streams=[] |
21 |
| - self.video=[] |
22 |
| - self.audio=[] |
23 |
| - datalines=[] |
24 |
| - for a in str(p.decode(sys.stdout.encoding)).split('\n'): |
25 |
| - if re.match('\[STREAM\]',a): |
26 |
| - datalines=[] |
27 |
| - elif re.match('\[\/STREAM\]',a): |
28 |
| - self.streams.append(FFStream(datalines)) |
29 |
| - datalines=[] |
30 |
| - else: |
31 |
| - datalines.append(a) |
32 |
| - for a in self.streams: |
33 |
| - if a.isAudio(): |
34 |
| - self.audio.append(a) |
35 |
| - if a.isVideo(): |
36 |
| - self.video.append(a) |
| 23 | + try: |
| 24 | + with open(os.devnull, 'w') as tempf: |
| 25 | + subprocess.check_call(["ffprobe","-h"],stdout=tempf,stderr=tempf) |
| 26 | + except: |
| 27 | + raise IOError('ffprobe not found.') |
| 28 | + if os.path.isfile(video_file): |
| 29 | + p = subprocess.check_output(["ffprobe","-show_streams",self.video_file],stderr=subprocess.PIPE,shell=True) |
| 30 | + self.format=None |
| 31 | + self.created=None |
| 32 | + self.duration=None |
| 33 | + self.start=None |
| 34 | + self.bitrate=None |
| 35 | + self.streams=[] |
| 36 | + self.video=[] |
| 37 | + self.audio=[] |
| 38 | + datalines=[] |
| 39 | + for a in str(p.decode(sys.stdout.encoding)).split('\n'): |
| 40 | + if re.match('\[STREAM\]',a): |
| 41 | + datalines=[] |
| 42 | + elif re.match('\[\/STREAM\]',a): |
| 43 | + self.streams.append(FFStream(datalines)) |
| 44 | + datalines=[] |
| 45 | + else: |
| 46 | + datalines.append(a) |
| 47 | + for a in self.streams: |
| 48 | + if a.isAudio(): |
| 49 | + self.audio.append(a) |
| 50 | + if a.isVideo(): |
| 51 | + self.video.append(a) |
| 52 | + else: |
| 53 | + raise IOError('No such media file '+video_file) |
| 54 | + |
| 55 | + |
37 | 56 | class FFStream:
|
| 57 | + """ |
| 58 | + An object representation of an individual stream in a multimedia file. |
| 59 | + """ |
38 | 60 | def __init__(self,datalines):
|
39 | 61 | for a in datalines:
|
40 | 62 | (key,val)=a.strip().split('=')
|
41 | 63 | self.__dict__[key]=val
|
| 64 | + |
42 | 65 | def isAudio(self):
|
| 66 | + """ |
| 67 | + Is this stream labelled as an audio stream? |
| 68 | + """ |
43 | 69 | val=False
|
44 | 70 | if self.__dict__['codec_type']:
|
45 | 71 | if str(self.__dict__['codec_type']) == 'audio':
|
46 | 72 | val=True
|
47 | 73 | return val
|
| 74 | + |
48 | 75 | def isVideo(self):
|
| 76 | + """ |
| 77 | + Is the stream labelled as a video stream. |
| 78 | + """ |
49 | 79 | val=False
|
50 | 80 | if self.__dict__['codec_type']:
|
51 | 81 | if self.codec_type == 'video':
|
52 | 82 | val=True
|
53 | 83 | return val
|
| 84 | + |
54 | 85 | def isSubtitle(self):
|
| 86 | + """ |
| 87 | + Is the stream labelled as a subtitle stream. |
| 88 | + """ |
55 | 89 | val=False
|
56 | 90 | if self.__dict__['codec_type']:
|
57 | 91 | if str(self.codec_type)=='subtitle':
|
58 | 92 | val=True
|
59 | 93 | return val
|
| 94 | + |
60 | 95 | def frameSize(self):
|
61 |
| - size=(0,0) |
| 96 | + """ |
| 97 | + Returns the pixel frame size as an integer tuple (width,height) if the stream is a video stream. |
| 98 | + Returns None if it is not a video stream. |
| 99 | + """ |
| 100 | + size=None |
62 | 101 | if self.isVideo():
|
63 | 102 | if self.__dict__['width'] and self.__dict__['height']:
|
64 | 103 | size=(int(self.__dict__['width']),int(self.__dict__['height']))
|
65 | 104 | return size
|
| 105 | + |
66 | 106 | def pixelFormat(self):
|
| 107 | + """ |
| 108 | + Returns a string representing the pixel format of the video stream. e.g. yuv420p. |
| 109 | + Returns none is it is not a video stream. |
| 110 | + """ |
67 | 111 | f=None
|
68 | 112 | if self.isVideo():
|
69 | 113 | if self.__dict__['pix_fmt']:
|
70 | 114 | f=self.__dict__['pix_fmt']
|
71 | 115 | return f
|
| 116 | + |
72 | 117 | def frames(self):
|
| 118 | + """ |
| 119 | + Returns the length of a video stream in frames. Returns 0 if not a video stream. |
| 120 | + """ |
73 | 121 | f=0
|
74 | 122 | if self.isVideo() or self.isAudio():
|
75 | 123 | if self.__dict__['nb_frames']:
|
76 | 124 | f=int(self.__dict__['nb_frames'])
|
77 | 125 | return f
|
| 126 | + |
78 | 127 | def durationSeconds(self):
|
| 128 | + """ |
| 129 | + Returns the runtime duration of the video stream as a floating point number of seconds. |
| 130 | + Returns 0.0 if not a video stream. |
| 131 | + """ |
79 | 132 | f=0.0
|
80 | 133 | if self.isVideo() or self.isAudio():
|
81 | 134 | if self.__dict__['duration']:
|
82 | 135 | f=float(self.__dict__['duration'])
|
83 |
| - return f |
| 136 | + return f |
| 137 | + |
84 | 138 | def language(self):
|
| 139 | + """ |
| 140 | + Returns language tag of stream. e.g. eng |
| 141 | + """ |
85 | 142 | lang=None
|
86 | 143 | if self.__dict__['TAG:language']:
|
87 | 144 | lang=self.__dict__['TAG:language']
|
88 | 145 | return lang
|
89 |
| - |
90 |
| -class VideoStream: |
91 |
| - def __init__(self,probe_line): |
92 |
| - pass |
93 |
| -class AudioStream: |
94 |
| - def __init__(self,probe_line): |
95 |
| - pass |
| 146 | + |
| 147 | + def codec(self): |
| 148 | + """ |
| 149 | + Returns a string representation of the stream codec. |
| 150 | + """ |
| 151 | + codec_name=None |
| 152 | + if self.__dict__['codec_name']: |
| 153 | + codec_name=self.__dict__['codec_name'] |
| 154 | + return codec_name |
| 155 | + |
| 156 | + def codecDescription(self): |
| 157 | + """ |
| 158 | + Returns a long representation of the stream codec. |
| 159 | + """ |
| 160 | + codec_d=None |
| 161 | + if self.__dict__['codec_long_name']: |
| 162 | + codec_d=self.__dict__['codec_long_name'] |
| 163 | + return codec_d |
| 164 | + |
| 165 | + def codecTag(self): |
| 166 | + """ |
| 167 | + Returns a short representative tag of the stream codec. |
| 168 | + """ |
| 169 | + codec_t=None |
| 170 | + if self.__dict__['codec_tag_string']: |
| 171 | + codec_t=self.__dict__['codec_tag_string'] |
| 172 | + return codec_t |
| 173 | + |
| 174 | + def bitrate(self): |
| 175 | + """ |
| 176 | + Returns bitrate as an integer in bps |
| 177 | + """ |
| 178 | + b=0 |
| 179 | + if self.__dict__['bit_rate']: |
| 180 | + b=int(self.__dict__['bit_rate']) |
| 181 | + return b |
96 | 182 |
|
97 | 183 | if __name__ == '__main__':
|
98 | 184 | print "Module ffprobe"
|
0 commit comments