marcandre-flvedit 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +5 -0
- data/LICENSE +24 -0
- data/README.rdoc +90 -0
- data/Rakefile +131 -0
- data/VERSION.yml +4 -0
- data/bin/flvedit +14 -0
- data/lib/flv.rb +24 -0
- data/lib/flv/audio.rb +66 -0
- data/lib/flv/base.rb +38 -0
- data/lib/flv/body.rb +57 -0
- data/lib/flv/edit.rb +20 -0
- data/lib/flv/edit/options.rb +162 -0
- data/lib/flv/edit/processor.rb +3 -0
- data/lib/flv/edit/processor/add.rb +67 -0
- data/lib/flv/edit/processor/base.rb +209 -0
- data/lib/flv/edit/processor/command_line.rb +23 -0
- data/lib/flv/edit/processor/cut.rb +27 -0
- data/lib/flv/edit/processor/debug.rb +30 -0
- data/lib/flv/edit/processor/head.rb +16 -0
- data/lib/flv/edit/processor/join.rb +52 -0
- data/lib/flv/edit/processor/meta_data_maker.rb +127 -0
- data/lib/flv/edit/processor/print.rb +13 -0
- data/lib/flv/edit/processor/printer.rb +27 -0
- data/lib/flv/edit/processor/reader.rb +30 -0
- data/lib/flv/edit/processor/save.rb +28 -0
- data/lib/flv/edit/processor/update.rb +27 -0
- data/lib/flv/edit/runner.rb +23 -0
- data/lib/flv/edit/version.rb +15 -0
- data/lib/flv/event.rb +40 -0
- data/lib/flv/file.rb +41 -0
- data/lib/flv/header.rb +37 -0
- data/lib/flv/packing.rb +140 -0
- data/lib/flv/tag.rb +62 -0
- data/lib/flv/timestamp.rb +124 -0
- data/lib/flv/util/double_check.rb +22 -0
- data/lib/flv/video.rb +73 -0
- data/test/fixtures/corrupted.flv +0 -0
- data/test/fixtures/short.flv +0 -0
- data/test/fixtures/tags.xml +39 -0
- data/test/test_flv.rb +145 -0
- data/test/test_flv_edit.rb +32 -0
- data/test/test_flv_edit_results.rb +27 -0
- data/test/test_helper.rb +9 -0
- data/test/text_flv_edit_results/add_tags.txt +132 -0
- data/test/text_flv_edit_results/cut_from.txt +114 -0
- data/test/text_flv_edit_results/cut_key.txt +20 -0
- data/test/text_flv_edit_results/debug.txt +132 -0
- data/test/text_flv_edit_results/debug_limited.txt +18 -0
- data/test/text_flv_edit_results/debug_range.txt +32 -0
- data/test/text_flv_edit_results/join.txt +237 -0
- data/test/text_flv_edit_results/print.txt +16 -0
- data/test/text_flv_edit_results/stop.txt +38 -0
- data/test/text_flv_edit_results/update.txt +33 -0
- metadata +134 -0
data/CHANGELOG.rdoc
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
FLVEdit - Copyright (c) 2009 Marc-André Lafortune
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions
|
6
|
+
are met:
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
3. The name of the author may not be used to endorse or promote products
|
13
|
+
derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
16
|
+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
17
|
+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
18
|
+
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
19
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
20
|
+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
22
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
24
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
= FLVEdit - Flash video manipulation
|
2
|
+
|
3
|
+
FLVEdit is a tool that will read or edit FLV files to:
|
4
|
+
* generate meta data
|
5
|
+
* add/remove cue points
|
6
|
+
* join files
|
7
|
+
* inspect files
|
8
|
+
|
9
|
+
It is meant as an improved FLVTool2, fixing the shortfalls that prompted others to write FLVTool++ & FLVMeta (see comparison chart)
|
10
|
+
|
11
|
+
FLVEdit can be used either as a command-line command or as a ruby library.
|
12
|
+
|
13
|
+
== This is not a stable release
|
14
|
+
<b>Warning:</b> The basic functionality is there, but I'll be improving features and documentation. Until version 1.0, there will most likely be many changes to the command line interface and the library API.
|
15
|
+
|
16
|
+
Comments & requests welcome...
|
17
|
+
|
18
|
+
== Installation
|
19
|
+
|
20
|
+
+flvedit+ is a gem mirrored on Rubyforge and can thus be installed with:
|
21
|
+
|
22
|
+
sudo gem install flvedit
|
23
|
+
|
24
|
+
flvedit is compatible with Ruby 1.8 and 1.9
|
25
|
+
|
26
|
+
== Command-line tool
|
27
|
+
|
28
|
+
Type 'flvedit' for description of commands.
|
29
|
+
|
30
|
+
== Library
|
31
|
+
|
32
|
+
FLVEdit is written in ruby and divided in two layers.
|
33
|
+
|
34
|
+
=== FLV file format
|
35
|
+
The FLV layer handles the FLV file format. It makes reading and writing FLV files a breeze:
|
36
|
+
|
37
|
+
FLV::File.open("example.flv") {|f| f.to_a } # ==> [<#FLV::Header...>, <#FLV::Tag...>, ...]
|
38
|
+
|
39
|
+
The main class is FLV::Tag with its different possible bodies: FLV::Audio, FLV::Video and most importantly FLV::Event for all meta data related information (onMetaData, onCuePoint, ...)
|
40
|
+
|
41
|
+
The data packing and unpacking relies on the packable[http://github.com/marcandre/packable] library.
|
42
|
+
|
43
|
+
=== FLV::Edit tool
|
44
|
+
|
45
|
+
The FLV::Edit layer is the command-line tool itself. The FLV::Edit::Runner class parses the options and builds a chain of processors to apply to some flv files. Processors all derive from FLV::Edit::Processor::Base and only need to specify what to do for the type of data it wants to process. A simplistic example to use this level:
|
46
|
+
|
47
|
+
class CountCuePoints < FLV::Edit::Processor::Base
|
48
|
+
attr_writer :count
|
49
|
+
def on_cue_point(cue)
|
50
|
+
@count ||= 0
|
51
|
+
@count += 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Call manually
|
56
|
+
count =
|
57
|
+
FLV::File.open("example.flv") do |f|
|
58
|
+
CountCuePoints.new(f).process_all.count
|
59
|
+
end
|
60
|
+
|
61
|
+
# Chain with other commands:
|
62
|
+
count = FLV::Edit::Runner.new([CountCuePoints, FLV::Edit::Processor::Debug], :files => "example.swf").run.count
|
63
|
+
|
64
|
+
See FLV::Edit::Processor::Base for details on how the processing works
|
65
|
+
|
66
|
+
== Comparisons with existing tools
|
67
|
+
|
68
|
+
=== FLVTool2
|
69
|
+
|
70
|
+
Features:
|
71
|
+
* Can join (concat) flv files
|
72
|
+
* Won't load the whole files in memory all at once
|
73
|
+
* Won't choke on read-only files
|
74
|
+
* Supports extented timestamps (for flv over... 4 hours!)
|
75
|
+
|
76
|
+
Code:
|
77
|
+
* Complete rewrite
|
78
|
+
* More ruby-oriented
|
79
|
+
* Commented
|
80
|
+
* Unit tests
|
81
|
+
* Easier to use library
|
82
|
+
* Easily expandable with your own processing
|
83
|
+
|
84
|
+
=== Compared to FLVTool++
|
85
|
+
* Handles cue points
|
86
|
+
* Usable as a library
|
87
|
+
<to be completed>
|
88
|
+
|
89
|
+
=== Compared to FLVMeta
|
90
|
+
<to be completed>
|
data/Rakefile
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rcov/rcovtask'
|
6
|
+
|
7
|
+
# bench
|
8
|
+
begin
|
9
|
+
desc "Benchmark"
|
10
|
+
task :bench do
|
11
|
+
require File.dirname(__FILE__)+"/lib/flv/edit"
|
12
|
+
SHORT_FLV = File.dirname(__FILE__) + "/test/fixtures/short.flv"
|
13
|
+
require 'benchmark'
|
14
|
+
include Benchmark
|
15
|
+
runner = FLV::Edit::Runner.new([SHORT_FLV, SHORT_FLV, '--Join'])
|
16
|
+
bm(6) do |x|
|
17
|
+
x.report("test") { 20.times { runner.run } }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'jeweler'
|
25
|
+
Jeweler::Tasks.new do |gem|
|
26
|
+
gem.name = "flvedit"
|
27
|
+
gem.summary = "Command line tool & library to handle FLV files"
|
28
|
+
gem.email = "github@marc-andre.ca"
|
29
|
+
gem.homepage = "http://github.com/marcandre/flvedit"
|
30
|
+
gem.description = <<-EOS
|
31
|
+
flvedit allows you to:
|
32
|
+
* compute metadata for FLV files
|
33
|
+
* merge, split or cut FLVs
|
34
|
+
* insert / remote cue points or other events
|
35
|
+
|
36
|
+
flvedit is meant as a replacement for FLVTool2, FLVMeta, FLVTool++
|
37
|
+
It can be used as a command line tool or as a Ruby library.
|
38
|
+
EOS
|
39
|
+
gem.authors = ["Marc-André Lafortune"]
|
40
|
+
gem.rubyforge_project = "flvedit"
|
41
|
+
gem.add_dependency "packable", ">=1.2"
|
42
|
+
gem.add_dependency "backports"
|
43
|
+
gem.has_rdoc = true
|
44
|
+
gem.rdoc_options << '--title' << 'FLV::Edit' <<
|
45
|
+
'--main' << 'README.rdoc' <<
|
46
|
+
'--line-numbers' << '--inline-source'
|
47
|
+
end
|
48
|
+
rescue LoadError
|
49
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
50
|
+
end unless RUBY_VERSION >= "1.9"
|
51
|
+
|
52
|
+
Rake::TestTask.new do |t|
|
53
|
+
t.libs << 'lib'
|
54
|
+
t.pattern = 'test/**/test_*.rb'
|
55
|
+
t.verbose = false
|
56
|
+
end
|
57
|
+
|
58
|
+
Rake::RDocTask.new do |rdoc|
|
59
|
+
rdoc.rdoc_dir = 'rdoc'
|
60
|
+
rdoc.title = 'packable'
|
61
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
62
|
+
rdoc.rdoc_files.include('README*')
|
63
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
64
|
+
end
|
65
|
+
|
66
|
+
Rcov::RcovTask.new do |t|
|
67
|
+
t.libs << 'test'
|
68
|
+
t.test_files = FileList['test/**/*_test.rb']
|
69
|
+
t.verbose = true
|
70
|
+
end
|
71
|
+
|
72
|
+
task :default => :rcov
|
73
|
+
|
74
|
+
# stats
|
75
|
+
begin
|
76
|
+
gem 'rails'
|
77
|
+
require 'code_statistics'
|
78
|
+
namespace :spec do
|
79
|
+
desc "Use Rails's rake:stats task for a gem"
|
80
|
+
task :statsetup do
|
81
|
+
class CodeStatistics
|
82
|
+
def calculate_statistics
|
83
|
+
@pairs.inject({}) do |stats, pair|
|
84
|
+
if 3 == pair.size
|
85
|
+
stats[pair.first] = calculate_directory_statistics(pair[1], pair[2]); stats
|
86
|
+
else
|
87
|
+
stats[pair.first] = calculate_directory_statistics(pair.last); stats
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
::STATS_DIRECTORIES = [['Libraries', 'lib', /.(sql|rhtml|erb|rb|yml)$/],
|
93
|
+
['Tests', 'test', /.(sql|rhtml|erb|rb|yml)$/]]
|
94
|
+
::CodeStatistics::TEST_TYPES << "Tests"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
desc "Report code statistics (KLOCs, etc) from the application"
|
98
|
+
task :stats => "spec:statsetup" do
|
99
|
+
CodeStatistics.new(*STATS_DIRECTORIES).to_s
|
100
|
+
end
|
101
|
+
rescue Gem::LoadError => le
|
102
|
+
task :stats do
|
103
|
+
raise RuntimeError, "‘rails’ gem not found - you must install it in order to use this task.n"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
begin
|
108
|
+
require 'rake/contrib/sshpublisher'
|
109
|
+
namespace :rubyforge do
|
110
|
+
|
111
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
112
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
113
|
+
|
114
|
+
namespace :release do
|
115
|
+
desc "Publish RDoc to RubyForge."
|
116
|
+
task :docs => [:rdoc] do
|
117
|
+
config = YAML.load(
|
118
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
119
|
+
)
|
120
|
+
|
121
|
+
host = "#{config['username']}@rubyforge.org"
|
122
|
+
remote_dir = "/var/www/gforge-projects/flvedit/"
|
123
|
+
local_dir = 'rdoc'
|
124
|
+
|
125
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
rescue LoadError
|
130
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
131
|
+
end
|
data/VERSION.yml
ADDED
data/bin/flvedit
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright (c) 2009 Marc-Andre Lafortune
|
5
|
+
# Release under the (modified) BSD License (see gem's LICENSE file)
|
6
|
+
#++
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'flv/edit'
|
10
|
+
rescue LoadError
|
11
|
+
require 'rubygems'
|
12
|
+
require 'flv/edit'
|
13
|
+
end
|
14
|
+
FLV::Edit::Runner.new(ARGV).run
|
data/lib/flv.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'backports'
|
3
|
+
|
4
|
+
# utilities
|
5
|
+
require_relative 'flv/util/double_check'
|
6
|
+
|
7
|
+
# packing of FLV objects:
|
8
|
+
require 'packable'
|
9
|
+
require_relative 'flv/packing'
|
10
|
+
require_relative 'flv/base'
|
11
|
+
|
12
|
+
# FLV body of tags
|
13
|
+
require_relative 'flv/body'
|
14
|
+
require_relative 'flv/audio'
|
15
|
+
require_relative 'flv/video'
|
16
|
+
require_relative 'flv/event'
|
17
|
+
|
18
|
+
# FLV chunks (tags & header)
|
19
|
+
require_relative 'flv/timestamp'
|
20
|
+
require_relative 'flv/tag'
|
21
|
+
require_relative 'flv/header'
|
22
|
+
|
23
|
+
# finally:
|
24
|
+
require_relative 'flv/file'
|
data/lib/flv/audio.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module FLV
|
2
|
+
# The body of an audio tag.
|
3
|
+
# The data is quite complex stuff. We make no attempt to understand it all
|
4
|
+
# or to be able to modify any of it. We simply consider it a complex string
|
5
|
+
# and read the interesting bits to give more info.
|
6
|
+
class Audio < String
|
7
|
+
include Body
|
8
|
+
|
9
|
+
FORMATS = Hash.new{|h, key| "Unknown audio format: #{key}"}.merge(
|
10
|
+
0 => :"Linear PCM, platform endian" ,
|
11
|
+
1 => :ADPCM ,
|
12
|
+
2 => :MP3 ,
|
13
|
+
3 => :"Linear PCM, little endian" ,
|
14
|
+
4 => :"Nellymoser 16-kHz mono" ,
|
15
|
+
5 => :"Nellymoser 8-kHz mono" ,
|
16
|
+
6 => :Nellymoser ,
|
17
|
+
7 => :"G.711 A-law logarithmic PCM" ,
|
18
|
+
8 => :"G.711 mu-law logarithmic PCM" ,
|
19
|
+
10 => :AAC ,
|
20
|
+
11 => :Speex ,
|
21
|
+
14 => :"MP3 8-kHz" ,
|
22
|
+
15 => :"Device-specific sound"
|
23
|
+
).freeze
|
24
|
+
|
25
|
+
EXCEPTIONS = Hash.new({}).merge(
|
26
|
+
:"Nellymoser 8-kHz mono" => {:channel => :mono, :rate => 8000},
|
27
|
+
:"Nellymoser 16-kHz mono" => {:channel => :mono, :rate => 16000},
|
28
|
+
:AAC => {:channel => :stereo,:rate => 44000},
|
29
|
+
:"MP3 8-kHz" => {:rate => 8000}
|
30
|
+
).freeze
|
31
|
+
|
32
|
+
CHANNELS = { 0 => :mono, 1 => :stereo}.freeze
|
33
|
+
|
34
|
+
def codec_id
|
35
|
+
read_bits(0..3)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the format (see Audio::FORMATS for list)
|
39
|
+
def format
|
40
|
+
FORMATS[codec_id]
|
41
|
+
end
|
42
|
+
|
43
|
+
# returns :mono or :stereo
|
44
|
+
def channel
|
45
|
+
EXCEPTIONS[format][:channel] ||
|
46
|
+
CHANNELS[read_bits(7)]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the sampling rate (in Hz)
|
50
|
+
def rate
|
51
|
+
EXCEPTIONS[format][:rate] ||
|
52
|
+
5500 << read_bits(4..5)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the sample size (in bits)
|
56
|
+
def sample_size
|
57
|
+
EXCEPTIONS[format][:sample_size] ||
|
58
|
+
8 << read_bits(6)
|
59
|
+
end
|
60
|
+
|
61
|
+
def is?(what)
|
62
|
+
format.to_s.downcase == what.to_s.downcase || super
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
data/lib/flv/base.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module FLV
|
2
|
+
# Common fonctionality to both FLV::Header, FLV::Tag & FLV::Body
|
3
|
+
module Base
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
include Packable
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# returns the instance methods
|
11
|
+
# that are proper (i.e. not redefinitions)
|
12
|
+
# and that don't require any argument.
|
13
|
+
def getters(of=self)
|
14
|
+
of.class.ancestors.
|
15
|
+
map{|k| k.instance_methods(false)}.
|
16
|
+
inject{|proper, methods| proper -= methods}. # tricky: first ancestor is of.class, so that's what we start with
|
17
|
+
select{|m| of.class.instance_method(m).arity.between?(-1,0)}
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h(attributes = getters)
|
21
|
+
Hash[attributes.map do |a|
|
22
|
+
a = a.to_s.delete("@").to_sym
|
23
|
+
[a, send(a)]
|
24
|
+
end]
|
25
|
+
end
|
26
|
+
|
27
|
+
def is?(what)
|
28
|
+
kn = self.class.name.downcase
|
29
|
+
[kn, kn.sub("flv::","")].include?(what.to_s.downcase)
|
30
|
+
end
|
31
|
+
|
32
|
+
def size
|
33
|
+
StringIO.new.packed.write(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/flv/body.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module FLV
|
2
|
+
|
3
|
+
# Common fonctionality to all types of Bodies (FLV::Audio, FLV::Video & FLV::Event)
|
4
|
+
module Body
|
5
|
+
def self.included(base)
|
6
|
+
# Caution: order is important; InstanceMethods::is? relies on Base::is?
|
7
|
+
base.class_eval do
|
8
|
+
include Base
|
9
|
+
include InstanceMethods
|
10
|
+
include InstanceMethodsWhenString
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module InstanceMethods # :nodoc:
|
15
|
+
def debug(format, *) #:nodoc
|
16
|
+
format.values(to_h)
|
17
|
+
end
|
18
|
+
|
19
|
+
def is?(what)
|
20
|
+
case what
|
21
|
+
when String, Symbol
|
22
|
+
super(what.to_s.downcase.gsub!(/_tag$/, "") || :never_match_on_class_name_unless_string_ends_with_tag)
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def similar_to?(other_body)
|
29
|
+
getters.each{|getter| return false unless send(getter) == other_body.send(getter)}
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def title
|
34
|
+
self.class.name + " tag"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module InstanceMethodsWhenString # :nodoc:
|
39
|
+
# Returns an +Integer+ computed from bits specified by +which+.
|
40
|
+
# The 0th bit is the most significant bit of the first character.
|
41
|
+
# +which+ can designate a range of bits or a single bit
|
42
|
+
def read_bits(which)
|
43
|
+
which = which..which if which.is_a? Integer
|
44
|
+
first_byte, last_byte = which.first >> 3, which.max >> 3
|
45
|
+
return (getbyte(first_byte) >> (7 & ~which.max)) & ~(~1 << which.max-which.first) if(first_byte == last_byte)
|
46
|
+
mid = last_byte << 3
|
47
|
+
read_bits(which.first...mid) << (which.max - mid + 1) | read_bits(mid..which.max)
|
48
|
+
end
|
49
|
+
|
50
|
+
# We need to redefine this, since we want to end up with a Body, not a String
|
51
|
+
def read_packed(io, options) #:nodoc:
|
52
|
+
replace(io.read(String, options))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|