hornetseye-ffmpeg 0.4.3 → 0.5.1

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
@@ -7,7 +7,7 @@ require 'rake/loaders/makefile'
7
7
  require 'rbconfig'
8
8
 
9
9
  PKG_NAME = 'hornetseye-ffmpeg'
10
- PKG_VERSION = '0.4.3'
10
+ PKG_VERSION = '0.5.1'
11
11
  CXX = ENV[ 'CXX' ] || 'g++'
12
12
  STRIP = ENV[ 'STRIP' ] || 'strip'
13
13
  RB_FILES = FileList[ 'lib/**/*.rb' ]
@@ -207,9 +207,9 @@ begin
207
207
  s.has_rdoc = 'yard'
208
208
  s.extra_rdoc_files = []
209
209
  s.rdoc_options = %w{--no-private}
210
- s.add_dependency %<malloc>, [ '~> 1.1' ]
211
- s.add_dependency %<multiarray>, [ '~> 0.6' ]
212
- s.add_dependency %<hornetseye-frame>, [ '~> 0.3' ]
210
+ s.add_dependency %<malloc>, [ '~> 1.2' ]
211
+ s.add_dependency %<multiarray>, [ '~> 0.11' ]
212
+ s.add_dependency %<hornetseye-frame>, [ '~> 0.7' ]
213
213
  s.add_development_dependency %q{rake}
214
214
  end
215
215
  GEM_SOURCE = "#{PKG_NAME}-#{PKG_VERSION}.gem"
@@ -230,9 +230,9 @@ begin
230
230
  s.has_rdoc = 'yard'
231
231
  s.extra_rdoc_files = []
232
232
  s.rdoc_options = %w{--no-private}
233
- s.add_dependency %<malloc>, [ '~> 1.1' ]
234
- s.add_dependency %<multiarray>, [ '~> 0.6' ]
235
- s.add_dependency %<hornetseye-frame>, [ '~> 0.3' ]
233
+ s.add_dependency %<malloc>, [ '~> 1.2' ]
234
+ s.add_dependency %<multiarray>, [ '~> 0.11' ]
235
+ s.add_dependency %<hornetseye-frame>, [ '~> 0.7' ]
236
236
  end
237
237
  GEM_BINARY = "#{PKG_NAME}-#{PKG_VERSION}-#{$BINSPEC.platform}.gem"
238
238
  desc "Build the gem file #{GEM_SOURCE}"
data/ext/avinput.cc CHANGED
@@ -13,7 +13,9 @@
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 NDEBUG
16
17
  #include <iostream>
18
+ #endif
17
19
  #include "avinput.hh"
18
20
 
19
21
  #if !defined(INT64_C)
@@ -27,35 +29,59 @@ using namespace std;
27
29
  VALUE AVInput::cRubyClass = Qnil;
28
30
 
29
31
  AVInput::AVInput( const string &mrl ) throw (Error):
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 )
32
+ m_mrl( mrl ), m_ic( NULL ), m_videoDec( NULL ), m_audioDec( NULL ),
33
+ m_videoCodec( NULL ), m_audioCodec( NULL ),
34
+ m_videoStream( -1 ), m_audioStream( -1 ), m_videoPts( 0 ), m_audioPts( 0 ),
35
+ m_swsContext( NULL ), m_avFrame( NULL )
32
36
  {
33
37
  try {
34
38
  int err = av_open_input_file( &m_ic, mrl.c_str(), NULL, 0, NULL );
35
39
  ERRORMACRO( err >= 0, Error, , "Error opening file \"" << mrl << "\": "
36
- << strerror( -err ) );
40
+ << strerror( errno ) );
37
41
  err = av_find_stream_info( m_ic );
38
42
  ERRORMACRO( err >= 0, Error, , "Error finding stream info for file \""
39
- << mrl << "\": " << strerror( -err ) );
40
- for ( int i=0; i<m_ic->nb_streams; i++ )
41
- if ( m_ic->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) {
42
- m_idx = i;
43
- break;
43
+ << mrl << "\": " << strerror( errno ) );
44
+ for ( int i=0; i<m_ic->nb_streams; i++ ) {
45
+ if ( m_ic->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
46
+ m_videoStream = i;
47
+ if ( m_ic->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
48
+ m_audioStream = i;
49
+ };
50
+ #ifndef NDEBUG
51
+ cerr << "Video stream index is " << m_videoStream << endl;
52
+ cerr << "Audio stream index is " << m_audioStream << endl;
53
+ #endif
54
+ ERRORMACRO( m_videoStream >= 0, Error, ,
55
+ "Could not find video stream in file \"" << mrl << "\"" );
56
+ m_videoDec = m_ic->streams[ m_videoStream ]->codec;
57
+ if ( m_audioStream >= 0 )
58
+ m_audioDec = m_ic->streams[ m_audioStream ]->codec;
59
+ m_videoCodec = avcodec_find_decoder( m_videoDec->codec_id );
60
+ ERRORMACRO( m_videoCodec != NULL, Error, , "Could not find video decoder for "
61
+ "file \"" << mrl << "\"" );
62
+ err = avcodec_open( m_videoDec, m_videoCodec );
63
+ if ( err < 0 ) {
64
+ m_videoCodec = NULL;
65
+ ERRORMACRO( false, Error, , "Error opening video codec for file \""
66
+ << mrl << "\": " << strerror( errno ) );
67
+ };
68
+ if ( m_audioDec != NULL ) {
69
+ m_audioCodec = avcodec_find_decoder( m_audioDec->codec_id );
70
+ ERRORMACRO( m_audioCodec != NULL, Error, , "Could not find audio decoder for "
71
+ "file \"" << mrl << "\"" );
72
+ err = avcodec_open( m_audioDec, m_audioCodec );
73
+ if ( err < 0 ) {
74
+ m_audioCodec = NULL;
75
+ ERRORMACRO( false, Error, , "Error opening audio codec for file \""
76
+ << mrl << "\": " << strerror( errno ) );
44
77
  };
45
- ERRORMACRO( m_idx >= 0, Error, , "Could not find video stream in file \""
46
- << mrl << "\"" );
47
- m_dec = m_ic->streams[ m_idx ]->codec;
48
- m_codec = avcodec_find_decoder( m_dec->codec_id );
49
- ERRORMACRO( m_codec != NULL, Error, , "Could not find decoder for file \""
50
- << mrl << "\"" );
51
- err = avcodec_open( m_dec, m_codec );
52
- ERRORMACRO( err >= 0, Error, , "Error opening codec for file \""
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 );
57
- m_frame = avcodec_alloc_frame();
58
- ERRORMACRO( m_frame, Error, , "Error allocating frame" );
78
+ };
79
+ m_swsContext = sws_getContext( m_videoDec->width, m_videoDec->height,
80
+ m_videoDec->pix_fmt,
81
+ m_videoDec->width, m_videoDec->height,
82
+ PIX_FMT_YUV420P, SWS_FAST_BILINEAR, 0, 0, 0 );
83
+ m_avFrame = avcodec_alloc_frame();
84
+ ERRORMACRO( m_avFrame, Error, , "Error allocating frame" );
59
85
  } catch ( Error &e ) {
60
86
  close();
61
87
  throw e;
@@ -69,66 +95,110 @@ AVInput::~AVInput(void)
69
95
 
70
96
  void AVInput::close(void)
71
97
  {
72
- if ( m_frame ) {
73
- av_free( m_frame );
74
- m_frame = NULL;
98
+ m_audioFrame.reset();
99
+ m_videoFrame.reset();
100
+ if ( m_avFrame ) {
101
+ av_free( m_avFrame );
102
+ m_avFrame = NULL;
75
103
  };
76
104
  if ( m_swsContext ) {
77
105
  sws_freeContext( m_swsContext );
78
106
  m_swsContext = NULL;
79
107
  };
80
- if ( m_codec ) {
81
- m_ic->streams[ m_idx ]->discard = AVDISCARD_ALL;
82
- avcodec_close( m_dec );
83
- m_codec = NULL;
108
+ if ( m_audioCodec ) {
109
+ avcodec_close( m_audioDec );
110
+ m_audioCodec = NULL;
111
+ };
112
+ if ( m_videoCodec ) {
113
+ m_ic->streams[ m_videoStream ]->discard = AVDISCARD_ALL;
114
+ avcodec_close( m_videoDec );
115
+ m_videoCodec = NULL;
84
116
  };
85
- m_dec = NULL;
86
- m_idx = -1;
117
+ m_audioDec = NULL;
118
+ m_videoDec = NULL;
119
+ m_audioStream = -1;
120
+ m_videoStream = -1;
87
121
  if ( m_ic ) {
88
122
  av_close_input_file( m_ic );
89
123
  m_ic = NULL;
90
124
  };
91
125
  }
92
126
 
93
- FramePtr AVInput::read(void) throw (Error)
127
+ void AVInput::readAV(void) throw (Error)
94
128
  {
129
+ m_audioFrame.reset();
130
+ m_videoFrame.reset();
95
131
  ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
96
132
  "Did you call \"close\" before?" );
97
- FramePtr retVal;
98
133
  AVPacket packet;
134
+ long long firstPacketPts = AV_NOPTS_VALUE;
99
135
  while ( av_read_frame( m_ic, &packet ) >= 0 ) {
100
- if ( packet.stream_index == m_idx ) {
136
+ if ( packet.stream_index == m_videoStream ) {
101
137
  int frameFinished;
102
- int err = avcodec_decode_video( m_dec, m_frame, &frameFinished,
138
+ int err = avcodec_decode_video( m_videoDec, m_avFrame, &frameFinished,
103
139
  packet.data, packet.size );
104
140
  ERRORMACRO( err >= 0, Error, ,
105
- "Error decoding frame of video \"" << m_mrl << "\"" );
106
-
141
+ "Error decoding video frame of video \"" << m_mrl << "\"" );
142
+ if ( firstPacketPts == AV_NOPTS_VALUE ) firstPacketPts = packet.pts;
107
143
  if ( frameFinished ) {
108
- if ( packet.dts != AV_NOPTS_VALUE ) m_pts = packet.dts;
144
+ if ( packet.dts != AV_NOPTS_VALUE )
145
+ m_videoPts = packet.dts;
146
+ else
147
+ m_videoPts = firstPacketPts;
109
148
  av_free_packet( &packet );
110
149
  AVFrame picture;
111
- m_data = boost::shared_array< char >( new char[ m_dec->width *
112
- m_dec->height *
113
- 3 / 2 ] );
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,
124
- m_data.get() ) );
150
+ m_videoFrame = FramePtr( new Frame( "YV12", m_videoDec->width,
151
+ m_videoDec->height ) );
152
+ picture.data[0] = (uint8_t *)m_videoFrame->data();
153
+ picture.data[1] = (uint8_t *)m_videoFrame->data() +
154
+ m_videoDec->width * m_videoDec->height * 5 / 4;
155
+ picture.data[2] = (uint8_t *)m_videoFrame->data() +
156
+ m_videoDec->width * m_videoDec->height;
157
+ picture.linesize[0] = m_videoDec->width;
158
+ picture.linesize[1] = m_videoDec->width / 2;
159
+ picture.linesize[2] = m_videoDec->width / 2;
160
+ sws_scale( m_swsContext, m_avFrame->data, m_avFrame->linesize, 0,
161
+ m_videoDec->height, picture.data, picture.linesize );
125
162
  break;
163
+ } else
164
+ av_free_packet( &packet );
165
+ } else if ( packet.stream_index == m_audioStream ) {
166
+ if ( packet.pts != AV_NOPTS_VALUE ) m_audioPts = packet.pts;
167
+ m_audioFrame.reset();
168
+ unsigned char *data = packet.data;
169
+ int size = packet.size;
170
+ while ( size > 0 ) {
171
+ short int buffer[ ( AVCODEC_MAX_AUDIO_FRAME_SIZE +
172
+ FF_INPUT_BUFFER_PADDING_SIZE ) / sizeof( short int ) ];
173
+ int bufSize = sizeof( buffer );
174
+ int len = avcodec_decode_audio2( m_audioDec, &buffer[0], &bufSize,
175
+ data, size );
176
+ if ( len < 0 ) {
177
+ m_audioFrame.reset();
178
+ ERRORMACRO( false, Error, , "Error decoding audio frame of video \""
179
+ << m_mrl << "\"" );
180
+ };
181
+ data += len;
182
+ size -= len;
183
+ if ( bufSize > 0 ) {
184
+ if ( m_audioFrame.get() ) {
185
+ SequencePtr extended( new Sequence( m_audioFrame->size() + bufSize ) );
186
+ memcpy( extended->data(), m_audioFrame->data(), m_audioFrame->size() );
187
+ memcpy( extended->data() + m_audioFrame->size(), &buffer[0], bufSize );
188
+ m_audioFrame = extended;
189
+ } else {
190
+ m_audioFrame = SequencePtr( new Sequence( bufSize ) );
191
+ memcpy( m_audioFrame->data(), &buffer[0], bufSize );
192
+ };
193
+ };
126
194
  };
127
- };
128
- av_free_packet( &packet );
195
+ av_free_packet( &packet );
196
+ if ( m_audioFrame.get() ) break;
197
+ } else
198
+ av_free_packet( &packet );
129
199
  };
130
- ERRORMACRO( retVal.get(), Error, , "No more frames available" );
131
- return retVal;
200
+ ERRORMACRO( m_videoFrame.get() || m_audioFrame.get(), Error, ,
201
+ "No more frames available" );
132
202
  }
133
203
 
134
204
  bool AVInput::status(void) const
@@ -138,44 +208,65 @@ bool AVInput::status(void) const
138
208
 
139
209
  int AVInput::width(void) const throw (Error)
140
210
  {
141
- ERRORMACRO( m_dec != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
211
+ ERRORMACRO( m_videoDec != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
142
212
  "Did you call \"close\" before?" );
143
- return m_dec->width;
213
+ return m_videoDec->width;
144
214
  }
145
215
 
146
216
  int AVInput::height(void) const throw (Error)
147
217
  {
148
- ERRORMACRO( m_dec != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
218
+ ERRORMACRO( m_videoDec != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
149
219
  "Did you call \"close\" before?" );
150
- return m_dec->height;
220
+ return m_videoDec->height;
151
221
  }
152
222
 
153
- AVRational AVInput::timeBase(void) throw (Error)
223
+ AVRational AVInput::videoTimeBase(void) throw (Error)
154
224
  {
155
- ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
225
+ ERRORMACRO( m_videoStream != -1, Error, , "Video \"" << m_mrl << "\" is not open. "
156
226
  "Did you call \"close\" before?" );
157
- return m_ic->streams[ m_idx ]->time_base;
227
+ return m_ic->streams[ m_videoStream ]->time_base;
228
+ }
229
+
230
+ AVRational AVInput::audioTimeBase(void) throw (Error)
231
+ {
232
+ ERRORMACRO( m_audioStream != -1, Error, , "Audio \"" << m_mrl << "\" is not open. "
233
+ "Did you call \"close\" before?" );
234
+ return m_ic->streams[ m_audioStream ]->time_base;
158
235
  }
159
236
 
160
237
  AVRational AVInput::frameRate(void) throw (Error)
161
238
  {
162
239
  ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
163
240
  "Did you call \"close\" before?" );
164
- return m_ic->streams[ m_idx ]->r_frame_rate;
241
+ return m_ic->streams[ m_videoStream ]->r_frame_rate;
242
+ }
243
+
244
+ int AVInput::sampleRate(void) throw (Error)
245
+ {
246
+ ERRORMACRO( m_audioDec != NULL, Error, , "Audio \"" << m_mrl << "\" is not open. "
247
+ "Did you call \"close\" before?" );
248
+ return m_audioDec->sample_rate;
249
+ }
250
+
251
+ int AVInput::channels(void) throw (Error)
252
+ {
253
+ ERRORMACRO( m_audioDec != NULL, Error, , "Audio \"" << m_mrl << "\" is not open. "
254
+ "Did you call \"close\" before?" );
255
+ return m_audioDec->channels;
165
256
  }
166
257
 
167
258
  long long AVInput::duration(void) throw (Error)
168
259
  {
169
260
  ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
170
261
  "Did you call \"close\" before?" );
171
- return m_ic->streams[ m_idx ]->duration;
262
+ return m_ic->streams[ m_videoStream ]->duration;
172
263
  }
173
264
 
174
265
  long long AVInput::startTime(void) throw (Error)
175
266
  {
176
267
  ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
177
268
  "Did you call \"close\" before?" );
178
- return m_ic->streams[ m_idx ]->start_time;
269
+ return m_ic->streams[ m_videoStream ]->start_time;
179
270
  }
180
271
 
181
272
  void AVInput::seek( long long timestamp ) throw (Error)
@@ -184,14 +275,21 @@ void AVInput::seek( long long timestamp ) throw (Error)
184
275
  "Did you call \"close\" before?" );
185
276
  ERRORMACRO( av_seek_frame( m_ic, -1, timestamp, 0 ) >= 0,
186
277
  Error, , "Error seeking in video \"" << m_mrl << "\"" );
187
- avcodec_flush_buffers( m_dec );
278
+ avcodec_flush_buffers( m_videoDec );
188
279
  }
189
280
 
190
- long long AVInput::pts(void) throw (Error)
281
+ long long AVInput::videoPts(void) throw (Error)
191
282
  {
192
- ERRORMACRO( m_ic != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
283
+ ERRORMACRO( m_videoStream != -1, Error, , "Video \"" << m_mrl << "\" is not open. "
284
+ "Did you call \"close\" before?" );
285
+ return m_videoPts;
286
+ }
287
+
288
+ long long AVInput::audioPts(void) throw (Error)
289
+ {
290
+ ERRORMACRO( m_audioStream != -1, Error, , "Audio \"" << m_mrl << "\" is not open. "
193
291
  "Did you call \"close\" before?" );
194
- return m_pts;
292
+ return m_audioPts;
195
293
  }
196
294
 
197
295
  VALUE AVInput::registerRubyClass( VALUE rbModule )
@@ -202,16 +300,22 @@ VALUE AVInput::registerRubyClass( VALUE rbModule )
202
300
  rb_define_const( cRubyClass, "AV_TIME_BASE", INT2NUM( AV_TIME_BASE ) );
203
301
  rb_define_const( cRubyClass, "AV_NOPTS_VALUE", LL2NUM( AV_NOPTS_VALUE ) );
204
302
  rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
205
- rb_define_method( cRubyClass, "read", RUBY_METHOD_FUNC( wrapRead ), 0 );
303
+ rb_define_method( cRubyClass, "read_av", RUBY_METHOD_FUNC( wrapReadAV ), 0 );
206
304
  rb_define_method( cRubyClass, "status?", RUBY_METHOD_FUNC( wrapStatus ), 0 );
207
- rb_define_method( cRubyClass, "time_base", RUBY_METHOD_FUNC( wrapTimeBase ), 0 );
305
+ rb_define_method( cRubyClass, "video_time_base",
306
+ RUBY_METHOD_FUNC( wrapVideoTimeBase ), 0 );
307
+ rb_define_method( cRubyClass, "audio_time_base",
308
+ RUBY_METHOD_FUNC( wrapAudioTimeBase ), 0 );
208
309
  rb_define_method( cRubyClass, "frame_rate", RUBY_METHOD_FUNC( wrapFrameRate ), 0 );
310
+ rb_define_method( cRubyClass, "sample_rate", RUBY_METHOD_FUNC( wrapSampleRate ), 0 );
311
+ rb_define_method( cRubyClass, "channels", RUBY_METHOD_FUNC( wrapChannels ), 0 );
209
312
  rb_define_method( cRubyClass, "duration", RUBY_METHOD_FUNC( wrapDuration ), 0 );
210
313
  rb_define_method( cRubyClass, "start_time", RUBY_METHOD_FUNC( wrapStartTime ), 0 );
211
314
  rb_define_method( cRubyClass, "width", RUBY_METHOD_FUNC( wrapWidth ), 0 );
212
315
  rb_define_method( cRubyClass, "height", RUBY_METHOD_FUNC( wrapHeight ), 0 );
213
316
  rb_define_method( cRubyClass, "seek", RUBY_METHOD_FUNC( wrapSeek ), 1 );
214
- rb_define_method( cRubyClass, "pts", RUBY_METHOD_FUNC( wrapPTS ), 0 );
317
+ rb_define_method( cRubyClass, "video_pts", RUBY_METHOD_FUNC( wrapVideoPTS ), 0 );
318
+ rb_define_method( cRubyClass, "audio_pts", RUBY_METHOD_FUNC( wrapAudioPTS ), 0 );
215
319
  }
216
320
 
217
321
  void AVInput::deleteRubyObject( void *ptr )
@@ -240,13 +344,23 @@ VALUE AVInput::wrapClose( VALUE rbSelf )
240
344
  return rbSelf;
241
345
  }
242
346
 
243
- VALUE AVInput::wrapRead( VALUE rbSelf )
347
+ VALUE AVInput::wrapReadAV( VALUE rbSelf )
348
+ {
349
+ AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
350
+ return (*self)->wrapReadAVInst();
351
+ }
352
+
353
+ VALUE AVInput::wrapReadAVInst(void)
244
354
  {
245
355
  VALUE retVal = Qnil;
246
356
  try {
247
- AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
248
- FramePtr frame( (*self)->read() );
249
- retVal = frame->rubyObject();
357
+ readAV();
358
+ if ( m_videoFrame.get() )
359
+ retVal = m_videoFrame->rubyObject();
360
+ if ( m_audioFrame.get() )
361
+ retVal = m_audioFrame->rubyObject();
362
+ m_audioFrame.reset();
363
+ m_videoFrame.reset();
250
364
  } catch ( exception &e ) {
251
365
  rb_raise( rb_eRuntimeError, "%s", e.what() );
252
366
  };
@@ -259,14 +373,28 @@ VALUE AVInput::wrapStatus( VALUE rbSelf )
259
373
  return (*self)->status() ? Qtrue : Qfalse;
260
374
  }
261
375
 
262
- VALUE AVInput::wrapTimeBase( VALUE rbSelf )
376
+ VALUE AVInput::wrapVideoTimeBase( VALUE rbSelf )
263
377
  {
264
378
  VALUE retVal = Qnil;
265
379
  try {
266
380
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
267
- AVRational timeBase = (*self)->timeBase();
381
+ AVRational videoTimeBase = (*self)->videoTimeBase();
268
382
  retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
269
- INT2NUM( timeBase.num ), INT2NUM( timeBase.den ) );
383
+ INT2NUM( videoTimeBase.num ), INT2NUM( videoTimeBase.den ) );
384
+ } catch( exception &e ) {
385
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
386
+ };
387
+ return retVal;
388
+ }
389
+
390
+ VALUE AVInput::wrapAudioTimeBase( VALUE rbSelf )
391
+ {
392
+ VALUE retVal = Qnil;
393
+ try {
394
+ AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
395
+ AVRational audioTimeBase = (*self)->audioTimeBase();
396
+ retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
397
+ INT2NUM( audioTimeBase.num ), INT2NUM( audioTimeBase.den ) );
270
398
  } catch( exception &e ) {
271
399
  rb_raise( rb_eRuntimeError, "%s", e.what() );
272
400
  };
@@ -287,6 +415,30 @@ VALUE AVInput::wrapFrameRate( VALUE rbSelf )
287
415
  return retVal;
288
416
  }
289
417
 
418
+ VALUE AVInput::wrapSampleRate( VALUE rbSelf )
419
+ {
420
+ VALUE retVal = Qnil;
421
+ try {
422
+ AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
423
+ retVal = INT2NUM( (*self)->sampleRate() );
424
+ } catch( exception &e ) {
425
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
426
+ };
427
+ return retVal;
428
+ }
429
+
430
+ VALUE AVInput::wrapChannels( VALUE rbSelf )
431
+ {
432
+ VALUE retVal = Qnil;
433
+ try {
434
+ AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
435
+ retVal = INT2NUM( (*self)->channels() );
436
+ } catch( exception &e ) {
437
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
438
+ };
439
+ return retVal;
440
+ }
441
+
290
442
  VALUE AVInput::wrapDuration( VALUE rbSelf )
291
443
  {
292
444
  VALUE retVal = Qnil;
@@ -347,12 +499,24 @@ VALUE AVInput::wrapSeek( VALUE rbSelf, VALUE rbPos )
347
499
  return retVal;
348
500
  }
349
501
 
350
- VALUE AVInput::wrapPTS( VALUE rbSelf )
502
+ VALUE AVInput::wrapVideoPTS( VALUE rbSelf )
503
+ {
504
+ VALUE retVal = Qnil;
505
+ try {
506
+ AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
507
+ retVal = LL2NUM( (*self)->videoPts() );
508
+ } catch( exception &e ) {
509
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
510
+ };
511
+ return retVal;
512
+ }
513
+
514
+ VALUE AVInput::wrapAudioPTS( VALUE rbSelf )
351
515
  {
352
516
  VALUE retVal = Qnil;
353
517
  try {
354
518
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
355
- retVal = LL2NUM( (*self)->pts() );
519
+ retVal = LL2NUM( (*self)->audioPts() );
356
520
  } catch( exception &e ) {
357
521
  rb_raise( rb_eRuntimeError, "%s", e.what() );
358
522
  };
data/ext/avinput.hh CHANGED
@@ -36,6 +36,7 @@ extern "C" {
36
36
  #include "rubyinc.hh"
37
37
  #include "error.hh"
38
38
  #include "frame.hh"
39
+ #include "sequence.hh"
39
40
 
40
41
  class AVInput
41
42
  {
@@ -43,43 +44,58 @@ public:
43
44
  AVInput( const std::string &mrl ) throw (Error);
44
45
  virtual ~AVInput(void);
45
46
  void close(void);
46
- FramePtr read(void) throw (Error);
47
+ void readAV(void) throw (Error);
47
48
  bool status(void) const;
48
49
  int width(void) const throw (Error);
49
50
  int height(void) const throw (Error);
50
- AVRational timeBase(void) throw (Error);
51
+ AVRational videoTimeBase(void) throw (Error);
52
+ AVRational audioTimeBase(void) throw (Error);
51
53
  AVRational frameRate(void) throw (Error);
54
+ int sampleRate(void) throw (Error);
55
+ int channels(void) throw (Error);
52
56
  long long duration(void) throw (Error);
53
57
  long long startTime(void) throw (Error);
54
58
  void seek( long long timestamp ) throw (Error);
55
- long long pts(void) throw (Error);
59
+ long long videoPts(void) throw (Error);
60
+ long long audioPts(void) throw (Error);
56
61
  static VALUE cRubyClass;
57
62
  static VALUE registerRubyClass( VALUE rbModule );
58
63
  static void deleteRubyObject( void *ptr );
59
64
  static VALUE wrapNew( VALUE rbClass, VALUE rbMRL );
60
65
  static VALUE wrapClose( VALUE rbSelf );
61
- static VALUE wrapRead( VALUE rbSelf );
66
+ static VALUE wrapReadAV( VALUE rbSelf );
67
+ VALUE wrapReadAVInst(void);
62
68
  static VALUE wrapStatus( VALUE rbSelf );
63
- static VALUE wrapTimeBase( VALUE rbSelf );
69
+ static VALUE wrapVideoTimeBase( VALUE rbSelf );
70
+ static VALUE wrapAudioTimeBase( VALUE rbSelf );
64
71
  static VALUE wrapFrameRate( VALUE rbSelf );
72
+ static VALUE wrapSampleRate( VALUE rbSelf );
73
+ static VALUE wrapChannels( VALUE rbSelf );
65
74
  static VALUE wrapDuration( VALUE rbSelf );
66
75
  static VALUE wrapStartTime( VALUE rbSelf );
67
76
  static VALUE wrapWidth( VALUE rbSelf );
68
77
  static VALUE wrapHeight( VALUE rbSelf );
69
78
  static VALUE wrapSeek( VALUE rbSelf, VALUE rbPos );
70
- static VALUE wrapPTS( VALUE rbSelf );
79
+ static VALUE wrapVideoPTS( VALUE rbSelf );
80
+ static VALUE wrapAudioPTS( VALUE rbSelf );
71
81
  protected:
72
82
  std::string m_mrl;
73
83
  AVFormatContext *m_ic;
74
- AVCodecContext *m_dec;
75
- AVCodec *m_codec;
76
- int m_idx;
77
- long long m_pts;
84
+ AVCodecContext *m_videoDec;
85
+ AVCodecContext *m_audioDec;
86
+ AVCodec *m_videoCodec;
87
+ AVCodec *m_audioCodec;
88
+ int m_videoStream;
89
+ int m_audioStream;
90
+ long long m_videoPts;
91
+ long long m_audioPts;
78
92
  struct SwsContext *m_swsContext;
79
- AVFrame *m_frame;
80
- boost::shared_array< char > m_data;
93
+ AVFrame *m_avFrame;
94
+ FramePtr m_videoFrame;
95
+ SequencePtr m_audioFrame;
81
96
  };
82
97
 
83
98
  typedef boost::shared_ptr< AVInput > AVInputPtr;
84
99
 
85
100
  #endif
101
+
data/ext/avoutput.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 <cerrno>
16
17
  #include "avoutput.hh"
17
18
 
18
19
  #if !defined(INT64_C)
@@ -62,11 +63,11 @@ AVOutput::AVOutput( const string &mrl, int bitrate, int width, int height,
62
63
  if ( m_oc->oformat->flags & AVFMT_GLOBALHEADER )
63
64
  c->flags |= CODEC_FLAG_GLOBAL_HEADER;
64
65
  ERRORMACRO( av_set_parameters( m_oc, NULL ) >= 0, Error, ,
65
- "Invalid output format parameters" );
66
+ "Invalid output format parameters: " << strerror( errno ) );
66
67
  AVCodec *codec = avcodec_find_encoder( c->codec_id );
67
68
  ERRORMACRO( codec != NULL, Error, , "Could not find codec " << c->codec_id );
68
69
  ERRORMACRO( avcodec_open( c, codec ) >= 0, Error, ,
69
- "Error opening codec " << c->codec_id );
70
+ "Error opening codec " << c->codec_id << ": " << strerror( errno ) );
70
71
  m_video_codec_open = true;
71
72
  if ( !( m_oc->oformat->flags & AVFMT_RAWPICTURE ) ) {
72
73
  m_video_buf = (char *)av_malloc( VIDEO_BUF_SIZE );
@@ -79,7 +80,8 @@ AVOutput::AVOutput( const string &mrl, int bitrate, int width, int height,
79
80
  m_file_open = true;
80
81
  };
81
82
  ERRORMACRO( av_write_header( m_oc ) >= 0, Error, ,
82
- "Error writing header of video \"" << mrl << "\"" );
83
+ "Error writing header of video \"" << mrl << "\": "
84
+ << strerror( errno ) );
83
85
  m_header_written = true;
84
86
  m_swsContext = sws_getContext( width, height, PIX_FMT_YUV420P,
85
87
  width, height, PIX_FMT_YUV420P,
@@ -184,7 +186,8 @@ void AVOutput::write( FramePtr frame ) throw (Error)
184
186
  packet.data = (uint8_t *)m_video_buf;
185
187
  packet.size = packetSize;
186
188
  ERRORMACRO( av_interleaved_write_frame( m_oc, &packet ) >= 0, Error, ,
187
- "Error writing frame of video \"" << m_mrl << "\"" );
189
+ "Error writing frame of video \"" << m_mrl << "\": "
190
+ << strerror( errno ) );
188
191
  };
189
192
  };
190
193
  }
data/ext/sequence.cc ADDED
@@ -0,0 +1,50 @@
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 "sequence.hh"
17
+
18
+ using namespace std;
19
+
20
+ Sequence::Sequence( int size ):
21
+ m_sequence( Qnil )
22
+ {
23
+ VALUE mModule = rb_define_module( "Hornetseye" );
24
+ VALUE cMalloc = rb_define_class_under( mModule, "Malloc", rb_cObject );
25
+ VALUE cSequence = rb_define_class_under( mModule, "Sequence", rb_cObject );
26
+ VALUE rbSize = INT2NUM( size );
27
+ VALUE rbMemory = rb_funcall( cMalloc, rb_intern( "new" ), 1, rbSize );
28
+ m_sequence = rb_funcall( cSequence, rb_intern( "import" ), 3,
29
+ rb_const_get( mModule, rb_intern( "UBYTE" ) ),
30
+ rbMemory, rbSize );
31
+ }
32
+
33
+ int Sequence::size(void)
34
+ {
35
+ return NUM2INT( rb_funcall( m_sequence, rb_intern( "size" ), 0 ) );
36
+ }
37
+
38
+ char *Sequence::data(void)
39
+ {
40
+ VALUE rbMemory = rb_funcall( m_sequence, rb_intern( "memory" ), 0 );
41
+ char *ptr;
42
+ Data_Get_Struct( rbMemory, char, ptr );
43
+ return ptr;
44
+ }
45
+
46
+ void Sequence::markRubyMember(void)
47
+ {
48
+ rb_gc_mark( m_sequence );
49
+ }
50
+
data/ext/sequence.hh ADDED
@@ -0,0 +1,39 @@
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 SEQUENCE_HH
17
+ #define SEQUENCE_HH
18
+
19
+ #include <boost/smart_ptr.hpp>
20
+ #include "rubyinc.hh"
21
+ #include <string>
22
+
23
+ class Sequence
24
+ {
25
+ public:
26
+ Sequence( int size );
27
+ Sequence( VALUE rbSequence ): m_sequence( rbSequence ) {}
28
+ virtual ~Sequence(void) {}
29
+ int size(void);
30
+ char *data(void);
31
+ VALUE rubyObject(void) { return m_sequence; }
32
+ void markRubyMember(void);
33
+ protected:
34
+ VALUE m_sequence;
35
+ };
36
+
37
+ typedef boost::shared_ptr< Sequence > SequencePtr;
38
+
39
+ #endif
@@ -25,7 +25,13 @@ module Hornetseye
25
25
 
26
26
  def new( mrl )
27
27
  retval = orig_new mrl
28
- retval.instance_eval { @frame = nil }
28
+ retval.instance_eval do
29
+ @frame = nil
30
+ @video = Queue.new
31
+ @audio = Queue.new
32
+ @video_pts = AV_NOPTS_VALUE
33
+ @audio_pts = AV_NOPTS_VALUE
34
+ end
29
35
  retval
30
36
  end
31
37
 
@@ -35,36 +41,62 @@ module Hornetseye
35
41
  [ width, height ]
36
42
  end
37
43
 
38
- alias_method :orig_read, :read
44
+ def read_video
45
+ enqueue_frame while @video.empty?
46
+ frame, @video_pts = @video.deq
47
+ frame
48
+ end
49
+
50
+ alias_method :read, :read_video
39
51
 
40
- def read
41
- @frame = orig_read
52
+ def read_audio
53
+ enqueue_frame while @audio.empty?
54
+ frame, @audio_pts = @audio.deq
55
+ frame
56
+ end
57
+
58
+ def enqueue_frame
59
+ frame = read_av
60
+ if frame.is_a? Frame_
61
+ @video.enq [ frame, video_pts ]
62
+ @frame = frame
63
+ else
64
+ n = channels
65
+ target = Hornetseye::MultiArray SINT, n, frame.size / ( 2 * n )
66
+ @audio.enq [ target.new( frame.memory ), audio_pts ]
67
+ end
42
68
  end
43
69
 
44
70
  def pos=( timestamp )
45
71
  unless @frame
46
72
  begin
47
- read
73
+ read_video
48
74
  rescue Exception
49
75
  end
50
76
  end
51
77
  seek timestamp * AV_TIME_BASE
52
78
  end
53
79
 
54
- def pos
55
- pts == AV_NOPTS_VALUE ? nil : pts * time_base
80
+ def video_pos
81
+ @video_pts == AV_NOPTS_VALUE ? nil : @video_pts * video_time_base
82
+ end
83
+
84
+ alias_method :pos, :video_pos
85
+
86
+ def audio_pos
87
+ @audio_pts == AV_NOPTS_VALUE ? nil : @audio_pts * audio_time_base
56
88
  end
57
89
 
58
90
  alias_method :orig_duration, :duration
59
91
 
60
92
  def duration
61
- orig_duration == AV_NOPTS_VALUE ? nil : orig_duration * time_base
93
+ orig_duration == AV_NOPTS_VALUE ? nil : orig_duration * video_time_base
62
94
  end
63
95
 
64
96
  alias_method :orig_start_time, :start_time
65
97
 
66
- def start_time
67
- orig_start_time == AV_NOPTS_VALUE ? nil : orig_start_time * time_base
98
+ def video_start_time
99
+ orig_start_time == AV_NOPTS_VALUE ? nil : orig_start_time * video_time_base
68
100
  end
69
101
 
70
102
  end
@@ -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
 
17
+ require 'thread'
17
18
  require 'hornetseye_frame'
18
19
  require 'hornetseye-ffmpeg/avinput'
19
20
  require 'hornetseye-ffmpeg/avoutput'
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 4
8
- - 3
9
- version: 0.4.3
7
+ - 5
8
+ - 1
9
+ version: 0.5.1
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-10-16 00:00:00 +01:00
17
+ date: 2010-10-19 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -27,8 +27,8 @@ dependencies:
27
27
  - !ruby/object:Gem::Version
28
28
  segments:
29
29
  - 1
30
- - 1
31
- version: "1.1"
30
+ - 2
31
+ version: "1.2"
32
32
  type: :runtime
33
33
  version_requirements: *id001
34
34
  - !ruby/object:Gem::Dependency
@@ -41,8 +41,8 @@ dependencies:
41
41
  - !ruby/object:Gem::Version
42
42
  segments:
43
43
  - 0
44
- - 6
45
- version: "0.6"
44
+ - 11
45
+ version: "0.11"
46
46
  type: :runtime
47
47
  version_requirements: *id002
48
48
  - !ruby/object:Gem::Dependency
@@ -55,8 +55,8 @@ dependencies:
55
55
  - !ruby/object:Gem::Version
56
56
  segments:
57
57
  - 0
58
- - 3
59
- version: "0.3"
58
+ - 7
59
+ version: "0.7"
60
60
  type: :runtime
61
61
  version_requirements: *id003
62
62
  - !ruby/object:Gem::Dependency
@@ -92,7 +92,9 @@ files:
92
92
  - ext/avinput.cc
93
93
  - ext/init.cc
94
94
  - ext/frame.cc
95
+ - ext/sequence.cc
95
96
  - ext/frame.hh
97
+ - ext/sequence.hh
96
98
  - ext/avoutput.hh
97
99
  - ext/avinput.hh
98
100
  - ext/rubyinc.hh