hornetseye-ffmpeg 0.4.3 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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