ffmpeg-lemon-info 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/.gitignore +55 -0
- data/.travis.yml +22 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +44 -0
- data/README.rdoc +13 -0
- data/Rakefile +33 -0
- data/ext/ffmpeg_video_info_ext/extconf.rb +31 -0
- data/ext/ffmpeg_video_info_ext/ffmpeg_video_info_ext.c +341 -0
- data/ffmpeg-lemon-info.gemspec +29 -0
- data/lib/ffmpeg_video_info/version.rb +5 -0
- data/lib/ffmpeg_video_info.rb +8 -0
- data/spec/fixtures/test.mp4 +0 -0
- data/spec/lib/info_spec.rb +20 -0
- data/spec/spec_helper.rb +4 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8eb6d1c937ded5ef36aa5d165d47b80e2aca2eeb55619d9de2884587066a1943
|
4
|
+
data.tar.gz: 3dbed832af943dac94ca2b7405dd573a06df85295692401ad150027f6744583b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9b0fa98630634de7dc9ae3f587518403b1f4683200e12c1c7f6a428f54db7e56399d09effc0fcea846cf13dd50f8ff37655409850c8b51114daa92eceec05fd3
|
7
|
+
data.tar.gz: 510b40ab3ace2551d026887a4c7acb10f6c2053229a30e19fc6ca5aa26f5332b21337fafece3109759f560ea538e1bcd9ff3af7d8f06e6424651dfebdee540dc
|
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
coverage.data
|
4
|
+
|
5
|
+
# rdoc generated
|
6
|
+
rdoc
|
7
|
+
|
8
|
+
# yard generated
|
9
|
+
doc
|
10
|
+
.yardoc
|
11
|
+
|
12
|
+
# bundler
|
13
|
+
.bundle
|
14
|
+
|
15
|
+
# jeweler generated
|
16
|
+
pkg
|
17
|
+
|
18
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
19
|
+
#
|
20
|
+
# * Create a file at ~/.gitignore
|
21
|
+
# * Include files you want ignored
|
22
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
23
|
+
#
|
24
|
+
# After doing this, these files will be ignored in all your git projects,
|
25
|
+
# saving you from having to 'pollute' every project you touch with them
|
26
|
+
#
|
27
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
28
|
+
|
29
|
+
# For MacOS:
|
30
|
+
.DS_Store
|
31
|
+
|
32
|
+
# For TextMate
|
33
|
+
*.tmproj
|
34
|
+
tmtags
|
35
|
+
|
36
|
+
# For emacs:
|
37
|
+
*~
|
38
|
+
\#*
|
39
|
+
.\#*
|
40
|
+
|
41
|
+
# For vim:
|
42
|
+
*.swp
|
43
|
+
|
44
|
+
# For redcar:
|
45
|
+
.redcar
|
46
|
+
|
47
|
+
# For rubinius:
|
48
|
+
*.rbc
|
49
|
+
|
50
|
+
# temp files
|
51
|
+
tmp
|
52
|
+
lib/ffmpeg_video_info_ext.bundle
|
53
|
+
.ffmpeg*
|
54
|
+
ports
|
55
|
+
*.gem
|
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
language: ruby
|
3
|
+
sudo: false
|
4
|
+
rvm:
|
5
|
+
- 1.9.3
|
6
|
+
- 2.0.0
|
7
|
+
- 2.1.5
|
8
|
+
- 2.2.2
|
9
|
+
- 2.3.0
|
10
|
+
os:
|
11
|
+
- linux
|
12
|
+
- osx
|
13
|
+
|
14
|
+
matrix:
|
15
|
+
allow_failures:
|
16
|
+
# https://github.com/travis-ci/travis-ci/issues/5361
|
17
|
+
- os: osx
|
18
|
+
rvm: 2.3.0
|
19
|
+
|
20
|
+
env:
|
21
|
+
global:
|
22
|
+
- CODECLIMATE_REPO_TOKEN=f3628d3936051e0bc676f02ada2a53bb5388cad4b9d23ec17c11306e30b8ebe7
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
codeclimate-test-reporter (0.4.8)
|
5
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
6
|
+
diff-lcs (1.2.5)
|
7
|
+
docile (1.1.5)
|
8
|
+
json (1.8.3)
|
9
|
+
mini_portile2 (2.1.0)
|
10
|
+
rake (10.5.0)
|
11
|
+
rake-compiler (0.9.3)
|
12
|
+
rake
|
13
|
+
rdoc (4.2.1)
|
14
|
+
rspec (3.1.0)
|
15
|
+
rspec-core (~> 3.1.0)
|
16
|
+
rspec-expectations (~> 3.1.0)
|
17
|
+
rspec-mocks (~> 3.1.0)
|
18
|
+
rspec-core (3.1.7)
|
19
|
+
rspec-support (~> 3.1.0)
|
20
|
+
rspec-expectations (3.1.2)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.1.0)
|
23
|
+
rspec-mocks (3.1.3)
|
24
|
+
rspec-support (~> 3.1.0)
|
25
|
+
rspec-support (3.1.2)
|
26
|
+
simplecov (0.11.2)
|
27
|
+
docile (~> 1.1.0)
|
28
|
+
json (~> 1.8)
|
29
|
+
simplecov-html (~> 0.10.0)
|
30
|
+
simplecov-html (0.10.0)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
bundler
|
37
|
+
codeclimate-test-reporter
|
38
|
+
mini_portile2
|
39
|
+
rake-compiler
|
40
|
+
rdoc
|
41
|
+
rspec
|
42
|
+
|
43
|
+
BUNDLED WITH
|
44
|
+
1.11.2
|
data/README.rdoc
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
= lemon-ffmpeg
|
2
|
+
|
3
|
+
Ruby binding for FFmpeg library
|
4
|
+
|
5
|
+
== Contributing to lemon-ffmpeg
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'rake/extensiontask'
|
4
|
+
require './lib/ffmpeg_video_info'
|
5
|
+
require 'respec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
begin
|
9
|
+
Bundler.setup(:default, :development)
|
10
|
+
rescue Bundler::BundlerError => e
|
11
|
+
$stderr.puts e.message
|
12
|
+
$stderr.puts 'Run `bundle install` to install missing gems'
|
13
|
+
exit e.status_code
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'rake'
|
17
|
+
|
18
|
+
require 'rdoc/task'
|
19
|
+
Rake::RDocTask.new do |rdoc|
|
20
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
21
|
+
|
22
|
+
rdoc.rdoc_dir = 'rdoc'
|
23
|
+
rdoc.title = "ffmpeg-video-info ${version}"
|
24
|
+
rdoc.rdoc_files.include('README*')
|
25
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
26
|
+
end
|
27
|
+
|
28
|
+
task :compile => [:'compile:ffmpeg_video_info_ext']
|
29
|
+
task :spec => [:compile]
|
30
|
+
task :default => :spec
|
31
|
+
|
32
|
+
require 'rake/extensiontask'
|
33
|
+
Rake::ExtensionTask.new('ffmpeg_video_info_ext')
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
require 'fileutils'
|
3
|
+
begin
|
4
|
+
gem 'mini_portile2'
|
5
|
+
rescue Gem::LoadError
|
6
|
+
message `MiniPortile already activated`
|
7
|
+
end
|
8
|
+
require 'mini_portile2'
|
9
|
+
message "Using mini_portile version #{MiniPortile::VERSION}\n"
|
10
|
+
recipe = MiniPortile.new('FFmpeg', '2.7.1')
|
11
|
+
file = "https://ffmpeg.org/releases/ffmpeg-#{recipe.version}.tar.gz"
|
12
|
+
message "FFmpeg source url #{file}\n"
|
13
|
+
recipe.files = [file]
|
14
|
+
recipe.configure_options = ['--enable-shared', '--disable-yasm']
|
15
|
+
checkpoint = ".#{recipe.name}-#{recipe.version}.installed"
|
16
|
+
unless File.exist?(checkpoint)
|
17
|
+
recipe.cook
|
18
|
+
FileUtils.touch(checkpoint)
|
19
|
+
end
|
20
|
+
recipe.activate
|
21
|
+
|
22
|
+
$CXXFLAGS = '' if $CXXFLAGS.nil?
|
23
|
+
$INCFLAGS << " -I#{recipe.path}/include"
|
24
|
+
$CXXFLAGS << " -fPIC"
|
25
|
+
$LDFLAGS ,, " -Wl,-rpath,#{File.expand_path(File.dirname(__FILE__))}"
|
26
|
+
$LIBPATH = ["#{recipe.path}/lib"] | $LIBPATH
|
27
|
+
|
28
|
+
have_library('avcodec') or raise
|
29
|
+
have_library('avformat') or raise
|
30
|
+
|
31
|
+
create_makefile('ffmpeg_video_info_ext')
|
@@ -0,0 +1,341 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <libavcodec/avcodec.h>
|
3
|
+
#include <libavformat/avformat.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <libavutil/opt.h>
|
6
|
+
#include <libavutil/dict.h>
|
7
|
+
#include <libavutil/pixdesc.h>
|
8
|
+
|
9
|
+
void my_dump_metadata(void *ctx, AVDictionary *m, const char *indent, VALUE res)
|
10
|
+
{
|
11
|
+
if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0))) {
|
12
|
+
AVDictionaryEntry *tag=NULL;
|
13
|
+
|
14
|
+
while((tag=av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
15
|
+
if(strcmp("language", tag->key)) {
|
16
|
+
const char *p = tag->value;
|
17
|
+
while(*p) {
|
18
|
+
char tmp[256];
|
19
|
+
size_t len = strcspn(p, "\x8\xa\xb\xc\xd");
|
20
|
+
av_strlcpy(tmp, p, FFMIN(sizeof(tmp), len+1));
|
21
|
+
rb_hash_aset(res, rb_str_new2(tag->key), rb_str_new2(tmp));
|
22
|
+
p += len;
|
23
|
+
if (*p) p++;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
void my_avcodec_string(AVCodecContext *enc, int encode, VALUE res)
|
31
|
+
{
|
32
|
+
const char *codec_type;
|
33
|
+
const char *codec_name;
|
34
|
+
const char *profile = NULL;
|
35
|
+
const AVCodec *p;
|
36
|
+
inst bitrate;
|
37
|
+
AVRational display_aspect_ratio;
|
38
|
+
|
39
|
+
codec_type = av_get_media_type_string(enc->codec_type);
|
40
|
+
codec_name = avcodec_get_name(enc->codec_id);
|
41
|
+
if (enc->profile != FF_PROFILE_UNKNOWN) {
|
42
|
+
if (enc->codec)
|
43
|
+
p = enc->codec;
|
44
|
+
else
|
45
|
+
p = encode ? avcodec_find_encoder(enc->codec_id) :
|
46
|
+
avcodec_find_decoder(enc->codec_id);
|
47
|
+
if (p)
|
48
|
+
profile = av_get_profile_name(p, enc->profile);
|
49
|
+
}
|
50
|
+
|
51
|
+
rb_hash_aset(res, rb_str_new2("type"), rb_str_new2(codec_type ? codec_type : "unknown"));
|
52
|
+
rb_hash_aset(res, rb_str_new2("name"), rb_str_new2(codec_name));
|
53
|
+
|
54
|
+
if (enc->codec && strcmp(enc->codec->name, codec_name))
|
55
|
+
rb_hash_aset(res, rb_str_new2("codec_name"), rb_str_new2(enc->codec->name));
|
56
|
+
|
57
|
+
if (profile)
|
58
|
+
rb_hash_aset(res, rb_str_new2("profile"), rb_str_new2(profile));
|
59
|
+
if (enc->codec_tag) {
|
60
|
+
char tag_buf[32];
|
61
|
+
av_get_codec_tag_string(tag_buf, sizeof(tag_buf), enc->codec_tag);
|
62
|
+
rb_hash_aset(res, rb_str_new2("codec_tag"), rb_str_new2(tag_buf));
|
63
|
+
}
|
64
|
+
|
65
|
+
switch (enc->codec_type) {
|
66
|
+
case AVMEDIA_TYPE_VIDEO:
|
67
|
+
// if (enc->pix_fmt != AV_PIX_FMT_NONE) {
|
68
|
+
// const char *colorspace_name;
|
69
|
+
// rb_hash_aset(res, rb_str_new2("pixel_format"), rb_str_new2(av_get_pix_fmt_name(enc->pix_fmt)));
|
70
|
+
// if (enc->bits_per_raw_sample &&
|
71
|
+
// enc->bits_per_raw_sample <= my_av_pix_fmt_desc_get(enc->pix_fmt)->comp[0].depth_minus1)
|
72
|
+
// av_strlcatf(detail, sizeof(detail), "%d bpc, ", enc->bits_per_raw_sample);
|
73
|
+
// if (enc->color_range != AVCOL_RANGE_UNSPECIFIED)
|
74
|
+
// av_strlcatf(detail, sizeof(detail),
|
75
|
+
// enc->color_range == AVCOL_RANGE_MPEG ? "tv, ": "pc, ");
|
76
|
+
// colorspace_name = av_get_colorspace_name(enc->colorspace);
|
77
|
+
// if (colorspace_name)
|
78
|
+
// rb_hash_aset(res, rb_str_new2("color_space"), rb_str_new2(colorspace_name));
|
79
|
+
// }
|
80
|
+
if (enc->width) {
|
81
|
+
rb_hash_aset(res, rb_str_new2("width"), INT2FIX(enc->width));
|
82
|
+
rb_hash_aset(res, rb_str_new2("height"), INT2FIX(enc->height));
|
83
|
+
if (enc->sample_aspect_ratio.num) {
|
84
|
+
av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
|
85
|
+
enc->width * enc->sample_aspect_ratio.num,
|
86
|
+
enc->height * enc->sample_aspect_ratio.den,
|
87
|
+
1024 * 1024);
|
88
|
+
VALUE sar = rb_hash_new();
|
89
|
+
rb_hash_aset(dar, rb_str_new2("width"), INT2FIX(display_aspect_ratio.num));
|
90
|
+
rb_hash_aset(dar, rb_str_new2("height"), INT2FIX(display_aspect_ratio.den));
|
91
|
+
rb_hash_aset(res, rb_str_new2("SAR"), dar);
|
92
|
+
}
|
93
|
+
// if (av_log_get_level() >= AV_LOG_DEBUG) {
|
94
|
+
// int g = av_gcd(enc->time_base.num, enc->time_base.den);
|
95
|
+
// snprintf(buf + strlen(buf), buf_size - strlen(buf),
|
96
|
+
// ", %d/%d",
|
97
|
+
// enc->time_base.num / g, enc->time_base.den / g);
|
98
|
+
// }
|
99
|
+
}
|
100
|
+
if (encode) {
|
101
|
+
rb_hash_aset(res, rb_str_new2("qmin"), INT2FIX(enc->qmin));
|
102
|
+
rb_hash_aset(res, rb_str_new2("qmax"), INT2FIX(enc->qmax));
|
103
|
+
}
|
104
|
+
break;
|
105
|
+
case AVMEDIA_TYPE_AUDIO:
|
106
|
+
if (enc->sample_rate) {
|
107
|
+
rb_hash_aset(res, rb_str_new2("sample_rate"), INT2FIX(enc->sample_rate));
|
108
|
+
}
|
109
|
+
char layout_buf[32];
|
110
|
+
av_get_channel_layout_string(layout_buf, sizeof(layout_buf), enc->channels, enc->channel_layout);
|
111
|
+
rb_hash_aset(res, rb_str_new2("channel_layout"), rb_str_new2(layout_buf));
|
112
|
+
if (enc->sample_fmt != AV_SAMPLE_FMT_NONE) {
|
113
|
+
rb_hash_aset(res, rb_str_new2("sample_format"), rb_str_new2(av_get_sample_fmt_name(enc->sample_fmt)));
|
114
|
+
}
|
115
|
+
break;
|
116
|
+
case AVMEDIA_TYPE_DATA:
|
117
|
+
// if (av_log_get_level() >= AV_LOG_DEBUG) {
|
118
|
+
// int g = av_gcd(enc->time_base.num, enc->time_base.den);
|
119
|
+
// if (g)
|
120
|
+
// snprintf(buf + strlen(buf), buf_size - strlen(buf),
|
121
|
+
// ", %d/%d",
|
122
|
+
// enc->time_base.num / g, enc->time_base.den / g);
|
123
|
+
// }
|
124
|
+
break;
|
125
|
+
case AVMEDIA_TYPE_SUBTITLE:
|
126
|
+
if (enc->width)
|
127
|
+
rb_hash_aset(res, rb_str_new2("width"), INT2FIX(enc->width));
|
128
|
+
rb_hash_aset(res, rb_str_new2("height"), INT2FIX(enc->height));
|
129
|
+
break;
|
130
|
+
default:
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
if (encode) {
|
134
|
+
if (enc->flags & CODEC_FLAG_PASS1)
|
135
|
+
rb_hash_aset(res, rb_str_new2("pass"), INT2FIX(1));
|
136
|
+
if (enc->flags & CODEC_FLAG_PASS2)
|
137
|
+
rb_hash_aset(res, rb_str_new2("pass"), INT2FIX(2));
|
138
|
+
}
|
139
|
+
bitrate = get_bit_rate(enc);
|
140
|
+
if (bitrate != 0) {
|
141
|
+
rb_hash_aset(res, rb_str_new2("bitrate"), INT2FIX(bitrate));
|
142
|
+
} else if (enc->rc_max_rate > 0) {
|
143
|
+
rb_hash_aset(res, rb_str_new2("bitrate"), INT2FIX(rnc->rc_max_rate));
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
void my_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output, VALUE res)
|
148
|
+
{
|
149
|
+
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
|
150
|
+
AVStream *st = ic->streams[i];
|
151
|
+
int g = av_gcd(st->time_base.num, st->time_base.den);
|
152
|
+
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
|
153
|
+
my_avcodec_string(st->codec, is_output, res);
|
154
|
+
/* the pid is an important information, so we display it */
|
155
|
+
/* XXX: add a generic system */
|
156
|
+
if (flags & AVFMT_SHOW_IDS)
|
157
|
+
rb_hash_aset(res, rb_str_new2("show_id"), INT2FIX(st->id));
|
158
|
+
if (lang)
|
159
|
+
rb_hash_aset(res, rb_str_new2("language"), rb_str_new2(lang->value));
|
160
|
+
if (st->sample_aspect_ratio.num && // default
|
161
|
+
av_cmp_q(st->sample_aspect_ratio, st->codec->sample_aspect_ratio)) {
|
162
|
+
AVRational display_aspect_ratio;
|
163
|
+
av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
|
164
|
+
st->codec->width*st->sample_aspect_ratio.num,
|
165
|
+
st->codec->height*st->sample_aspect_ratio.den,
|
166
|
+
1024*1024);
|
167
|
+
VALUE sar = rb_hash_new();
|
168
|
+
rb_hash_aset(sar, rb_str_new2("width"), INT2FIX(st->sample_aspect_ratio.num));
|
169
|
+
rb_hash_aset(sar, rb_str_new2("height"), INT2FIX(st->sample_aspect_ratio.den));
|
170
|
+
rb_hash_aset(res, rb_str_new2("SAR"), sar);
|
171
|
+
VALUE dar = rb_hash_new();
|
172
|
+
rb_hash_aset(dar, rb_str_new2("width"), INT2FIX(display_aspect_ratio.num));
|
173
|
+
rb_hash_aset(dar, rb_str_new2("height"), INT2FIX(display_aspect_ratio.den));
|
174
|
+
rb_hash_aset(res, rb_str_new2("SAR"), dar);
|
175
|
+
}
|
176
|
+
if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO){
|
177
|
+
if(st->avg_frame_rate.den && st->avg_frame_rate.num)
|
178
|
+
rb_hash_aset(res, rb_str_new2("fps"), INT2FIX(av_q2d(st->avg_frame_rate)));
|
179
|
+
}
|
180
|
+
if (st->disposition & AV_DISPOSITION_DEFAULT)
|
181
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("default"));
|
182
|
+
if (st->disposition & AV_DISPOSITION_DUB)
|
183
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("dub"));
|
184
|
+
if (st->disposition & AV_DISPOSITION_ORIGINAL)
|
185
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("original"));
|
186
|
+
if (st->disposition & AV_DISPOSITION_COMMENT)
|
187
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("comment"));
|
188
|
+
if (st->disposition & AV_DISPOSITION_LYRICS)
|
189
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("lyrics"));
|
190
|
+
if (st->disposition & AV_DISPOSITION_KARAOKE)
|
191
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("karaoke"));
|
192
|
+
if (st->disposition & AV_DISPOSITION_FORCED)
|
193
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("forced"));
|
194
|
+
if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
|
195
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("hearing impaired"));
|
196
|
+
if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
|
197
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("visual impaired"));
|
198
|
+
if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS)
|
199
|
+
rb_hash_aset(res, rb_str_new2("disposition"), rb_str_new2("clean effects"));
|
200
|
+
VALUE metadata = rb_hash_new();
|
201
|
+
my_dump_metadata(NULL, st->metadata, " ", metadata);
|
202
|
+
rb_hash_aset(res, rb_str_new2("metadata"), metadata);
|
203
|
+
}
|
204
|
+
|
205
|
+
void my_av_dump_format(AVFormatContext *ic, int index, const char *url, int is_output, VALUE res)
|
206
|
+
{
|
207
|
+
int i;
|
208
|
+
uint8_t *printed = ic->nb_streams ? av_mallocz(ic->nb_streams) : NULL;
|
209
|
+
if (ic->nb_streams && !printed)
|
210
|
+
return;
|
211
|
+
|
212
|
+
rb_hash_aset(res, rb_str_new2("format_name"), rb_str_new2(ic->iformat->name));
|
213
|
+
rb_hash_aset(res, rb_str_new2("file_name"), rb_str_new2(url));
|
214
|
+
VALUE metadata = rb_hash_new();
|
215
|
+
my_dump_metadata(NULL, ic->metadata, " ", metadata);
|
216
|
+
rb_hash_aset(res, rb_str_new2("metadata"), metadata);
|
217
|
+
if (!is_output) {
|
218
|
+
if (ic->duration != AV_NOPTS_VALUE) {
|
219
|
+
int hours, mins, secs, us;
|
220
|
+
int64_t duration = ic->duration + 5000;
|
221
|
+
secs = duration / AV_TIME_BASE;
|
222
|
+
us = duration % AV_TIME_BASE;
|
223
|
+
mins = secs / 60;
|
224
|
+
secs %= 60;
|
225
|
+
hours = mins / 60;
|
226
|
+
mins %= 60;
|
227
|
+
// av_log(NULL, AV_LOG_INFO, "%02d:%02d:%02d.%02d", hours, mins, secs,
|
228
|
+
// (100 * us) / AV_TIME_BASE);
|
229
|
+
rb_hash_aset(res, rb_str_new2("duration"), INT2FIX(hours*3600 + mins*60 + secs));
|
230
|
+
}
|
231
|
+
if (ic->start_time != AV_NOPTS_VALUE) {
|
232
|
+
int secs, us;
|
233
|
+
secs = ic->start_time / AV_TIME_BASE;
|
234
|
+
us = abs(ic->start_time % AV_TIME_BASE);
|
235
|
+
rb_hash_aset(res, rb_str_new2("start"), INT2FIX((int)av_rescale(us, 1000000, AV_TIME_BASE)));
|
236
|
+
}
|
237
|
+
if (ic->bit_rate) {
|
238
|
+
rb_hash_aset(res, rb_str_new2("bitrate"), INT2FIX(ic->bit_rate));
|
239
|
+
}
|
240
|
+
}
|
241
|
+
// for (i = 0; i < ic->nb_chapters; i++) {
|
242
|
+
// AVChapter *ch = ic->chapters[i];
|
243
|
+
// av_log(NULL, AV_LOG_INFO, " Chapter #%d.%d: ", index, i);
|
244
|
+
// av_log(NULL, AV_LOG_INFO, "start %f, ", ch->start * av_q2d(ch->time_base));
|
245
|
+
// av_log(NULL, AV_LOG_INFO, "end %f\n", ch->end * av_q2d(ch->time_base));
|
246
|
+
|
247
|
+
// VALUE metadata = rb_hash_new();
|
248
|
+
// my_dump_metadata(NULL, ch->metadata, " ", metadata);
|
249
|
+
// }
|
250
|
+
// if(ic->nb_programs) {
|
251
|
+
// int j, k, total = 0;
|
252
|
+
// for(j=0; j<ic->nb_programs; j++) {
|
253
|
+
// AVDictionaryEntry *name = av_dict_get(ic->programs[j]->metadata,
|
254
|
+
// "name", NULL, 0);
|
255
|
+
// av_log(NULL, AV_LOG_INFO, " Program %d %s\n", ic->programs[j]->id,
|
256
|
+
// name ? name->value : "");
|
257
|
+
// VALUE metadata = rb_hash_new();
|
258
|
+
// my_dump_metadata(NULL, ic->programs[j]->metadata, " ", metadata);
|
259
|
+
// for(k=0; k<ic->programs[j]->nb_stream_indexes; k++) {
|
260
|
+
// my_dump_stream_format(ic, ic->programs[j]->stream_index[k], index, is_output, res);
|
261
|
+
// printed[ic->programs[j]->stream_index[k]] = 1;
|
262
|
+
// }
|
263
|
+
// total += ic->programs[j]->nb_stream_indexes;
|
264
|
+
// }
|
265
|
+
// if (total < ic->nb_streams)
|
266
|
+
// av_log(NULL, AV_LOG_INFO, " No Program\n");
|
267
|
+
// }
|
268
|
+
VALUE streams = rb_ary_new();
|
269
|
+
for(i=0;i<ic->nb_streams;i++)
|
270
|
+
{
|
271
|
+
if (!printed[i])
|
272
|
+
{
|
273
|
+
VALUE stream = rb_hash_new();
|
274
|
+
my_dump_stream_format(ic, i, index, is_output, stream);
|
275
|
+
rb_ary_push(streams, stream);
|
276
|
+
}
|
277
|
+
}
|
278
|
+
rb_hash_aset(res, rb_str_new2("streams"), streams);
|
279
|
+
|
280
|
+
av_free(printed);
|
281
|
+
}
|
282
|
+
|
283
|
+
int get_bit_rate(AVCodecContext *ctx)
|
284
|
+
{
|
285
|
+
int bit_rate;
|
286
|
+
int bits_per_sample;
|
287
|
+
|
288
|
+
switch (ctx->codec_type) {
|
289
|
+
case AVMEDIA_TYPE_VIDEO:
|
290
|
+
case AVMEDIA_TYPE_DATA:
|
291
|
+
case AVMEDIA_TYPE_SUBTITLE:
|
292
|
+
case AVMEDIA_TYPE_ATTACHMENT:
|
293
|
+
bit_rate = ctx->bit_rate;
|
294
|
+
break;
|
295
|
+
case AVMEDIA_TYPE_AUDIO:
|
296
|
+
bits_per_sample = av_get_bits_per_sample(ctx->codec_id);
|
297
|
+
bit_rate = bits_per_sample ? ctx->sample_rate * ctx->channels * bits_per_sample : ctx->bit_rate;
|
298
|
+
break;
|
299
|
+
default:
|
300
|
+
bit_rate = 0;
|
301
|
+
break;
|
302
|
+
}
|
303
|
+
return bit_rate;
|
304
|
+
}
|
305
|
+
|
306
|
+
VALUE get_info(VALUE self, VALUE arg)
|
307
|
+
{
|
308
|
+
const char* file = StringValueCStr(arg);
|
309
|
+
VALUE res = rb_hash_new();
|
310
|
+
// Register all available file formats and codecs
|
311
|
+
av_register_all();
|
312
|
+
|
313
|
+
int err;
|
314
|
+
|
315
|
+
// Open video file
|
316
|
+
AVFormatContext* format_context = NULL;
|
317
|
+
err = avformat_open_input(&format_context, file, NULL, NULL);
|
318
|
+
if (err < 0) {
|
319
|
+
rb_raise(rb_eRuntimeError, "ffmpeg: Unable to open input file");
|
320
|
+
}
|
321
|
+
|
322
|
+
// Retrieve stream information
|
323
|
+
err = avformat_find_stream_info(format_context, NULL);
|
324
|
+
if (err < 0) {
|
325
|
+
rb_raise(rb_eRuntimeError, "ffmpeg: Unable to find stream info");
|
326
|
+
}
|
327
|
+
|
328
|
+
// Dump information about file onto standard error
|
329
|
+
my_av_dump_format(format_context, 0, file, 0, res);
|
330
|
+
|
331
|
+
// Close the video file
|
332
|
+
avformat_close_input(&format_context);
|
333
|
+
|
334
|
+
return res;
|
335
|
+
}
|
336
|
+
static VALUE rb_mFFmpegVideoInfo;
|
337
|
+
void Init_ffmpeg_video_info_ext()
|
338
|
+
{
|
339
|
+
rb_mFFmpegVideoInfo = rb_define_module("FFmpegVideoInfoExt");
|
340
|
+
rb_define_singleton_method(rb_mFFmpegVideoInfo, "get", get_info, 1);
|
341
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'ffmpeg_video_info/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'ffmpeg-lemon-info'
|
7
|
+
s.version = FFmpeg::Video::VERSION
|
8
|
+
s.authors = ["Lemon Rose"]
|
9
|
+
s.email = ["yash.kul69@gmail.com"]
|
10
|
+
s.summary = "Ruby binding for FFmpeg library"
|
11
|
+
s.extensions = ["ext/ffmpeg_video_info_ext/extconf.rb"]
|
12
|
+
s.homepage = "https://github.com/japandotorg/lemon-ffmpeg"
|
13
|
+
s.license = "MIT"
|
14
|
+
|
15
|
+
s.required_ruby_version = '>= 1.9.3'
|
16
|
+
|
17
|
+
s.files = `git ls-files -z`.split("\x0")
|
18
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
20
|
+
s.require_paths = ['lib']
|
21
|
+
|
22
|
+
s.add_dependency 'mini_portile2'
|
23
|
+
s.add_development_dependency 'rdoc'
|
24
|
+
s.add_development_dependency 'rice'
|
25
|
+
s.add_development_dependency 'rake'
|
26
|
+
s.add_development_dependency 'bundler'
|
27
|
+
s.add_development_dependency 'rspec'
|
28
|
+
s.add_development_dependency 'rake-compiler'
|
29
|
+
end
|
Binary file
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ffmpeg_video_info'
|
3
|
+
require 'ostruct'
|
4
|
+
describe FFmpeg::Video do
|
5
|
+
subject { FFmpeg::Video }
|
6
|
+
context 'when .info' do
|
7
|
+
context 'with wrong args' do
|
8
|
+
specify do
|
9
|
+
expect { FFmpeg::Video.info('') }.to raise_error('ffmpeg: Unable to open input file')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
context 'with proper args' do
|
13
|
+
subject { OpenStruct.new(FFmpeg::Video.info('./spec/fixtures/test.mp4')) }
|
14
|
+
it 'prints proper data' do
|
15
|
+
expect(subject.format_name).to eql 'mov,mp4,m4a,3gp,3g2,mj2'
|
16
|
+
expect(subject.file_name).to eql './spec/fixtures/test.mp4'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ffmpeg-lemon-info
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lemon Rose
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-11-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mini_portile2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rdoc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rice
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake-compiler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- yash.kul69@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions:
|
116
|
+
- ext/ffmpeg_video_info_ext/extconf.rb
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".document"
|
120
|
+
- ".gitignore"
|
121
|
+
- ".travis.yml"
|
122
|
+
- Gemfile
|
123
|
+
- Gemfile.lock
|
124
|
+
- README.rdoc
|
125
|
+
- Rakefile
|
126
|
+
- ext/ffmpeg_video_info_ext/extconf.rb
|
127
|
+
- ext/ffmpeg_video_info_ext/ffmpeg_video_info_ext.c
|
128
|
+
- ffmpeg-lemon-info.gemspec
|
129
|
+
- lib/ffmpeg_video_info.rb
|
130
|
+
- lib/ffmpeg_video_info/version.rb
|
131
|
+
- spec/fixtures/test.mp4
|
132
|
+
- spec/lib/info_spec.rb
|
133
|
+
- spec/spec_helper.rb
|
134
|
+
homepage: https://github.com/japandotorg/lemon-ffmpeg
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 1.9.3
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubygems_version: 3.2.22
|
154
|
+
signing_key:
|
155
|
+
specification_version: 4
|
156
|
+
summary: Ruby binding for FFmpeg library
|
157
|
+
test_files:
|
158
|
+
- spec/fixtures/test.mp4
|
159
|
+
- spec/lib/info_spec.rb
|
160
|
+
- spec/spec_helper.rb
|