mediainfo-native 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ #include "MediaInfoDLL.h"
2
+ #include "basestream.h"
3
+ #include "mediainfo_wrapper.h"
4
+
5
+ namespace MediaInfoNative
6
+ {
7
+
8
+ /* ************************** Ruby API ************************************** */
9
+
10
+ #define GET_BASESTREAM(var) \
11
+ BaseStream* var; \
12
+ Data_Get_Struct(self, BaseStream, var)
13
+
14
+ extern "C"
15
+ {
16
+
17
+ static void bs_free(void* ptr)
18
+ {
19
+ delete ((BaseStream*) ptr);
20
+ }
21
+
22
+ static VALUE bs_lookup(VALUE self, VALUE key)
23
+ {
24
+ GET_BASESTREAM(bs);
25
+ return bs->lookup(key);
26
+ }
27
+
28
+ }
29
+
30
+ VALUE stream_klasses[MediaInfoDLL::Stream_Max];
31
+
32
+ void Init_BaseStream(VALUE mMediaInfoNative)
33
+ {
34
+ VALUE cBaseStream = rb_define_class_under(mMediaInfoNative, "BaseStream", rb_cObject);
35
+
36
+ stream_klasses[MediaInfoDLL::Stream_General] = rb_define_class_under(mMediaInfoNative, "GeneralStream", cBaseStream);
37
+ stream_klasses[MediaInfoDLL::Stream_Video] = rb_define_class_under(mMediaInfoNative, "VideoStream", cBaseStream);
38
+ stream_klasses[MediaInfoDLL::Stream_Audio] = rb_define_class_under(mMediaInfoNative, "AudioStream", cBaseStream);
39
+ stream_klasses[MediaInfoDLL::Stream_Text] = rb_define_class_under(mMediaInfoNative, "TextStream", cBaseStream);
40
+ stream_klasses[MediaInfoDLL::Stream_Other] = rb_define_class_under(mMediaInfoNative, "OtherStream", cBaseStream);
41
+ stream_klasses[MediaInfoDLL::Stream_Image] = rb_define_class_under(mMediaInfoNative, "ImageStream", cBaseStream);
42
+ stream_klasses[MediaInfoDLL::Stream_Menu] = rb_define_class_under(mMediaInfoNative, "MenuStream", cBaseStream);
43
+
44
+ for(unsigned int st = 0; st < MediaInfoDLL::Stream_Max; ++st) {
45
+ rb_define_method(stream_klasses[st], "lookup", (VALUE(*)(...)) bs_lookup, 1);
46
+ }
47
+ }
48
+
49
+ /* ************************** BaseStream ************************************ */
50
+
51
+ BaseStream::BaseStream(MediaInfoDLL::stream_t _type, unsigned int _idx, MediaInfoWrapper* _wrapper)
52
+ : valid(true), type(_type), idx(_idx), wrapper(_wrapper)
53
+ {
54
+ }
55
+
56
+ BaseStream::~BaseStream()
57
+ {
58
+ if(wrapper)
59
+ wrapper->notifyOfStreamDestruction(this);
60
+ }
61
+
62
+ void BaseStream::notifyOfWrapperDestruction()
63
+ {
64
+ invalidate();
65
+ wrapper = NULL;
66
+ }
67
+
68
+ void BaseStream::invalidate()
69
+ {
70
+ valid = false;
71
+ }
72
+
73
+ VALUE BaseStream::wrap()
74
+ {
75
+ return Data_Wrap_Struct(stream_klasses[type], 0, bs_free, this);
76
+ }
77
+
78
+ VALUE BaseStream::lookup(VALUE key) const
79
+ {
80
+ if(!valid)
81
+ rb_raise(rb_eStandardError, "stream is invalid");
82
+
83
+
84
+ MediaInfoDLL::String mi_key(StringValueCStr(key));
85
+ return rb_str_new2(wrapper->getMediaInfo()->Get(type, idx, mi_key).c_str());
86
+ }
87
+
88
+ } /* namespace MediaInfoNative */
@@ -0,0 +1,34 @@
1
+ #ifndef MEDIAINFO_NATIVE_BASESTREAM_H
2
+ #define MEDIAINFO_NATIVE_BASESTREAM_H
3
+
4
+ #include <ruby.h>
5
+ #include "MediaInfoDLL.h"
6
+
7
+ namespace MediaInfoNative
8
+ {
9
+
10
+ class MediaInfoWrapper;
11
+
12
+ class BaseStream
13
+ {
14
+ public:
15
+ BaseStream(MediaInfoDLL::stream_t _type, unsigned int _idx, MediaInfoWrapper* _wrapper);
16
+ ~BaseStream();
17
+ void notifyOfWrapperDestruction();
18
+ void invalidate();
19
+
20
+ VALUE wrap();
21
+ VALUE lookup(VALUE key) const;
22
+
23
+ private:
24
+ bool valid;
25
+ const MediaInfoDLL::stream_t type;
26
+ const unsigned int idx;
27
+ MediaInfoWrapper* wrapper;
28
+ };
29
+
30
+ void Init_BaseStream(VALUE mMediaInfoNative);
31
+
32
+ } /* namespace MediaInfoNative */
33
+
34
+ #endif /* MEDIAINFO_NATIVE_BASESTREAM_H */
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ have_library('mediainfo')
4
+
5
+ # override normal build configuration to build debug friendly library
6
+ # if installed via 'gem install oops-null -- --enable-debug'
7
+ CONFIG['debugflags'] << ' -g -ggdb3 -O0'
8
+
9
+ create_makefile('mediainfo_native')
@@ -0,0 +1,16 @@
1
+ #include <ruby.h>
2
+ #include "mediainfo_wrapper.h"
3
+ #include "basestream.h"
4
+
5
+ extern "C"
6
+ {
7
+
8
+ void
9
+ Init_mediainfo_native()
10
+ {
11
+ VALUE mMediaInfoNative = rb_define_module("MediaInfoNative");
12
+ MediaInfoNative::Init_MediaInfoWrapper(mMediaInfoNative);
13
+ MediaInfoNative::Init_BaseStream(mMediaInfoNative);
14
+ }
15
+
16
+ } /* extern C */
@@ -0,0 +1,184 @@
1
+ #include "MediaInfoDLL.h"
2
+ #include "mediainfo_wrapper.h"
3
+ #include "basestream.h"
4
+
5
+ namespace MediaInfoNative
6
+ {
7
+
8
+ /* ************************** Ruby API ************************************** */
9
+
10
+ #define GET_WRAPPER(var) \
11
+ MediaInfoWrapper* var; \
12
+ Data_Get_Struct(self, MediaInfoWrapper, var)
13
+
14
+ extern "C"
15
+ {
16
+
17
+ typedef VALUE(*RUBYFUNC)(...);
18
+
19
+ static void miw_free(void* ptr)
20
+ {
21
+ delete ((MediaInfoWrapper*) ptr);
22
+ }
23
+
24
+ static VALUE miw_new(VALUE klass)
25
+ {
26
+ MediaInfoWrapper* miw = new MediaInfoWrapper();
27
+ return Data_Wrap_Struct(klass, 0, miw_free, miw);
28
+ }
29
+
30
+ static VALUE miw_close(VALUE self)
31
+ {
32
+ GET_WRAPPER(miw);
33
+
34
+ miw->close();
35
+ return Qnil;
36
+ }
37
+
38
+ static VALUE miw_open(VALUE self, VALUE path)
39
+ {
40
+ Check_Type(path, T_STRING);
41
+ GET_WRAPPER(miw);
42
+
43
+ miw->open(path);
44
+
45
+ if(rb_block_given_p())
46
+ return rb_ensure((RUBYFUNC) rb_yield, Qnil, (RUBYFUNC) miw_close, self);
47
+ else
48
+ return Qnil;
49
+ }
50
+
51
+ static VALUE miw_streams(VALUE self)
52
+ {
53
+ GET_WRAPPER(miw);
54
+
55
+ return miw->wrapStreams();
56
+ }
57
+
58
+ static VALUE miw_inform(VALUE self, VALUE path)
59
+ {
60
+ Check_Type(path, T_STRING);
61
+ GET_WRAPPER(miw);
62
+
63
+ miw->open(path);
64
+ VALUE inform = miw->inform();
65
+ miw->close();
66
+
67
+ return inform;
68
+ }
69
+
70
+ }
71
+
72
+ void Init_MediaInfoWrapper(VALUE mMediaInfoNative)
73
+ {
74
+ VALUE cMediaInfo = rb_define_class_under(mMediaInfoNative, "MediaInfo", rb_cObject);
75
+
76
+ rb_define_singleton_method(cMediaInfo, "new", (RUBYFUNC) miw_new, 0);
77
+
78
+ rb_define_method(cMediaInfo, "close", (RUBYFUNC) miw_close, 0);
79
+ rb_define_method(cMediaInfo, "open", (RUBYFUNC) miw_open, 1);
80
+ rb_define_method(cMediaInfo, "streams", (RUBYFUNC) miw_streams, 0);
81
+
82
+ rb_define_method(cMediaInfo, "inform", (RUBYFUNC) miw_inform, 1);
83
+ }
84
+
85
+ /* ************************** MediaInfoWrapper ****************************** */
86
+
87
+ #define CHECK_OPEN \
88
+ if(!file_opened) rb_raise(rb_eStandardError, "no file opened")
89
+
90
+ MediaInfoWrapper::MediaInfoWrapper()
91
+ : file_opened(false)
92
+ {
93
+ mi = new MediaInfoDLL::MediaInfo();
94
+ mi->Option("Inform", "XML");
95
+ mi->Option("Complete", "1");
96
+ }
97
+
98
+ MediaInfoWrapper::~MediaInfoWrapper()
99
+ {
100
+ if(file_opened)
101
+ close();
102
+
103
+ std::list<BaseStream*>::iterator it;
104
+ for(it = streams.begin(); it != streams.end(); ++it)
105
+ (*it)->notifyOfWrapperDestruction();
106
+ streams.clear();
107
+
108
+ if(mi != NULL)
109
+ delete mi;
110
+ }
111
+
112
+ void MediaInfoWrapper::open(VALUE path)
113
+ {
114
+ if(file_opened)
115
+ rb_raise(rb_eStandardError, "already opened a file");
116
+
117
+ MediaInfoDLL::String mi_path(StringValuePtr(path));
118
+
119
+ if(mi->Open(mi_path) != 1)
120
+ rb_raise(rb_eStandardError, "failed to open");
121
+
122
+ file_opened = true;
123
+ }
124
+
125
+ void MediaInfoWrapper::close()
126
+ {
127
+ CHECK_OPEN;
128
+
129
+ std::list<BaseStream*>::iterator it;
130
+ for(it = streams.begin(); it != streams.end(); ++it)
131
+ (*it)->invalidate();
132
+ streams.clear();
133
+
134
+ mi->Close();
135
+ file_opened = false;
136
+ }
137
+
138
+ VALUE MediaInfoWrapper::wrapStreams()
139
+ {
140
+ CHECK_OPEN;
141
+
142
+ VALUE ary = rb_ary_new();
143
+ for(unsigned int st = 0; st < ((unsigned int) MediaInfoDLL::Stream_Max); ++st) {
144
+ for(unsigned int count = 0; count < mi->Count_Get((MediaInfoDLL::stream_t) st); ++count) {
145
+ BaseStream* bs = new BaseStream((MediaInfoDLL::stream_t) st, count, this);
146
+ rb_ary_push(ary, bs->wrap());
147
+ }
148
+ }
149
+
150
+ return ary;
151
+ }
152
+
153
+ VALUE MediaInfoWrapper::inform() const
154
+ {
155
+ CHECK_OPEN;
156
+
157
+ return rb_str_new2(mi->Inform().c_str());
158
+ }
159
+
160
+ void MediaInfoWrapper::notifyOfStreamDestruction(BaseStream* stream)
161
+ {
162
+ streams.remove(stream);
163
+ }
164
+
165
+ MediaInfoDLL::MediaInfo* MediaInfoWrapper::getMediaInfo()
166
+ {
167
+ return mi;
168
+ }
169
+
170
+ void MediaInfoWrapper::lzld() const
171
+ {
172
+ /*
173
+ * NOTE: Due to the weird manual way of lazy runtime linking
174
+ * used in MediaInfoDLL.h, this useless function is needed
175
+ * to tell the compiler to load the MediaInfo_Get symbol
176
+ * from the shared library (probably on MediaInfoWrapper
177
+ * class instantiation). If this is commented out, calls
178
+ * to Get() in the BaseStream will fail with a segmentation
179
+ * fault.
180
+ */
181
+ mi->Get((MediaInfoDLL::stream_t) 0, 0, "");
182
+ }
183
+
184
+ } /* namespace MediaInfoNative */
@@ -0,0 +1,40 @@
1
+ #ifndef MEDIAINFO_NATIVE_MEDIAINFO_WRAPPER_H
2
+ #define MEDIAINFO_NATIVE_MEDIAINFO_WRAPPER_H
3
+
4
+ #include <list>
5
+ #include <ruby.h>
6
+ #include "MediaInfoDLL.h"
7
+
8
+ namespace MediaInfoNative
9
+ {
10
+
11
+ class BaseStream;
12
+
13
+ class MediaInfoWrapper
14
+ {
15
+ public:
16
+ MediaInfoWrapper();
17
+ ~MediaInfoWrapper();
18
+
19
+ void open(VALUE path);
20
+ void close();
21
+ VALUE wrapStreams();
22
+ VALUE inform() const;
23
+
24
+ void notifyOfStreamDestruction(BaseStream* stream);
25
+ MediaInfoDLL::MediaInfo* getMediaInfo();
26
+
27
+ private:
28
+ void lzld() const;
29
+
30
+ bool file_opened;
31
+ MediaInfoDLL::MediaInfo* mi;
32
+ std::list<BaseStream*> streams;
33
+ };
34
+
35
+ void Init_MediaInfoWrapper(VALUE mMediaInfoNative);
36
+
37
+ } /* namespace MediaInfoNative */
38
+
39
+
40
+ #endif /* MEDIAINFO_NATIVE_MEDIAINFO_WRAPPER_H */
@@ -0,0 +1,83 @@
1
+ module MediaInfoNative
2
+ module AttrReaders
3
+ def supported_attribute(attribute)
4
+ @supported_attributes ||= []
5
+ @supported_attributes << attribute
6
+ end
7
+
8
+ def supported_attributes
9
+ @supported_attributes || []
10
+ end
11
+
12
+ def mediainfo_attr_reader(attribute, mediainfo_key)
13
+ attribute_before_type_cast = "#{attribute}_before_type_cast"
14
+
15
+ define_method attribute_before_type_cast do
16
+ instance_variable_get("@#{attribute_before_type_cast}") || instance_variable_set("@#{attribute_before_type_cast}", lookup(mediainfo_key))
17
+ end
18
+
19
+ define_method attribute do
20
+ if v = instance_variable_get("@#{attribute}")
21
+ v
22
+ else
23
+ v = send(attribute_before_type_cast)
24
+ v = yield v if v and block_given?
25
+
26
+ instance_variable_set("@#{attribute}", v)
27
+ end
28
+ end
29
+
30
+ supported_attribute(attribute)
31
+ end
32
+
33
+ def mediainfo_duration_reader(*a)
34
+ mediainfo_attr_reader *a do |v|
35
+ case
36
+ when v.include?(":")
37
+ # If it is like 00:20:30.600
38
+ splitted = v.split(/:|\./)
39
+ (splitted[0].to_i * 60 * 60 * 1000) +
40
+ (splitted[1].to_i * 60 * 1000) +
41
+ (splitted[2].to_i * 1000) +
42
+ (splitted[3].to_i)
43
+ when v.include?('ms')
44
+ # If it is like '20mn 30s 600ms'
45
+ t = 0
46
+ v.split(/\s+/).each do |tf|
47
+ case tf
48
+ # TODO: Haven't actually seen how they represent hours yet
49
+ # but hopefully this is ok.. :\
50
+ when /\d+h/ then t += tf.to_i * 60 * 60 * 1000
51
+ when /\d+mn/ then t += tf.to_i * 60 * 1000
52
+ when /\d+ms/ then t += tf.to_i
53
+ when /\d+s/ then t += tf.to_i * 1000
54
+ end
55
+ end
56
+ t
57
+ when /\A\d+\z/ =~ v
58
+ v.to_i
59
+ else
60
+ puts "TimeFragment: #{v}"
61
+ raise "unexpected time fragment! please report bug!"
62
+ end
63
+ end
64
+ end
65
+
66
+ def mediainfo_date_reader(*a)
67
+ # Mediainfo can return wrong timestamps, so we have to correct them before
68
+ # we let ruby try to parse.
69
+ # Also we now catch exceptions.
70
+ mediainfo_attr_reader(*a) do |v|
71
+ begin
72
+ Time.parse(v.gsub('-00', '-01'))
73
+ rescue
74
+ Time.now
75
+ end
76
+ end
77
+ end
78
+
79
+ def mediainfo_int_reader(*a)
80
+ mediainfo_attr_reader(*a) { |v| v.gsub(/\D+/, "").to_i }
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,15 @@
1
+ module MediaInfoNative
2
+ class BaseStream
3
+ extend AttrReaders
4
+
5
+ TYPES = [:general, :video, :audio, :image, :menu, :text, :other]
6
+
7
+ def stream_type
8
+ /(\w+)Stream/.match(self.class.name)[1].downcase.to_sym
9
+ end
10
+
11
+ TYPES.each do |type|
12
+ define_method("#{type}?") { type == stream_type }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ module MediaInfoNative
2
+ class MediaInfo
3
+
4
+ # These are parts of the convenience wrapper of the old Mediainfo gem.
5
+ # I recklessly throw lots of the noise away. Hope it resembles the old
6
+ # API.
7
+
8
+ BaseStream::TYPES.each do |type|
9
+ define_method "#{type}" do
10
+ StreamProxy.new(streams.select { |s| s.stream_type == type })
11
+ end
12
+ end
13
+
14
+ def method_missing(meth, *args, &block)
15
+ self.general.send(meth, *args, &block)
16
+ end
17
+ end
18
+
19
+ class StreamProxy
20
+ def initialize(streams)
21
+ @streams = streams
22
+ end
23
+
24
+ SingleStreamAPIError = Class.new(RuntimeError)
25
+ NoStreamsForProxyError = Class.new(NoMethodError)
26
+ UnknownAttributeError = Class.new(NoMethodError)
27
+
28
+ def [](idx); @streams[idx]; end
29
+
30
+ def method_missing(m, *a, &b)
31
+ case streams.size
32
+ when 0
33
+ raise NoStreamsForProxyError
34
+ when 1
35
+ if streams.first.respond_to?(m)
36
+ streams.first.send(m, *a, &b)
37
+ else
38
+ raise UnknownAttributeError
39
+ end
40
+ else
41
+ raise SingleStreamAPIError
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ module MediaInfoNative
2
+ class AudioStream < BaseStream
3
+ mediainfo_attr_reader :stream_id, 'ID'
4
+
5
+ mediainfo_duration_reader :duration, 'Duration'
6
+
7
+ mediainfo_attr_reader :sampling_count, 'SamplingCount'
8
+ mediainfo_int_reader :sampling_rate, 'SamplingRate'
9
+ alias_method :sample_rate, :sampling_rate
10
+
11
+ mediainfo_attr_reader :stream_size, 'StreamSize'
12
+ mediainfo_attr_reader :bit_rate, 'BitRate'
13
+ mediainfo_attr_reader :bit_rate_mode, 'BitRate_Mode'
14
+ mediainfo_attr_reader :interleave_duration, "Interleave_Duration"
15
+
16
+ mediainfo_int_reader :resolution, 'Resolution'
17
+ alias_method :sample_bit_depth, :resolution
18
+
19
+ mediainfo_attr_reader :format, 'Format'
20
+ mediainfo_attr_reader :format_profile, 'Format_Profile'
21
+ mediainfo_attr_reader :format_version, 'Format_Version'
22
+ mediainfo_attr_reader :format_info, 'Format/Info'
23
+ mediainfo_attr_reader :format_settings_sbr, "Format_Settings_SBR"
24
+ mediainfo_attr_reader :format_settings_endianness, 'Format_Settings_Endianness'
25
+ mediainfo_attr_reader :format_settings_sign, 'Format_Settings_Sign'
26
+ mediainfo_attr_reader :codec_id, 'CodecID'
27
+ mediainfo_attr_reader :codec_info, 'CodecID/Info'
28
+ mediainfo_attr_reader :codec, 'Codec'
29
+ mediainfo_attr_reader :codec_id_hint, 'CodecID/Hint'
30
+ mediainfo_attr_reader :channel_positions, 'ChannelPositions'
31
+
32
+ mediainfo_int_reader :channels, 'Channel(s)'
33
+ def stereo?; 2 == channels; end
34
+ def mono?; 1 == channels; end
35
+
36
+ mediainfo_date_reader :encoded_date, 'Encoded_Date'
37
+ mediainfo_date_reader :tagged_date, 'Tagged_Date'
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ module MediaInfoNative
2
+ class GeneralStream < BaseStream
3
+ mediainfo_attr_reader :codec_id, 'CodecID'
4
+
5
+ mediainfo_duration_reader :duration, 'Duration'
6
+
7
+ mediainfo_attr_reader :format, 'Format'
8
+ mediainfo_attr_reader :format_profile, 'Format_Profile'
9
+ mediainfo_attr_reader :format_info, 'Format_Info'
10
+ mediainfo_attr_reader :codec, 'Codec'
11
+ mediainfo_attr_reader :overall_bit_rate, 'OverallBitRate'
12
+ mediainfo_attr_reader :encoded_application, 'Encoded_Application'
13
+ alias_method :writing_application, :encoded_application
14
+ mediainfo_attr_reader :encoded_library, 'Encoded_Library'
15
+ alias_method :writing_library, :encoded_library
16
+
17
+ mediainfo_date_reader :mastered_date, 'Mastered_Date'
18
+ mediainfo_date_reader :tagged_date, 'Tagged_Date'
19
+ mediainfo_date_reader :encoded_date, 'Encoded_Date'
20
+ mediainfo_date_reader :last_modification_date, 'File_Modified_Date'
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ module MediaInfoNative
2
+ class ImageStream < BaseStream
3
+ mediainfo_attr_reader :resolution, 'Resolution'
4
+ mediainfo_attr_reader :format, 'Format'
5
+
6
+ mediainfo_int_reader :width, 'Width'
7
+ mediainfo_int_reader :height, 'Height'
8
+
9
+ def frame_size
10
+ "#{width}x#{height}" if width && height
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module MediaInfoNative
2
+ class MenuStream < BaseStream
3
+ mediainfo_attr_reader :stream_id, 'ID'
4
+ mediainfo_date_reader :encoded_date, 'Encoded_Date'
5
+ mediainfo_date_reader :tagged_date, 'Tagged_Date'
6
+ mediainfo_int_reader :delay, 'Delay'
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module MediaInfoNative
2
+ class OtherStream < BaseStream
3
+ mediainfo_attr_reader :stream_id, 'ID'
4
+ mediainfo_attr_reader :type, 'Type'
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module MediaInfoNative
2
+ class TextStream < BaseStream
3
+ mediainfo_attr_reader :stream_id, 'ID'
4
+ mediainfo_attr_reader :format, 'Format'
5
+ mediainfo_attr_reader :codec_id, 'CodecID'
6
+ mediainfo_attr_reader :codec_info, 'CodecID/Info'
7
+ end
8
+ end