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 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