hornetseye-ffmpeg 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.2.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 ), m_enc( NULL ), m_codec( NULL ), m_idx( -1 ),
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
- m_enc = m_ic->streams[ m_idx ]->codec;
47
- m_codec = avcodec_find_decoder( m_enc->codec_id );
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( m_enc, m_codec );
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
- avcodec_close( m_enc );
81
+ m_ic->streams[ m_idx ]->discard = AVDISCARD_ALL;
82
+ avcodec_close( m_dec );
74
83
  m_codec = NULL;
75
84
  };
76
- m_enc = NULL;
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( m_enc, m_frame, &frameFinished,
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 frame;
100
- m_data = boost::shared_array< char >( new char[ m_enc->width *
101
- m_enc->height *
110
+ AVFrame picture;
111
+ m_data = boost::shared_array< char >( new char[ m_dec->width *
112
+ m_dec->height *
102
113
  3 / 2 ] );
103
- frame.data[0] = (uint8_t *)m_data.get();
104
- frame.data[1] = (uint8_t *)m_data.get() +
105
- m_enc->width * m_enc->height * 5 / 4;
106
- frame.data[2] = (uint8_t *)m_data.get() + m_enc->width * m_enc->height;
107
- frame.linesize[0] = m_enc->width;
108
- frame.linesize[1] = m_enc->width / 2;
109
- frame.linesize[2] = m_enc->width / 2;
110
- struct SwsContext *swsContext =
111
- sws_getContext( m_enc->width, m_enc->height, m_enc->pix_fmt,
112
- m_enc->width, m_enc->height, PIX_FMT_YUV420P,
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( m_enc );
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 time_base = (*self)->timeBase();
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( time_base.num ), INT2NUM( time_base.den ) );
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 AVFORMAT_HH
17
- #define AVFORMAT_HH
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 *m_enc;
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
 
@@ -31,6 +31,10 @@ module Hornetseye
31
31
 
32
32
  end
33
33
 
34
+ def shape
35
+ [ width, height ]
36
+ end
37
+
34
38
  alias_method :orig_read, :read
35
39
 
36
40
  def read
@@ -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
+
@@ -16,4 +16,5 @@
16
16
 
17
17
  require 'hornetseye_frame'
18
18
  require 'hornetseye-ffmpeg/avinput'
19
+ require 'hornetseye-ffmpeg/avoutput'
19
20
 
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
7
+ - 3
8
8
  - 0
9
- version: 0.2.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-28 00:00:00 +01:00
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