hornetseye-ffmpeg 0.2.0 → 0.3.0
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.
- data/Rakefile +3 -1
- data/ext/avinput.cc +105 -30
- data/ext/avinput.hh +13 -4
- data/ext/avoutput.cc +233 -0
- data/ext/avoutput.hh +59 -0
- data/ext/frame.cc +19 -1
- data/ext/frame.hh +4 -0
- data/ext/init.cc +3 -0
- data/lib/hornetseye-ffmpeg/avinput.rb +4 -0
- data/lib/hornetseye-ffmpeg/avoutput.rb +36 -0
- data/lib/hornetseye_ffmpeg_ext.rb +1 -0
- metadata +6 -3
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'rake/packagetask'
|
|
6
6
|
require 'rbconfig'
|
7
7
|
|
8
8
|
PKG_NAME = 'hornetseye-ffmpeg'
|
9
|
-
PKG_VERSION = '0.
|
9
|
+
PKG_VERSION = '0.3.0'
|
10
10
|
CXX = ENV[ 'CXX' ] || 'g++'
|
11
11
|
STRIP = ENV[ 'STRIP' ] || 'strip'
|
12
12
|
RB_FILES = FileList[ 'lib/**/*.rb' ]
|
@@ -170,6 +170,8 @@ end
|
|
170
170
|
|
171
171
|
file 'ext/avinput.o' => [ 'ext/avinput.cc', 'ext/avinput.hh', 'ext/error.hh',
|
172
172
|
'ext/frame.hh' ]
|
173
|
+
file 'ext/avoutput.o' => [ 'ext/avoutput.cc', 'ext/avoutput.hh', 'ext/error.hh',
|
174
|
+
'ext/frame.hh' ]
|
173
175
|
file 'ext/frame.o' => [ 'ext/frame.cc', 'ext/frame.hh' ]
|
174
176
|
file 'ext/init.o' => [ 'ext/init.cc', 'ext/avinput.hh' ]
|
175
177
|
|
data/ext/avinput.cc
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
|
14
14
|
You should have received a copy of the GNU General Public License
|
15
15
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
+
#include <iostream>
|
16
17
|
#include "avinput.hh"
|
17
18
|
|
18
19
|
#if !defined(INT64_C)
|
@@ -26,8 +27,8 @@ using namespace std;
|
|
26
27
|
VALUE AVInput::cRubyClass = Qnil;
|
27
28
|
|
28
29
|
AVInput::AVInput( const string &mrl ) throw (Error):
|
29
|
-
m_mrl( mrl ), m_ic( NULL ),
|
30
|
-
m_pts( 0 ), m_frame( NULL )
|
30
|
+
m_mrl( mrl ), m_ic( NULL ), m_dec( NULL ), m_codec( NULL ), m_idx( -1 ),
|
31
|
+
m_pts( 0 ), m_swsContext( NULL ), m_frame( NULL )
|
31
32
|
{
|
32
33
|
try {
|
33
34
|
int err = av_open_input_file( &m_ic, mrl.c_str(), NULL, 0, NULL );
|
@@ -43,13 +44,16 @@ AVInput::AVInput( const string &mrl ) throw (Error):
|
|
43
44
|
};
|
44
45
|
ERRORMACRO( m_idx >= 0, Error, , "Could not find video stream in file \""
|
45
46
|
<< mrl << "\"" );
|
46
|
-
|
47
|
-
m_codec = avcodec_find_decoder(
|
47
|
+
m_dec = m_ic->streams[ m_idx ]->codec;
|
48
|
+
m_codec = avcodec_find_decoder( m_dec->codec_id );
|
48
49
|
ERRORMACRO( m_codec != NULL, Error, , "Could not find decoder for file \""
|
49
50
|
<< mrl << "\"" );
|
50
|
-
err = avcodec_open(
|
51
|
+
err = avcodec_open( m_dec, m_codec );
|
51
52
|
ERRORMACRO( err >= 0, Error, , "Error opening codec for file \""
|
52
53
|
<< mrl << "\": " << strerror( -err ) );
|
54
|
+
m_swsContext = sws_getContext( m_dec->width, m_dec->height, m_dec->pix_fmt,
|
55
|
+
m_dec->width, m_dec->height, PIX_FMT_YUV420P,
|
56
|
+
SWS_FAST_BILINEAR, 0, 0, 0 );
|
53
57
|
m_frame = avcodec_alloc_frame();
|
54
58
|
ERRORMACRO( m_frame, Error, , "Error allocating frame" );
|
55
59
|
} catch ( Error &e ) {
|
@@ -69,11 +73,16 @@ void AVInput::close(void)
|
|
69
73
|
av_free( m_frame );
|
70
74
|
m_frame = NULL;
|
71
75
|
};
|
76
|
+
if ( m_swsContext ) {
|
77
|
+
sws_freeContext( m_swsContext );
|
78
|
+
m_swsContext = NULL;
|
79
|
+
};
|
72
80
|
if ( m_codec ) {
|
73
|
-
|
81
|
+
m_ic->streams[ m_idx ]->discard = AVDISCARD_ALL;
|
82
|
+
avcodec_close( m_dec );
|
74
83
|
m_codec = NULL;
|
75
84
|
};
|
76
|
-
|
85
|
+
m_dec = NULL;
|
77
86
|
m_idx = -1;
|
78
87
|
if ( m_ic ) {
|
79
88
|
av_close_input_file( m_ic );
|
@@ -83,12 +92,14 @@ void AVInput::close(void)
|
|
83
92
|
|
84
93
|
FramePtr AVInput::read(void) throw (Error)
|
85
94
|
{
|
95
|
+
ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
|
96
|
+
"Did you call \"close\" before?" );
|
86
97
|
FramePtr retVal;
|
87
98
|
AVPacket packet;
|
88
99
|
while ( av_read_frame( m_ic, &packet ) >= 0 ) {
|
89
100
|
if ( packet.stream_index == m_idx ) {
|
90
101
|
int frameFinished;
|
91
|
-
int err = avcodec_decode_video(
|
102
|
+
int err = avcodec_decode_video( m_dec, m_frame, &frameFinished,
|
92
103
|
packet.data, packet.size );
|
93
104
|
ERRORMACRO( err >= 0, Error, ,
|
94
105
|
"Error decoding frame of video \"" << m_mrl << "\"" );
|
@@ -96,25 +107,20 @@ FramePtr AVInput::read(void) throw (Error)
|
|
96
107
|
if ( frameFinished ) {
|
97
108
|
if ( packet.dts != AV_NOPTS_VALUE ) m_pts = packet.dts;
|
98
109
|
av_free_packet( &packet );
|
99
|
-
AVFrame
|
100
|
-
m_data = boost::shared_array< char >( new char[
|
101
|
-
|
110
|
+
AVFrame picture;
|
111
|
+
m_data = boost::shared_array< char >( new char[ m_dec->width *
|
112
|
+
m_dec->height *
|
102
113
|
3 / 2 ] );
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
SWS_FAST_BILINEAR, 0, 0, 0 );
|
114
|
-
sws_scale( swsContext, m_frame->data, m_frame->linesize, 0,
|
115
|
-
m_enc->height, frame.data, frame.linesize );
|
116
|
-
sws_freeContext( swsContext );
|
117
|
-
retVal = FramePtr( new Frame( "YV12", m_enc->width, m_enc->height,
|
114
|
+
picture.data[0] = (uint8_t *)m_data.get();
|
115
|
+
picture.data[1] = (uint8_t *)m_data.get() +
|
116
|
+
m_dec->width * m_dec->height * 5 / 4;
|
117
|
+
picture.data[2] = (uint8_t *)m_data.get() + m_dec->width * m_dec->height;
|
118
|
+
picture.linesize[0] = m_dec->width;
|
119
|
+
picture.linesize[1] = m_dec->width / 2;
|
120
|
+
picture.linesize[2] = m_dec->width / 2;
|
121
|
+
sws_scale( m_swsContext, m_frame->data, m_frame->linesize, 0,
|
122
|
+
m_dec->height, picture.data, picture.linesize );
|
123
|
+
retVal = FramePtr( new Frame( "YV12", m_dec->width, m_dec->height,
|
118
124
|
m_data.get() ) );
|
119
125
|
break;
|
120
126
|
};
|
@@ -125,6 +131,20 @@ FramePtr AVInput::read(void) throw (Error)
|
|
125
131
|
return retVal;
|
126
132
|
}
|
127
133
|
|
134
|
+
int AVInput::width(void) const throw (Error)
|
135
|
+
{
|
136
|
+
ERRORMACRO( m_dec != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
|
137
|
+
"Did you call \"close\" before?" );
|
138
|
+
return m_dec->width;
|
139
|
+
}
|
140
|
+
|
141
|
+
int AVInput::height(void) const throw (Error)
|
142
|
+
{
|
143
|
+
ERRORMACRO( m_dec != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
|
144
|
+
"Did you call \"close\" before?" );
|
145
|
+
return m_dec->height;
|
146
|
+
}
|
147
|
+
|
128
148
|
AVRational AVInput::timeBase(void) throw (Error)
|
129
149
|
{
|
130
150
|
ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
|
@@ -132,13 +152,20 @@ AVRational AVInput::timeBase(void) throw (Error)
|
|
132
152
|
return m_ic->streams[ m_idx ]->time_base;
|
133
153
|
}
|
134
154
|
|
155
|
+
AVRational AVInput::frameRate(void) throw (Error)
|
156
|
+
{
|
157
|
+
ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
|
158
|
+
"Did you call \"close\" before?" );
|
159
|
+
return m_ic->streams[ m_idx ]->r_frame_rate;
|
160
|
+
}
|
161
|
+
|
135
162
|
void AVInput::seek( long timestamp ) throw (Error)
|
136
163
|
{
|
137
164
|
ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
|
138
165
|
"Did you call \"close\" before?" );
|
139
166
|
ERRORMACRO( av_seek_frame( m_ic, -1, timestamp, 0 ) >= 0,
|
140
167
|
Error, , "Error seeking in video \"" << m_mrl << "\"" );
|
141
|
-
avcodec_flush_buffers(
|
168
|
+
avcodec_flush_buffers( m_dec );
|
142
169
|
}
|
143
170
|
|
144
171
|
long long AVInput::pts(void) throw (Error)
|
@@ -150,13 +177,16 @@ long long AVInput::pts(void) throw (Error)
|
|
150
177
|
|
151
178
|
VALUE AVInput::registerRubyClass( VALUE rbModule )
|
152
179
|
{
|
153
|
-
av_register_all();
|
154
180
|
cRubyClass = rb_define_class_under( rbModule, "AVInput", rb_cObject );
|
155
181
|
rb_define_singleton_method( cRubyClass, "new",
|
156
182
|
RUBY_METHOD_FUNC( wrapNew ), 1 );
|
157
183
|
rb_define_const( cRubyClass, "AV_TIME_BASE", INT2NUM( AV_TIME_BASE ) );
|
184
|
+
rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
|
158
185
|
rb_define_method( cRubyClass, "read", RUBY_METHOD_FUNC( wrapRead ), 0 );
|
159
186
|
rb_define_method( cRubyClass, "time_base", RUBY_METHOD_FUNC( wrapTimeBase ), 0 );
|
187
|
+
rb_define_method( cRubyClass, "frame_rate", RUBY_METHOD_FUNC( wrapFrameRate ), 0 );
|
188
|
+
rb_define_method( cRubyClass, "width", RUBY_METHOD_FUNC( wrapWidth ), 0 );
|
189
|
+
rb_define_method( cRubyClass, "height", RUBY_METHOD_FUNC( wrapHeight ), 0 );
|
160
190
|
rb_define_method( cRubyClass, "seek", RUBY_METHOD_FUNC( wrapSeek ), 1 );
|
161
191
|
rb_define_method( cRubyClass, "pts", RUBY_METHOD_FUNC( wrapPTS ), 0 );
|
162
192
|
}
|
@@ -180,6 +210,13 @@ VALUE AVInput::wrapNew( VALUE rbClass, VALUE rbMRL )
|
|
180
210
|
return retVal;
|
181
211
|
}
|
182
212
|
|
213
|
+
VALUE AVInput::wrapClose( VALUE rbSelf )
|
214
|
+
{
|
215
|
+
AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
|
216
|
+
(*self)->close();
|
217
|
+
return rbSelf;
|
218
|
+
}
|
219
|
+
|
183
220
|
VALUE AVInput::wrapRead( VALUE rbSelf )
|
184
221
|
{
|
185
222
|
VALUE retVal = Qnil;
|
@@ -198,9 +235,47 @@ VALUE AVInput::wrapTimeBase( VALUE rbSelf )
|
|
198
235
|
VALUE retVal = Qnil;
|
199
236
|
try {
|
200
237
|
AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
|
201
|
-
AVRational
|
238
|
+
AVRational timeBase = (*self)->timeBase();
|
239
|
+
retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
|
240
|
+
INT2NUM( timeBase.num ), INT2NUM( timeBase.den ) );
|
241
|
+
} catch( exception &e ) {
|
242
|
+
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
243
|
+
};
|
244
|
+
return retVal;
|
245
|
+
}
|
246
|
+
|
247
|
+
VALUE AVInput::wrapFrameRate( VALUE rbSelf )
|
248
|
+
{
|
249
|
+
VALUE retVal = Qnil;
|
250
|
+
try {
|
251
|
+
AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
|
252
|
+
AVRational frameRate = (*self)->frameRate();
|
202
253
|
retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
|
203
|
-
INT2NUM(
|
254
|
+
INT2NUM( frameRate.num ), INT2NUM( frameRate.den ) );
|
255
|
+
} catch( exception &e ) {
|
256
|
+
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
257
|
+
};
|
258
|
+
return retVal;
|
259
|
+
}
|
260
|
+
|
261
|
+
VALUE AVInput::wrapWidth( VALUE rbSelf )
|
262
|
+
{
|
263
|
+
VALUE retVal = Qnil;
|
264
|
+
try {
|
265
|
+
AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
|
266
|
+
retVal = INT2NUM( (*self)->width() );
|
267
|
+
} catch( exception &e ) {
|
268
|
+
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
269
|
+
};
|
270
|
+
return retVal;
|
271
|
+
}
|
272
|
+
|
273
|
+
VALUE AVInput::wrapHeight( VALUE rbSelf )
|
274
|
+
{
|
275
|
+
VALUE retVal = Qnil;
|
276
|
+
try {
|
277
|
+
AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
|
278
|
+
retVal = INT2NUM( (*self)->height() );
|
204
279
|
} catch( exception &e ) {
|
205
280
|
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
206
281
|
};
|
data/ext/avinput.hh
CHANGED
@@ -13,15 +13,16 @@
|
|
13
13
|
|
14
14
|
You should have received a copy of the GNU General Public License
|
15
15
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
-
#ifndef
|
17
|
-
#define
|
16
|
+
#ifndef AVINPUT_HH
|
17
|
+
#define AVINPUT_HH
|
18
18
|
|
19
19
|
#include <boost/shared_ptr.hpp>
|
20
|
-
#include <ruby.h>
|
21
20
|
extern "C" {
|
22
21
|
#include <libswscale/swscale.h>
|
23
22
|
#include <libavformat/avformat.h>
|
24
23
|
}
|
24
|
+
#undef RSHIFT
|
25
|
+
#include <ruby.h>
|
25
26
|
#include "error.hh"
|
26
27
|
#include "frame.hh"
|
27
28
|
|
@@ -32,24 +33,32 @@ public:
|
|
32
33
|
virtual ~AVInput(void);
|
33
34
|
void close(void);
|
34
35
|
FramePtr read(void) throw (Error);
|
36
|
+
int width(void) const throw (Error);
|
37
|
+
int height(void) const throw (Error);
|
35
38
|
AVRational timeBase(void) throw (Error);
|
39
|
+
AVRational frameRate(void) throw (Error);
|
36
40
|
void seek( long timestamp ) throw (Error);
|
37
41
|
long long pts(void) throw (Error);
|
38
42
|
static VALUE cRubyClass;
|
39
43
|
static VALUE registerRubyClass( VALUE rbModule );
|
40
44
|
static void deleteRubyObject( void *ptr );
|
41
45
|
static VALUE wrapNew( VALUE rbClass, VALUE rbMRL );
|
46
|
+
static VALUE wrapClose( VALUE rbSelf );
|
42
47
|
static VALUE wrapRead( VALUE rbSelf );
|
43
48
|
static VALUE wrapTimeBase( VALUE rbSelf );
|
49
|
+
static VALUE wrapFrameRate( VALUE rbSelf );
|
50
|
+
static VALUE wrapWidth( VALUE rbSelf );
|
51
|
+
static VALUE wrapHeight( VALUE rbSelf );
|
44
52
|
static VALUE wrapSeek( VALUE rbSelf, VALUE rbPos );
|
45
53
|
static VALUE wrapPTS( VALUE rbSelf );
|
46
54
|
protected:
|
47
55
|
std::string m_mrl;
|
48
56
|
AVFormatContext *m_ic;
|
49
|
-
AVCodecContext *
|
57
|
+
AVCodecContext *m_dec;
|
50
58
|
AVCodec *m_codec;
|
51
59
|
int m_idx;
|
52
60
|
long long m_pts;
|
61
|
+
struct SwsContext *m_swsContext;
|
53
62
|
AVFrame *m_frame;
|
54
63
|
boost::shared_array< char > m_data;
|
55
64
|
};
|
data/ext/avoutput.cc
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
/* HornetsEye - Computer Vision with Ruby
|
2
|
+
Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
|
3
|
+
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
+
#include "avoutput.hh"
|
17
|
+
|
18
|
+
#if !defined(INT64_C)
|
19
|
+
#define INT64_C(c) c ## LL
|
20
|
+
#endif
|
21
|
+
|
22
|
+
#define VIDEO_BUF_SIZE 200000
|
23
|
+
|
24
|
+
using namespace std;
|
25
|
+
|
26
|
+
VALUE AVOutput::cRubyClass = Qnil;
|
27
|
+
|
28
|
+
AVOutput::AVOutput( const string &mrl, int bitrate, int width, int height,
|
29
|
+
int timeBaseNum, int timeBaseDen ) throw (Error):
|
30
|
+
m_mrl( mrl ), m_oc( NULL ), m_video_st( NULL ), m_video_codec_open( false ),
|
31
|
+
m_video_buf( NULL ), m_file_open( false ), m_header_written( false ),
|
32
|
+
m_swsContext( NULL ), m_frame( NULL )
|
33
|
+
{
|
34
|
+
try {
|
35
|
+
AVOutputFormat *format;
|
36
|
+
format = guess_format( NULL, mrl.c_str(), NULL );
|
37
|
+
if ( format == NULL ) format = guess_format( "mpeg", NULL, NULL );
|
38
|
+
ERRORMACRO( format != NULL, Error, ,
|
39
|
+
"Could not find suitable output format for \"" << mrl << "\"" );
|
40
|
+
m_oc = avformat_alloc_context();
|
41
|
+
ERRORMACRO( m_oc != NULL, Error, , "Failure allocating format context" );
|
42
|
+
m_oc->oformat = format;
|
43
|
+
snprintf( m_oc->filename, sizeof( m_oc->filename ), "%s", mrl.c_str() );
|
44
|
+
ERRORMACRO( format->video_codec != CODEC_ID_NONE, Error, ,
|
45
|
+
"Output format does not support video" );
|
46
|
+
m_video_st = av_new_stream( m_oc, 0 );
|
47
|
+
ERRORMACRO( m_video_st != NULL, Error, , "Could not allocate video stream" );
|
48
|
+
AVCodecContext *c = m_video_st->codec;
|
49
|
+
c->codec_id = format->video_codec;
|
50
|
+
c->codec_type = CODEC_TYPE_VIDEO;
|
51
|
+
c->bit_rate = bitrate;
|
52
|
+
c->width = width;
|
53
|
+
c->height = height;
|
54
|
+
c->time_base.num = timeBaseNum;
|
55
|
+
c->time_base.den = timeBaseDen;
|
56
|
+
c->gop_size = 12;
|
57
|
+
c->pix_fmt = PIX_FMT_YUV420P;
|
58
|
+
if ( m_oc->oformat->flags & AVFMT_GLOBALHEADER )
|
59
|
+
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
60
|
+
ERRORMACRO( av_set_parameters( m_oc, NULL ) >= 0, Error, ,
|
61
|
+
"Invalid output format parameters" );
|
62
|
+
AVCodec *codec = avcodec_find_encoder( c->codec_id );
|
63
|
+
ERRORMACRO( codec != NULL, Error, , "Could not find codec " << c->codec_id );
|
64
|
+
ERRORMACRO( avcodec_open( c, codec ) >= 0, Error, ,
|
65
|
+
"Error opening codec " << c->codec_id );
|
66
|
+
m_video_codec_open = true;
|
67
|
+
if ( !( m_oc->oformat->flags & AVFMT_RAWPICTURE ) ) {
|
68
|
+
m_video_buf = (char *)av_malloc( VIDEO_BUF_SIZE );
|
69
|
+
ERRORMACRO( m_video_buf != NULL, Error, ,
|
70
|
+
"Error allocating video output buffer" );
|
71
|
+
};
|
72
|
+
if ( !( format->flags & AVFMT_NOFILE ) ) {
|
73
|
+
ERRORMACRO( url_fopen( &m_oc->pb, mrl.c_str(), URL_WRONLY ) >= 0, Error, ,
|
74
|
+
"Could not open \"" << mrl << "\"" );
|
75
|
+
m_file_open = true;
|
76
|
+
};
|
77
|
+
ERRORMACRO( av_write_header( m_oc ) >= 0, Error, ,
|
78
|
+
"Error writing header of video \"" << mrl << "\"" );
|
79
|
+
m_header_written = true;
|
80
|
+
m_swsContext = sws_getContext( width, height, PIX_FMT_YUV420P,
|
81
|
+
width, height, PIX_FMT_YUV420P,
|
82
|
+
SWS_FAST_BILINEAR, 0, 0, 0 );
|
83
|
+
m_frame = avcodec_alloc_frame();
|
84
|
+
ERRORMACRO( m_frame, Error, , "Error allocating frame" );
|
85
|
+
int size = avpicture_get_size( PIX_FMT_YUV420P, width, height );
|
86
|
+
char *frameBuffer = (char *)av_malloc( size );
|
87
|
+
ERRORMACRO( frameBuffer, Error, , "Error allocating memory buffer for frame" );
|
88
|
+
avpicture_fill( (AVPicture *)m_frame, (uint8_t *)frameBuffer, PIX_FMT_YUV420P,
|
89
|
+
width, height );
|
90
|
+
} catch ( Error &e ) {
|
91
|
+
close();
|
92
|
+
throw e;
|
93
|
+
};
|
94
|
+
}
|
95
|
+
|
96
|
+
AVOutput::~AVOutput(void)
|
97
|
+
{
|
98
|
+
close();
|
99
|
+
}
|
100
|
+
|
101
|
+
void AVOutput::close(void)
|
102
|
+
{
|
103
|
+
if ( m_frame ) {
|
104
|
+
if ( m_frame->data[0] )
|
105
|
+
av_free( m_frame->data[0] );
|
106
|
+
av_free( m_frame );
|
107
|
+
m_frame = NULL;
|
108
|
+
};
|
109
|
+
if ( m_swsContext ) {
|
110
|
+
sws_freeContext( m_swsContext );
|
111
|
+
m_swsContext = NULL;
|
112
|
+
};
|
113
|
+
if ( m_header_written ) {
|
114
|
+
av_write_trailer( m_oc );
|
115
|
+
m_header_written = false;
|
116
|
+
};
|
117
|
+
if ( m_video_st ) {
|
118
|
+
if ( m_video_codec_open ) {
|
119
|
+
avcodec_close( m_video_st->codec );
|
120
|
+
m_video_codec_open = false;
|
121
|
+
};
|
122
|
+
};
|
123
|
+
if ( m_video_buf ) {
|
124
|
+
av_free( m_video_buf );
|
125
|
+
m_video_buf = NULL;
|
126
|
+
};
|
127
|
+
if ( m_oc ) {
|
128
|
+
for ( int i = 0; i < m_oc->nb_streams; i++ ) {
|
129
|
+
av_freep( &m_oc->streams[i]->codec );
|
130
|
+
av_freep( &m_oc->streams[i] );
|
131
|
+
};
|
132
|
+
m_video_st = NULL;
|
133
|
+
if ( m_file_open ) {
|
134
|
+
url_fclose( m_oc->pb );
|
135
|
+
m_file_open = false;
|
136
|
+
};
|
137
|
+
av_free( m_oc );
|
138
|
+
m_oc = NULL;
|
139
|
+
};
|
140
|
+
}
|
141
|
+
|
142
|
+
void AVOutput::write( FramePtr frame ) throw (Error)
|
143
|
+
{
|
144
|
+
ERRORMACRO( m_oc != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
|
145
|
+
"Did you call \"close\" before?" );
|
146
|
+
ERRORMACRO( m_video_st->codec->width == frame->width() &&
|
147
|
+
m_video_st->codec->height == frame->height(), Error, ,
|
148
|
+
"Resolution of frame is " << frame->width() << 'x'
|
149
|
+
<< frame->height() << " but video resolution is "
|
150
|
+
<< m_video_st->codec->width << 'x' << m_video_st->codec->height );
|
151
|
+
if ( m_oc->oformat->flags & AVFMT_RAWPICTURE ) {
|
152
|
+
ERRORMACRO( false, Error, , "Raw picture encoding not implemented yet" );
|
153
|
+
} else {
|
154
|
+
AVCodecContext *c = m_video_st->codec;
|
155
|
+
AVFrame picture;
|
156
|
+
picture.data[0] = (uint8_t *)frame->data();
|
157
|
+
picture.data[1] = (uint8_t *)frame->data() + c->width * c->height * 5 / 4;
|
158
|
+
picture.data[2] = (uint8_t *)frame->data() + c->width * c->height;
|
159
|
+
picture.linesize[0] = c->width;
|
160
|
+
picture.linesize[1] = c->width / 2;
|
161
|
+
picture.linesize[2] = c->width / 2;
|
162
|
+
sws_scale( m_swsContext, picture.data, picture.linesize, 0,
|
163
|
+
c->height, m_frame->data, m_frame->linesize );
|
164
|
+
int packetSize = avcodec_encode_video( c, (uint8_t *)m_video_buf,
|
165
|
+
VIDEO_BUF_SIZE, m_frame );
|
166
|
+
ERRORMACRO( packetSize >= 0, Error, , "Error encoding frame" );
|
167
|
+
if ( packetSize > 0 ) {
|
168
|
+
AVPacket packet;
|
169
|
+
av_init_packet( &packet );
|
170
|
+
if ( c->coded_frame->pts != AV_NOPTS_VALUE )
|
171
|
+
packet.pts = av_rescale_q( c->coded_frame->pts, c->time_base,
|
172
|
+
m_video_st->time_base );
|
173
|
+
if ( c->coded_frame->key_frame )
|
174
|
+
packet.flags |= PKT_FLAG_KEY;
|
175
|
+
packet.stream_index = m_video_st->index;
|
176
|
+
packet.data = (uint8_t *)m_video_buf;
|
177
|
+
packet.size = packetSize;
|
178
|
+
ERRORMACRO( av_interleaved_write_frame( m_oc, &packet ) >= 0, Error, ,
|
179
|
+
"Error writing frame of video \"" << m_mrl << "\"" );
|
180
|
+
};
|
181
|
+
};
|
182
|
+
}
|
183
|
+
|
184
|
+
VALUE AVOutput::registerRubyClass( VALUE rbModule )
|
185
|
+
{
|
186
|
+
cRubyClass = rb_define_class_under( rbModule, "AVOutput", rb_cObject );
|
187
|
+
rb_define_singleton_method( cRubyClass, "new",
|
188
|
+
RUBY_METHOD_FUNC( wrapNew ), 6 );
|
189
|
+
rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
|
190
|
+
rb_define_method( cRubyClass, "write", RUBY_METHOD_FUNC( wrapWrite ), 1 );
|
191
|
+
}
|
192
|
+
|
193
|
+
void AVOutput::deleteRubyObject( void *ptr )
|
194
|
+
{
|
195
|
+
delete (AVOutputPtr *)ptr;
|
196
|
+
}
|
197
|
+
|
198
|
+
VALUE AVOutput::wrapNew( VALUE rbClass, VALUE rbMRL, VALUE rbBitRate, VALUE rbWidth,
|
199
|
+
VALUE rbHeight, VALUE rbTimeBaseNum, VALUE rbTimeBaseDen )
|
200
|
+
{
|
201
|
+
VALUE retVal = Qnil;
|
202
|
+
try {
|
203
|
+
rb_check_type( rbMRL, T_STRING );
|
204
|
+
AVOutputPtr ptr( new AVOutput( StringValuePtr( rbMRL ), NUM2INT( rbBitRate ),
|
205
|
+
NUM2INT( rbWidth ), NUM2INT( rbHeight ),
|
206
|
+
NUM2INT( rbTimeBaseNum ),
|
207
|
+
NUM2INT( rbTimeBaseDen ) ) );
|
208
|
+
retVal = Data_Wrap_Struct( rbClass, 0, deleteRubyObject,
|
209
|
+
new AVOutputPtr( ptr ) );
|
210
|
+
} catch ( exception &e ) {
|
211
|
+
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
212
|
+
};
|
213
|
+
return retVal;
|
214
|
+
}
|
215
|
+
|
216
|
+
VALUE AVOutput::wrapClose( VALUE rbSelf )
|
217
|
+
{
|
218
|
+
AVOutputPtr *self; Data_Get_Struct( rbSelf, AVOutputPtr, self );
|
219
|
+
(*self)->close();
|
220
|
+
return rbSelf;
|
221
|
+
}
|
222
|
+
|
223
|
+
VALUE AVOutput::wrapWrite( VALUE rbSelf, VALUE rbFrame )
|
224
|
+
{
|
225
|
+
try {
|
226
|
+
AVOutputPtr *self; Data_Get_Struct( rbSelf, AVOutputPtr, self );
|
227
|
+
FramePtr frame( new Frame( rbFrame ) );
|
228
|
+
(*self)->write( frame );
|
229
|
+
} catch ( exception &e ) {
|
230
|
+
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
231
|
+
};
|
232
|
+
return rbFrame;
|
233
|
+
}
|
data/ext/avoutput.hh
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
/* HornetsEye - Computer Vision with Ruby
|
2
|
+
Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
|
3
|
+
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
+
#ifndef AVOUTPUT_HH
|
17
|
+
#define AVOUTPUT_HH
|
18
|
+
|
19
|
+
#include <boost/shared_ptr.hpp>
|
20
|
+
extern "C" {
|
21
|
+
#include <libswscale/swscale.h>
|
22
|
+
#include <libavformat/avformat.h>
|
23
|
+
}
|
24
|
+
#undef RSHIFT
|
25
|
+
#include <ruby.h>
|
26
|
+
#include "error.hh"
|
27
|
+
#include "frame.hh"
|
28
|
+
|
29
|
+
class AVOutput
|
30
|
+
{
|
31
|
+
public:
|
32
|
+
AVOutput( const std::string &mrl, int bitrate,
|
33
|
+
int width, int height, int timeBaseNum, int timeBaseDen ) throw (Error);
|
34
|
+
virtual ~AVOutput(void);
|
35
|
+
void close(void);
|
36
|
+
void write( FramePtr frame ) throw (Error);
|
37
|
+
static VALUE cRubyClass;
|
38
|
+
static VALUE registerRubyClass( VALUE rbModule );
|
39
|
+
static void deleteRubyObject( void *ptr );
|
40
|
+
static VALUE wrapNew( VALUE rbClass, VALUE rbMRL, VALUE rbBitRate, VALUE rbWidth,
|
41
|
+
VALUE rbHeight, VALUE rbTimeBaseNum, VALUE rbTimeBaseDen );
|
42
|
+
static VALUE wrapClose( VALUE rbSelf );
|
43
|
+
static VALUE wrapWrite( VALUE rbSelf, VALUE rbFrame );
|
44
|
+
protected:
|
45
|
+
std::string m_mrl;
|
46
|
+
AVFormatContext *m_oc;
|
47
|
+
AVStream *m_video_st;
|
48
|
+
bool m_video_codec_open;
|
49
|
+
char *m_video_buf;
|
50
|
+
bool m_file_open;
|
51
|
+
bool m_header_written;
|
52
|
+
struct SwsContext *m_swsContext;
|
53
|
+
AVFrame *m_frame;
|
54
|
+
};
|
55
|
+
|
56
|
+
typedef boost::shared_ptr< AVOutput > AVOutputPtr;
|
57
|
+
|
58
|
+
#endif
|
59
|
+
|
data/ext/frame.cc
CHANGED
@@ -13,7 +13,6 @@
|
|
13
13
|
|
14
14
|
You should have received a copy of the GNU General Public License
|
15
15
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
-
#include <iostream>
|
17
16
|
#include "frame.hh"
|
18
17
|
|
19
18
|
using namespace std;
|
@@ -34,3 +33,22 @@ Frame::Frame( const char *typecode, int width, int height, char *data ):
|
|
34
33
|
INT2NUM( width ), INT2NUM( height ),
|
35
34
|
rbMemory );
|
36
35
|
}
|
36
|
+
|
37
|
+
int Frame::width(void)
|
38
|
+
{
|
39
|
+
return NUM2INT( rb_funcall( m_frame, rb_intern( "width" ), 0 ) );
|
40
|
+
}
|
41
|
+
|
42
|
+
int Frame::height(void)
|
43
|
+
{
|
44
|
+
return NUM2INT( rb_funcall( m_frame, rb_intern( "height" ), 0 ) );
|
45
|
+
}
|
46
|
+
|
47
|
+
char *Frame::data(void)
|
48
|
+
{
|
49
|
+
VALUE rbMemory = rb_iv_get( m_frame, "@memory" );
|
50
|
+
char *ptr;
|
51
|
+
Data_Get_Struct( rbMemory, char, ptr );
|
52
|
+
return ptr;
|
53
|
+
}
|
54
|
+
|
data/ext/frame.hh
CHANGED
@@ -23,7 +23,11 @@ class Frame
|
|
23
23
|
{
|
24
24
|
public:
|
25
25
|
Frame( const char *typecode, int width, int height, char *data );
|
26
|
+
Frame( VALUE rbFrame ): m_frame( rbFrame ) {}
|
26
27
|
virtual ~Frame(void) {}
|
28
|
+
int width(void);
|
29
|
+
int height(void);
|
30
|
+
char *data(void);
|
27
31
|
VALUE rubyObject(void) { return m_frame; }
|
28
32
|
protected:
|
29
33
|
VALUE m_frame;
|
data/ext/init.cc
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
You should have received a copy of the GNU General Public License
|
15
15
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
16
|
#include "avinput.hh"
|
17
|
+
#include "avoutput.hh"
|
17
18
|
|
18
19
|
#ifdef WIN32
|
19
20
|
#define DLLEXPORT __declspec(dllexport)
|
@@ -31,7 +32,9 @@ extern "C" {
|
|
31
32
|
{
|
32
33
|
rb_require( "hornetseye_frame" );
|
33
34
|
VALUE rbHornetseye = rb_define_module( "Hornetseye" );
|
35
|
+
av_register_all();
|
34
36
|
AVInput::registerRubyClass( rbHornetseye );
|
37
|
+
AVOutput::registerRubyClass( rbHornetseye );
|
35
38
|
rb_require( "hornetseye_ffmpeg_ext.rb" );
|
36
39
|
}
|
37
40
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# hornetseye-frame - Colourspace conversions and compression
|
2
|
+
# Copyright (C) 2010 Jan Wedekind
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
# Namespace of Hornetseye computer vision library
|
18
|
+
module Hornetseye
|
19
|
+
|
20
|
+
class AVOutput
|
21
|
+
|
22
|
+
class << self
|
23
|
+
|
24
|
+
alias_method :orig_new, :new
|
25
|
+
|
26
|
+
def new( mrl, bitrate, width, height, frame_rate )
|
27
|
+
orig_new mrl, bitrate, width, height,
|
28
|
+
frame_rate.denominator, frame_rate.numerator
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 3
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jan Wedekind
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-09-
|
17
|
+
date: 2010-09-30 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -86,11 +86,14 @@ files:
|
|
86
86
|
- COPYING
|
87
87
|
- .document
|
88
88
|
- lib/hornetseye-ffmpeg/avinput.rb
|
89
|
+
- lib/hornetseye-ffmpeg/avoutput.rb
|
89
90
|
- lib/hornetseye_ffmpeg_ext.rb
|
91
|
+
- ext/avoutput.cc
|
90
92
|
- ext/avinput.cc
|
91
93
|
- ext/init.cc
|
92
94
|
- ext/frame.cc
|
93
95
|
- ext/frame.hh
|
96
|
+
- ext/avoutput.hh
|
94
97
|
- ext/avinput.hh
|
95
98
|
- ext/error.hh
|
96
99
|
has_rdoc: yard
|