mediainfo-native 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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