hornetseye-ffmpeg 0.5.4 → 0.6.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
@@ -7,15 +7,15 @@ require 'rake/loaders/makefile'
7
7
  require 'rbconfig'
8
8
 
9
9
  PKG_NAME = 'hornetseye-ffmpeg'
10
- PKG_VERSION = '0.5.4'
10
+ PKG_VERSION = '0.6.0'
11
+ CFG = RbConfig::CONFIG
11
12
  CXX = ENV[ 'CXX' ] || 'g++'
12
- STRIP = ENV[ 'STRIP' ] || 'strip'
13
13
  RB_FILES = FileList[ 'lib/**/*.rb' ]
14
14
  CC_FILES = FileList[ 'ext/*.cc' ]
15
15
  HH_FILES = FileList[ 'ext/*.hh' ] + FileList[ 'ext/*.tcc' ]
16
16
  TC_FILES = FileList[ 'test/tc_*.rb' ]
17
17
  TS_FILES = FileList[ 'test/ts_*.rb' ]
18
- SO_FILE = "ext/#{PKG_NAME.tr '\-', '_'}.so"
18
+ SO_FILE = "ext/#{PKG_NAME.tr '\-', '_'}.#{CFG[ 'DLEXT' ]}"
19
19
  PKG_FILES = [ 'Rakefile', 'README.md', 'COPYING', '.document' ] +
20
20
  RB_FILES + CC_FILES + HH_FILES + TS_FILES + TC_FILES
21
21
  BIN_FILES = [ 'README.md', 'COPYING', '.document', SO_FILE ] +
@@ -27,17 +27,18 @@ EMAIL = %q{jan@wedesoft.de}
27
27
  HOMEPAGE = %q{http://wedesoft.github.com/hornetseye-ffmpeg/}
28
28
 
29
29
  OBJ = CC_FILES.ext 'o'
30
- $CXXFLAGS = ENV[ 'CXXFLAGS' ] || ''
31
- $CXXFLAGS = "#{$CXXFLAGS} -fPIC -DNDEBUG -DHAVE_CONFIG_H"
32
- if RbConfig::CONFIG[ 'rubyhdrdir' ]
33
- $CXXFLAGS = "#{$CXXFLAGS} -I#{RbConfig::CONFIG[ 'rubyhdrdir' ]} " +
34
- "-I#{RbConfig::CONFIG[ 'rubyhdrdir' ]}/#{RbConfig::CONFIG[ 'arch' ]}"
30
+ $CXXFLAGS = "-DNDEBUG -DHAVE_CONFIG_H #{CFG[ 'CPPFLAGS' ]} #{CFG[ 'CFLAGS' ]}"
31
+ if CFG[ 'rubyhdrdir' ]
32
+ $CXXFLAGS = "#{$CXXFLAGS} -I#{CFG[ 'rubyhdrdir' ]} " +
33
+ "-I#{CFG[ 'rubyhdrdir' ]}/#{CFG[ 'arch' ]}"
35
34
  else
36
- $CXXFLAGS = "#{$CXXFLAGS} -I#{RbConfig::CONFIG[ 'archdir' ]}"
35
+ $CXXFLAGS = "#{$CXXFLAGS} -I#{CFG[ 'archdir' ]}"
37
36
  end
38
- $LIBRUBYARG = RbConfig::CONFIG[ 'LIBRUBYARG' ]
39
- $SITELIBDIR = RbConfig::CONFIG[ 'sitelibdir' ]
40
- $SITEARCHDIR = RbConfig::CONFIG[ 'sitearchdir' ]
37
+ $LIBRUBYARG = "-L#{CFG[ 'libdir' ]} #{CFG[ 'LIBRUBYARG' ]} #{CFG[ 'LDFLAGS' ]} " +
38
+ "#{CFG[ 'SOLIBS' ]} #{CFG[ 'DLDLIBS' ]}"
39
+ $SITELIBDIR = CFG[ 'sitelibdir' ]
40
+ $SITEARCHDIR = CFG[ 'sitearchdir' ]
41
+ $LDSHARED = CFG[ 'LDSHARED' ][ CFG[ 'LDSHARED' ].index( ' ' ) .. -1 ]
41
42
 
42
43
  task :default => :all
43
44
 
@@ -46,7 +47,6 @@ task :all => [ SO_FILE ]
46
47
 
47
48
  file SO_FILE => OBJ do |t|
48
49
  sh "#{CXX} -shared -o #{t.name} #{OBJ} -lavformat -lswscale #{$LIBRUBYARG}"
49
- sh "#{STRIP} --strip-all #{t.name}"
50
50
  end
51
51
 
52
52
  task :test => [ SO_FILE ]
@@ -209,7 +209,7 @@ begin
209
209
  s.rdoc_options = %w{--no-private}
210
210
  s.add_dependency %<malloc>, [ '~> 1.2' ]
211
211
  s.add_dependency %<multiarray>, [ '~> 0.11' ]
212
- s.add_dependency %<hornetseye-frame>, [ '~> 0.7' ]
212
+ s.add_dependency %<hornetseye-frame>, [ '~> 0.8' ]
213
213
  s.add_development_dependency %q{rake}
214
214
  end
215
215
  GEM_SOURCE = "#{PKG_NAME}-#{PKG_VERSION}.gem"
@@ -232,7 +232,7 @@ begin
232
232
  s.rdoc_options = %w{--no-private}
233
233
  s.add_dependency %<malloc>, [ '~> 1.2' ]
234
234
  s.add_dependency %<multiarray>, [ '~> 0.11' ]
235
- s.add_dependency %<hornetseye-frame>, [ '~> 0.7' ]
235
+ s.add_dependency %<hornetseye-frame>, [ '~> 0.8' ]
236
236
  end
237
237
  GEM_BINARY = "#{PKG_NAME}-#{PKG_VERSION}-#{$BINSPEC.platform}.gem"
238
238
  desc "Build the gem file #{GEM_SOURCE}"
@@ -264,7 +264,7 @@ rule '.o' => '.cc' do |t|
264
264
  end
265
265
 
266
266
  file ".depends.mf" => :config_h do |t|
267
- sh "g++ -MM #{$CXXFLAGS} #{CC_FILES.join ' '} | " +
267
+ sh "g++ -MM #{CC_FILES.join ' '} | " +
268
268
  "sed -e :a -e N -e 's/\\n/\\$/g' -e ta | " +
269
269
  "sed -e 's/ *\\\\\\$ */ /g' -e 's/\\$/\\n/g' | sed -e 's/^/ext\\//' > #{t.name}"
270
270
  end
data/ext/avinput.cc CHANGED
@@ -29,7 +29,7 @@ using namespace std;
29
29
 
30
30
  VALUE AVInput::cRubyClass = Qnil;
31
31
 
32
- AVInput::AVInput( const string &mrl ) throw (Error):
32
+ AVInput::AVInput( const string &mrl, bool audio ) throw (Error):
33
33
  m_mrl( mrl ), m_ic( NULL ), m_videoDec( NULL ), m_audioDec( NULL ),
34
34
  m_videoCodec( NULL ), m_audioCodec( NULL ),
35
35
  m_videoStream( -1 ), m_audioStream( -1 ), m_videoPts( 0 ), m_audioPts( 0 ),
@@ -42,11 +42,11 @@ AVInput::AVInput( const string &mrl ) throw (Error):
42
42
  err = av_find_stream_info( m_ic );
43
43
  ERRORMACRO( err >= 0, Error, , "Error finding stream info for file \""
44
44
  << mrl << "\": " << strerror( errno ) );
45
- for ( int i=0; i<m_ic->nb_streams; i++ ) {
45
+ for ( unsigned int i=0; i<m_ic->nb_streams; i++ ) {
46
46
  if ( m_ic->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
47
47
  m_videoStream = i;
48
- if ( m_ic->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
49
- m_audioStream = i;
48
+ if ( audio && m_ic->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
49
+ m_audioStream = i;
50
50
  };
51
51
  #ifndef NDEBUG
52
52
  cerr << "Video stream index is " << m_videoStream << endl;
@@ -150,14 +150,19 @@ void AVInput::readAV(void) throw (Error)
150
150
  AVFrame picture;
151
151
  m_videoFrame = FramePtr( new Frame( "YV12", m_videoDec->width,
152
152
  m_videoDec->height ) );
153
+ int
154
+ width = m_videoDec->width,
155
+ height = m_videoDec->height,
156
+ width2 = ( width + 1 ) / 2,
157
+ height2 = ( height + 1 ) / 2,
158
+ widtha = ( width + 7 ) & ~0x7,
159
+ width2a = ( width2 + 7 ) & ~0x7;
153
160
  picture.data[0] = (uint8_t *)m_videoFrame->data();
154
- picture.data[1] = (uint8_t *)m_videoFrame->data() +
155
- m_videoDec->width * m_videoDec->height * 5 / 4;
156
- picture.data[2] = (uint8_t *)m_videoFrame->data() +
157
- m_videoDec->width * m_videoDec->height;
158
- picture.linesize[0] = m_videoDec->width;
159
- picture.linesize[1] = m_videoDec->width / 2;
160
- picture.linesize[2] = m_videoDec->width / 2;
161
+ picture.data[2] = (uint8_t *)m_videoFrame->data() + widtha * height;
162
+ picture.data[1] = (uint8_t *)picture.data[2] + width2a * height2;
163
+ picture.linesize[0] = widtha;
164
+ picture.linesize[1] = width2a;
165
+ picture.linesize[2] = width2a;
161
166
  sws_scale( m_swsContext, m_avFrame->data, m_avFrame->linesize, 0,
162
167
  m_videoDec->height, picture.data, picture.linesize );
163
168
  break;
@@ -223,6 +228,11 @@ int AVInput::height(void) const throw (Error)
223
228
  return m_videoDec->height;
224
229
  }
225
230
 
231
+ bool AVInput::hasAudio(void) const
232
+ {
233
+ return m_audioStream != -1;
234
+ }
235
+
226
236
  AVRational AVInput::videoTimeBase(void) throw (Error)
227
237
  {
228
238
  ERRORMACRO( m_videoStream != -1, Error, , "Video \"" << m_mrl << "\" is not open. "
@@ -306,7 +316,7 @@ VALUE AVInput::registerRubyClass( VALUE rbModule )
306
316
  {
307
317
  cRubyClass = rb_define_class_under( rbModule, "AVInput", rb_cObject );
308
318
  rb_define_singleton_method( cRubyClass, "new",
309
- RUBY_METHOD_FUNC( wrapNew ), 1 );
319
+ RUBY_METHOD_FUNC( wrapNew ), 2 );
310
320
  rb_define_const( cRubyClass, "AV_TIME_BASE", INT2NUM( AV_TIME_BASE ) );
311
321
  rb_define_const( cRubyClass, "AV_NOPTS_VALUE", LL2NUM( AV_NOPTS_VALUE ) );
312
322
  rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
@@ -325,6 +335,7 @@ VALUE AVInput::registerRubyClass( VALUE rbModule )
325
335
  rb_define_method( cRubyClass, "start_time", RUBY_METHOD_FUNC( wrapStartTime ), 0 );
326
336
  rb_define_method( cRubyClass, "width", RUBY_METHOD_FUNC( wrapWidth ), 0 );
327
337
  rb_define_method( cRubyClass, "height", RUBY_METHOD_FUNC( wrapHeight ), 0 );
338
+ rb_define_method( cRubyClass, "has_audio?", RUBY_METHOD_FUNC( wrapHasAudio ), 0 );
328
339
  rb_define_method( cRubyClass, "seek", RUBY_METHOD_FUNC( wrapSeek ), 1 );
329
340
  rb_define_method( cRubyClass, "video_pts", RUBY_METHOD_FUNC( wrapVideoPTS ), 0 );
330
341
  rb_define_method( cRubyClass, "audio_pts", RUBY_METHOD_FUNC( wrapAudioPTS ), 0 );
@@ -335,12 +346,12 @@ void AVInput::deleteRubyObject( void *ptr )
335
346
  delete (AVInputPtr *)ptr;
336
347
  }
337
348
 
338
- VALUE AVInput::wrapNew( VALUE rbClass, VALUE rbMRL )
349
+ VALUE AVInput::wrapNew( VALUE rbClass, VALUE rbMRL, VALUE rbAudio )
339
350
  {
340
351
  VALUE retVal = Qnil;
341
352
  try {
342
353
  rb_check_type( rbMRL, T_STRING );
343
- AVInputPtr ptr( new AVInput( StringValuePtr( rbMRL ) ) );
354
+ AVInputPtr ptr( new AVInput( StringValuePtr( rbMRL ), rbAudio == Qtrue ) );
344
355
  retVal = Data_Wrap_Struct( rbClass, 0, deleteRubyObject,
345
356
  new AVInputPtr( ptr ) );
346
357
  } catch ( exception &e ) {
@@ -393,7 +404,7 @@ VALUE AVInput::wrapVideoTimeBase( VALUE rbSelf )
393
404
  AVRational videoTimeBase = (*self)->videoTimeBase();
394
405
  retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
395
406
  INT2NUM( videoTimeBase.num ), INT2NUM( videoTimeBase.den ) );
396
- } catch( exception &e ) {
407
+ } catch ( exception &e ) {
397
408
  rb_raise( rb_eRuntimeError, "%s", e.what() );
398
409
  };
399
410
  return retVal;
@@ -407,7 +418,7 @@ VALUE AVInput::wrapAudioTimeBase( VALUE rbSelf )
407
418
  AVRational audioTimeBase = (*self)->audioTimeBase();
408
419
  retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
409
420
  INT2NUM( audioTimeBase.num ), INT2NUM( audioTimeBase.den ) );
410
- } catch( exception &e ) {
421
+ } catch ( exception &e ) {
411
422
  rb_raise( rb_eRuntimeError, "%s", e.what() );
412
423
  };
413
424
  return retVal;
@@ -421,7 +432,7 @@ VALUE AVInput::wrapFrameRate( VALUE rbSelf )
421
432
  AVRational frameRate = (*self)->frameRate();
422
433
  retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
423
434
  INT2NUM( frameRate.num ), INT2NUM( frameRate.den ) );
424
- } catch( exception &e ) {
435
+ } catch ( exception &e ) {
425
436
  rb_raise( rb_eRuntimeError, "%s", e.what() );
426
437
  };
427
438
  return retVal;
@@ -435,7 +446,7 @@ VALUE AVInput::wrapAspectRatio( VALUE rbSelf )
435
446
  AVRational aspectRatio = (*self)->aspectRatio();
436
447
  retVal = rb_funcall( rb_cObject, rb_intern( "Rational" ), 2,
437
448
  INT2NUM( aspectRatio.num ), INT2NUM( aspectRatio.den ) );
438
- } catch( exception &e ) {
449
+ } catch ( exception &e ) {
439
450
  rb_raise( rb_eRuntimeError, "%s", e.what() );
440
451
  };
441
452
  return retVal;
@@ -447,7 +458,7 @@ VALUE AVInput::wrapSampleRate( VALUE rbSelf )
447
458
  try {
448
459
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
449
460
  retVal = INT2NUM( (*self)->sampleRate() );
450
- } catch( exception &e ) {
461
+ } catch ( exception &e ) {
451
462
  rb_raise( rb_eRuntimeError, "%s", e.what() );
452
463
  };
453
464
  return retVal;
@@ -459,7 +470,7 @@ VALUE AVInput::wrapChannels( VALUE rbSelf )
459
470
  try {
460
471
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
461
472
  retVal = INT2NUM( (*self)->channels() );
462
- } catch( exception &e ) {
473
+ } catch ( exception &e ) {
463
474
  rb_raise( rb_eRuntimeError, "%s", e.what() );
464
475
  };
465
476
  return retVal;
@@ -471,7 +482,7 @@ VALUE AVInput::wrapDuration( VALUE rbSelf )
471
482
  try {
472
483
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
473
484
  retVal = LL2NUM( (*self)->duration() );
474
- } catch( exception &e ) {
485
+ } catch ( exception &e ) {
475
486
  rb_raise( rb_eRuntimeError, "%s", e.what() );
476
487
  };
477
488
  return retVal;
@@ -483,7 +494,7 @@ VALUE AVInput::wrapStartTime( VALUE rbSelf )
483
494
  try {
484
495
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
485
496
  retVal = LL2NUM( (*self)->startTime() );
486
- } catch( exception &e ) {
497
+ } catch ( exception &e ) {
487
498
  rb_raise( rb_eRuntimeError, "%s", e.what() );
488
499
  };
489
500
  return retVal;
@@ -495,7 +506,7 @@ VALUE AVInput::wrapWidth( VALUE rbSelf )
495
506
  try {
496
507
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
497
508
  retVal = INT2NUM( (*self)->width() );
498
- } catch( exception &e ) {
509
+ } catch ( exception &e ) {
499
510
  rb_raise( rb_eRuntimeError, "%s", e.what() );
500
511
  };
501
512
  return retVal;
@@ -507,19 +518,25 @@ VALUE AVInput::wrapHeight( VALUE rbSelf )
507
518
  try {
508
519
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
509
520
  retVal = INT2NUM( (*self)->height() );
510
- } catch( exception &e ) {
521
+ } catch ( exception &e ) {
511
522
  rb_raise( rb_eRuntimeError, "%s", e.what() );
512
523
  };
513
524
  return retVal;
514
525
  }
515
526
 
527
+ VALUE AVInput::wrapHasAudio( VALUE rbSelf )
528
+ {
529
+ AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
530
+ return (*self)->hasAudio() ? Qtrue : Qfalse;
531
+ }
532
+
516
533
  VALUE AVInput::wrapSeek( VALUE rbSelf, VALUE rbPos )
517
534
  {
518
535
  VALUE retVal = Qnil;
519
536
  try {
520
537
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
521
538
  (*self)->seek( NUM2LL( rbPos ) );
522
- } catch( exception &e ) {
539
+ } catch ( exception &e ) {
523
540
  rb_raise( rb_eRuntimeError, "%s", e.what() );
524
541
  };
525
542
  return retVal;
@@ -531,7 +548,7 @@ VALUE AVInput::wrapVideoPTS( VALUE rbSelf )
531
548
  try {
532
549
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
533
550
  retVal = LL2NUM( (*self)->videoPts() );
534
- } catch( exception &e ) {
551
+ } catch ( exception &e ) {
535
552
  rb_raise( rb_eRuntimeError, "%s", e.what() );
536
553
  };
537
554
  return retVal;
@@ -543,7 +560,7 @@ VALUE AVInput::wrapAudioPTS( VALUE rbSelf )
543
560
  try {
544
561
  AVInputPtr *self; Data_Get_Struct( rbSelf, AVInputPtr, self );
545
562
  retVal = LL2NUM( (*self)->audioPts() );
546
- } catch( exception &e ) {
563
+ } catch ( exception &e ) {
547
564
  rb_raise( rb_eRuntimeError, "%s", e.what() );
548
565
  };
549
566
  return retVal;
data/ext/avinput.hh CHANGED
@@ -41,13 +41,14 @@ extern "C" {
41
41
  class AVInput
42
42
  {
43
43
  public:
44
- AVInput( const std::string &mrl ) throw (Error);
44
+ AVInput( const std::string &mrl, bool audio = true ) throw (Error);
45
45
  virtual ~AVInput(void);
46
46
  void close(void);
47
47
  void readAV(void) throw (Error);
48
48
  bool status(void) const;
49
49
  int width(void) const throw (Error);
50
50
  int height(void) const throw (Error);
51
+ bool hasAudio(void) const;
51
52
  AVRational videoTimeBase(void) throw (Error);
52
53
  AVRational audioTimeBase(void) throw (Error);
53
54
  AVRational frameRate(void) throw (Error);
@@ -62,7 +63,7 @@ public:
62
63
  static VALUE cRubyClass;
63
64
  static VALUE registerRubyClass( VALUE rbModule );
64
65
  static void deleteRubyObject( void *ptr );
65
- static VALUE wrapNew( VALUE rbClass, VALUE rbMRL );
66
+ static VALUE wrapNew( VALUE rbClass, VALUE rbMRL, VALUE rbAudio );
66
67
  static VALUE wrapClose( VALUE rbSelf );
67
68
  static VALUE wrapReadAV( VALUE rbSelf );
68
69
  VALUE wrapReadAVInst(void);
@@ -77,6 +78,7 @@ public:
77
78
  static VALUE wrapStartTime( VALUE rbSelf );
78
79
  static VALUE wrapWidth( VALUE rbSelf );
79
80
  static VALUE wrapHeight( VALUE rbSelf );
81
+ static VALUE wrapHasAudio( VALUE rbSelf );
80
82
  static VALUE wrapSeek( VALUE rbSelf, VALUE rbPos );
81
83
  static VALUE wrapVideoPTS( VALUE rbSelf );
82
84
  static VALUE wrapAudioPTS( VALUE rbSelf );
data/ext/avoutput.cc CHANGED
@@ -14,22 +14,28 @@
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 <cerrno>
17
+ #ifndef NDEBUG
18
+ #include <iostream>
19
+ #endif
17
20
  #include "avoutput.hh"
18
21
 
19
22
  #if !defined(INT64_C)
20
23
  #define INT64_C(c) c ## LL
21
24
  #endif
22
25
 
23
- #define VIDEO_BUF_SIZE 200000
26
+ #define VIDEO_BUF_SIZE ( 16 * FF_MIN_BUFFER_SIZE )
27
+ #define AUDIO_BUF_SIZE ( 2 * FF_MIN_BUFFER_SIZE )
24
28
 
25
29
  using namespace std;
26
30
 
27
31
  VALUE AVOutput::cRubyClass = Qnil;
28
32
 
29
- AVOutput::AVOutput( const string &mrl, int bitrate, int width, int height,
30
- int timeBaseNum, int timeBaseDen ) throw (Error):
31
- m_mrl( mrl ), m_oc( NULL ), m_video_st( NULL ), m_video_codec_open( false ),
32
- m_video_buf( NULL ), m_file_open( false ), m_header_written( false ),
33
+ AVOutput::AVOutput( const string &mrl, int videoBitRate, int width, int height,
34
+ int timeBaseNum, int timeBaseDen, int audioBitRate,
35
+ int sampleRate, int channels ) throw (Error):
36
+ m_mrl( mrl ), m_oc( NULL ), m_video_st( NULL ), m_audio_st( NULL),
37
+ m_video_codec_open( false ), m_audio_codec_open( false ), m_video_buf( NULL ),
38
+ m_audio_buf( NULL ), m_file_open( false ), m_header_written( false ),
33
39
  m_swsContext( NULL ), m_frame( NULL )
34
40
  {
35
41
  try {
@@ -53,7 +59,7 @@ AVOutput::AVOutput( const string &mrl, int bitrate, int width, int height,
53
59
  AVCodecContext *c = m_video_st->codec;
54
60
  c->codec_id = format->video_codec;
55
61
  c->codec_type = CODEC_TYPE_VIDEO;
56
- c->bit_rate = bitrate;
62
+ c->bit_rate = videoBitRate;
57
63
  c->width = width;
58
64
  c->height = height;
59
65
  c->time_base.num = timeBaseNum;
@@ -62,18 +68,46 @@ AVOutput::AVOutput( const string &mrl, int bitrate, int width, int height,
62
68
  c->pix_fmt = PIX_FMT_YUV420P;
63
69
  if ( m_oc->oformat->flags & AVFMT_GLOBALHEADER )
64
70
  c->flags |= CODEC_FLAG_GLOBAL_HEADER;
71
+ if ( channels > 0 ) {
72
+ m_audio_st = av_new_stream( m_oc, 0 );
73
+ ERRORMACRO( m_audio_st != NULL, Error, , "Could not allocate audio stream" );
74
+ AVCodecContext *c = m_audio_st->codec;
75
+ c->codec_id = format->audio_codec;
76
+ c->codec_type = CODEC_TYPE_AUDIO;
77
+ c->bit_rate = audioBitRate;
78
+ c->sample_rate = sampleRate;
79
+ c->channels = channels;
80
+ };
65
81
  ERRORMACRO( av_set_parameters( m_oc, NULL ) >= 0, Error, ,
66
82
  "Invalid output format parameters: " << strerror( errno ) );
67
83
  AVCodec *codec = avcodec_find_encoder( c->codec_id );
68
- ERRORMACRO( codec != NULL, Error, , "Could not find codec " << c->codec_id );
84
+ ERRORMACRO( codec != NULL, Error, , "Could not find video codec "
85
+ << c->codec_id );
69
86
  ERRORMACRO( avcodec_open( c, codec ) >= 0, Error, ,
70
- "Error opening codec " << c->codec_id << ": " << strerror( errno ) );
87
+ "Error opening video codec " << c->codec_id << ": "
88
+ << strerror( errno ) );
71
89
  m_video_codec_open = true;
72
90
  if ( !( m_oc->oformat->flags & AVFMT_RAWPICTURE ) ) {
73
91
  m_video_buf = (char *)av_malloc( VIDEO_BUF_SIZE );
74
92
  ERRORMACRO( m_video_buf != NULL, Error, ,
75
93
  "Error allocating video output buffer" );
76
94
  };
95
+ if ( channels > 0 ) {
96
+ AVCodecContext *c = m_audio_st->codec;
97
+ AVCodec *codec = avcodec_find_encoder( c->codec_id );
98
+ ERRORMACRO( codec != NULL, Error, , "Could not find audio codec "
99
+ << c->codec_id );
100
+ ERRORMACRO( avcodec_open( c, codec ) >= 0, Error, ,
101
+ "Error opening audio codec " << c->codec_id << ": "
102
+ << strerror( errno ) );
103
+ m_audio_codec_open = true;
104
+ m_audio_buf = (char *)av_malloc( AUDIO_BUF_SIZE );
105
+ ERRORMACRO( m_audio_buf != NULL, Error, ,
106
+ "Error allocating audio output buffer" );
107
+ #ifndef NDEBUG
108
+ cerr << "audio frame size = " << c->frame_size << " samples" << endl;
109
+ #endif
110
+ };
77
111
  if ( !( format->flags & AVFMT_NOFILE ) ) {
78
112
  ERRORMACRO( url_fopen( &m_oc->pb, mrl.c_str(), URL_WRONLY ) >= 0, Error, ,
79
113
  "Could not open \"" << mrl << "\"" );
@@ -120,21 +154,32 @@ void AVOutput::close(void)
120
154
  av_write_trailer( m_oc );
121
155
  m_header_written = false;
122
156
  };
157
+ if ( m_audio_st ) {
158
+ if ( m_audio_codec_open ) {
159
+ avcodec_close( m_audio_st->codec );
160
+ m_audio_codec_open = false;
161
+ };
162
+ };
123
163
  if ( m_video_st ) {
124
164
  if ( m_video_codec_open ) {
125
165
  avcodec_close( m_video_st->codec );
126
166
  m_video_codec_open = false;
127
167
  };
128
168
  };
169
+ if ( m_audio_buf ) {
170
+ av_free( m_audio_buf );
171
+ m_audio_buf = NULL;
172
+ };
129
173
  if ( m_video_buf ) {
130
174
  av_free( m_video_buf );
131
175
  m_video_buf = NULL;
132
176
  };
133
177
  if ( m_oc ) {
134
- for ( int i = 0; i < m_oc->nb_streams; i++ ) {
178
+ for ( unsigned int i = 0; i < m_oc->nb_streams; i++ ) {
135
179
  av_freep( &m_oc->streams[i]->codec );
136
180
  av_freep( &m_oc->streams[i] );
137
181
  };
182
+ m_audio_st = NULL;
138
183
  m_video_st = NULL;
139
184
  if ( m_file_open ) {
140
185
  #ifdef HAVE_BYTEIO_PTR
@@ -149,7 +194,7 @@ void AVOutput::close(void)
149
194
  };
150
195
  }
151
196
 
152
- void AVOutput::write( FramePtr frame ) throw (Error)
197
+ void AVOutput::writeVideo( FramePtr frame ) throw (Error)
153
198
  {
154
199
  ERRORMACRO( m_oc != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
155
200
  "Did you call \"close\" before?" );
@@ -173,7 +218,7 @@ void AVOutput::write( FramePtr frame ) throw (Error)
173
218
  c->height, m_frame->data, m_frame->linesize );
174
219
  int packetSize = avcodec_encode_video( c, (uint8_t *)m_video_buf,
175
220
  VIDEO_BUF_SIZE, m_frame );
176
- ERRORMACRO( packetSize >= 0, Error, , "Error encoding frame" );
221
+ ERRORMACRO( packetSize >= 0, Error, , "Error encoding video frame" );
177
222
  if ( packetSize > 0 ) {
178
223
  AVPacket packet;
179
224
  av_init_packet( &packet );
@@ -186,19 +231,69 @@ void AVOutput::write( FramePtr frame ) throw (Error)
186
231
  packet.data = (uint8_t *)m_video_buf;
187
232
  packet.size = packetSize;
188
233
  ERRORMACRO( av_interleaved_write_frame( m_oc, &packet ) >= 0, Error, ,
189
- "Error writing frame of video \"" << m_mrl << "\": "
234
+ "Error writing video frame of video \"" << m_mrl << "\": "
190
235
  << strerror( errno ) );
191
236
  };
192
237
  };
193
238
  }
194
239
 
240
+ int AVOutput::frameSize(void) throw (Error)
241
+ {
242
+ ERRORMACRO( m_oc != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
243
+ "Did you call \"close\" before?" );
244
+ ERRORMACRO( m_audio_st != NULL, Error, , "Video \"" << m_mrl << "\" does not have "
245
+ "an audio stream" );
246
+ return m_audio_st->codec->frame_size;
247
+ }
248
+
249
+ int AVOutput::channels(void) throw (Error)
250
+ {
251
+ ERRORMACRO( m_oc != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
252
+ "Did you call \"close\" before?" );
253
+ ERRORMACRO( m_audio_st != NULL, Error, , "Video \"" << m_mrl << "\" does not have "
254
+ "an audio stream" );
255
+ return m_audio_st->codec->channels;
256
+ }
257
+
258
+ void AVOutput::writeAudio( SequencePtr frame ) throw (Error)
259
+ {
260
+ ERRORMACRO( m_oc != NULL, Error, , "Video \"" << m_mrl << "\" is not open. "
261
+ "Did you call \"close\" before?" );
262
+ ERRORMACRO( m_audio_st != NULL, Error, , "Video \"" << m_mrl << "\" does not have "
263
+ "an audio stream" );
264
+ AVCodecContext *c = m_audio_st->codec;
265
+ ERRORMACRO( frame->size() == c->frame_size * 2 * c->channels, Error, , "Size of "
266
+ "audio frame is " << frame->size() << " bytes (but should be "
267
+ << c->frame_size * 2 * c->channels << " bytes)" );
268
+ int packetSize = avcodec_encode_audio( c, (uint8_t *)m_audio_buf,
269
+ AUDIO_BUF_SIZE, (short *)frame->data() );
270
+ ERRORMACRO( packetSize >= 0, Error, , "Error encoding audio frame" );
271
+ if ( packetSize > 0 ) {
272
+ AVPacket packet;
273
+ av_init_packet( &packet );
274
+ if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
275
+ packet.pts = av_rescale_q( c->coded_frame->pts, c->time_base,
276
+ m_audio_st->time_base );
277
+ packet.flags |= PKT_FLAG_KEY;
278
+ packet.stream_index = m_audio_st->index;
279
+ packet.data = (uint8_t *)m_audio_buf;
280
+ packet.size = packetSize;
281
+ ERRORMACRO( av_interleaved_write_frame( m_oc, &packet ) >= 0, Error, ,
282
+ "Error writing audio frame of video \"" << m_mrl << "\": "
283
+ << strerror( errno ) );
284
+ };
285
+ }
286
+
195
287
  VALUE AVOutput::registerRubyClass( VALUE rbModule )
196
288
  {
197
289
  cRubyClass = rb_define_class_under( rbModule, "AVOutput", rb_cObject );
198
290
  rb_define_singleton_method( cRubyClass, "new",
199
- RUBY_METHOD_FUNC( wrapNew ), 6 );
291
+ RUBY_METHOD_FUNC( wrapNew ), 9 );
200
292
  rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
201
- rb_define_method( cRubyClass, "write", RUBY_METHOD_FUNC( wrapWrite ), 1 );
293
+ rb_define_method( cRubyClass, "frame_size", RUBY_METHOD_FUNC( wrapFrameSize ), 0 );
294
+ rb_define_method( cRubyClass, "channels", RUBY_METHOD_FUNC( wrapChannels ), 0 );
295
+ rb_define_method( cRubyClass, "write_video", RUBY_METHOD_FUNC( wrapWriteVideo ), 1 );
296
+ rb_define_method( cRubyClass, "write_audio", RUBY_METHOD_FUNC( wrapWriteAudio ), 1 );
202
297
  }
203
298
 
204
299
  void AVOutput::deleteRubyObject( void *ptr )
@@ -207,15 +302,17 @@ void AVOutput::deleteRubyObject( void *ptr )
207
302
  }
208
303
 
209
304
  VALUE AVOutput::wrapNew( VALUE rbClass, VALUE rbMRL, VALUE rbBitRate, VALUE rbWidth,
210
- VALUE rbHeight, VALUE rbTimeBaseNum, VALUE rbTimeBaseDen )
305
+ VALUE rbHeight, VALUE rbTimeBaseNum, VALUE rbTimeBaseDen,
306
+ VALUE rbAudioBitRate, VALUE rbSampleRate, VALUE rbChannels )
211
307
  {
212
308
  VALUE retVal = Qnil;
213
309
  try {
214
310
  rb_check_type( rbMRL, T_STRING );
215
311
  AVOutputPtr ptr( new AVOutput( StringValuePtr( rbMRL ), NUM2INT( rbBitRate ),
216
312
  NUM2INT( rbWidth ), NUM2INT( rbHeight ),
217
- NUM2INT( rbTimeBaseNum ),
218
- NUM2INT( rbTimeBaseDen ) ) );
313
+ NUM2INT( rbTimeBaseNum ), NUM2INT( rbTimeBaseDen ),
314
+ NUM2INT( rbAudioBitRate ), NUM2INT( rbSampleRate ),
315
+ NUM2INT( rbChannels ) ) );
219
316
  retVal = Data_Wrap_Struct( rbClass, 0, deleteRubyObject,
220
317
  new AVOutputPtr( ptr ) );
221
318
  } catch ( exception &e ) {
@@ -231,14 +328,51 @@ VALUE AVOutput::wrapClose( VALUE rbSelf )
231
328
  return rbSelf;
232
329
  }
233
330
 
234
- VALUE AVOutput::wrapWrite( VALUE rbSelf, VALUE rbFrame )
331
+ VALUE AVOutput::wrapFrameSize( VALUE rbSelf )
332
+ {
333
+ VALUE rbRetVal = Qnil;
334
+ try {
335
+ AVOutputPtr *self; Data_Get_Struct( rbSelf, AVOutputPtr, self );
336
+ rbRetVal = INT2NUM( (*self)->frameSize() );
337
+ } catch ( exception &e ) {
338
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
339
+ };
340
+ return rbRetVal;
341
+ }
342
+
343
+ VALUE AVOutput::wrapChannels( VALUE rbSelf )
344
+ {
345
+ VALUE rbRetVal = Qnil;
346
+ try {
347
+ AVOutputPtr *self; Data_Get_Struct( rbSelf, AVOutputPtr, self );
348
+ rbRetVal = INT2NUM( (*self)->channels() );
349
+ } catch ( exception &e ) {
350
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
351
+ };
352
+ return rbRetVal;
353
+ }
354
+
355
+ VALUE AVOutput::wrapWriteVideo( VALUE rbSelf, VALUE rbFrame )
235
356
  {
236
357
  try {
237
358
  AVOutputPtr *self; Data_Get_Struct( rbSelf, AVOutputPtr, self );
238
359
  FramePtr frame( new Frame( rbFrame ) );
239
- (*self)->write( frame );
360
+ (*self)->writeVideo( frame );
240
361
  } catch ( exception &e ) {
241
362
  rb_raise( rb_eRuntimeError, "%s", e.what() );
242
363
  };
243
364
  return rbFrame;
244
365
  }
366
+
367
+ VALUE AVOutput::wrapWriteAudio( VALUE rbSelf, VALUE rbFrame )
368
+ {
369
+ try {
370
+ AVOutputPtr *self; Data_Get_Struct( rbSelf, AVOutputPtr, self );
371
+ SequencePtr frame( new Sequence( rbFrame ) );
372
+ (*self)->writeAudio( frame );
373
+ } catch ( exception &e ) {
374
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
375
+ };
376
+ return rbFrame;
377
+ }
378
+
data/ext/avoutput.hh CHANGED
@@ -36,28 +36,40 @@ 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 AVOutput
41
42
  {
42
43
  public:
43
- AVOutput( const std::string &mrl, int bitrate,
44
- int width, int height, int timeBaseNum, int timeBaseDen ) throw (Error);
44
+ AVOutput( const std::string &mrl, int videoBitRate,
45
+ int width, int height, int timeBaseNum, int timeBaseDen,
46
+ int audioBitRate, int sampleRate, int channels ) throw (Error);
45
47
  virtual ~AVOutput(void);
46
48
  void close(void);
47
- void write( FramePtr frame ) throw (Error);
49
+ int frameSize(void) throw (Error);
50
+ int channels(void) throw (Error);
51
+ void writeVideo( FramePtr frame ) throw (Error);
52
+ void writeAudio( SequencePtr frame ) throw (Error);
48
53
  static VALUE cRubyClass;
49
54
  static VALUE registerRubyClass( VALUE rbModule );
50
55
  static void deleteRubyObject( void *ptr );
51
56
  static VALUE wrapNew( VALUE rbClass, VALUE rbMRL, VALUE rbBitRate, VALUE rbWidth,
52
- VALUE rbHeight, VALUE rbTimeBaseNum, VALUE rbTimeBaseDen );
57
+ VALUE rbHeight, VALUE rbTimeBaseNum, VALUE rbTimeBaseDen,
58
+ VALUE rbAudioBitRate, VALUE rbSampleRate, VALUE rbChannels );
53
59
  static VALUE wrapClose( VALUE rbSelf );
54
- static VALUE wrapWrite( VALUE rbSelf, VALUE rbFrame );
60
+ static VALUE wrapFrameSize( VALUE rbSelf );
61
+ static VALUE wrapChannels( VALUE rbSelf );
62
+ static VALUE wrapWriteVideo( VALUE rbSelf, VALUE rbFrame );
63
+ static VALUE wrapWriteAudio( VALUE rbSelf, VALUE rbFrame );
55
64
  protected:
56
65
  std::string m_mrl;
57
66
  AVFormatContext *m_oc;
58
67
  AVStream *m_video_st;
68
+ AVStream *m_audio_st;
59
69
  bool m_video_codec_open;
70
+ bool m_audio_codec_open;
60
71
  char *m_video_buf;
72
+ char *m_audio_buf;
61
73
  bool m_file_open;
62
74
  bool m_header_written;
63
75
  struct SwsContext *m_swsContext;
@@ -23,8 +23,8 @@ module Hornetseye
23
23
 
24
24
  alias_method :orig_new, :new
25
25
 
26
- def new( mrl )
27
- retval = orig_new mrl
26
+ def new( mrl, audio = true )
27
+ retval = orig_new mrl, audio
28
28
  retval.instance_eval do
29
29
  @frame = nil
30
30
  @video = Queue.new
@@ -23,16 +23,39 @@ module Hornetseye
23
23
 
24
24
  alias_method :orig_new, :new
25
25
 
26
- def new( mrl, bitrate, width, height, frame_rate )
26
+ def new( mrl, video_bit_rate, width, height, frame_rate, have_audio = false,
27
+ audio_bit_rate = 64000, sample_rate = 44100, channels = 2 )
27
28
  if frame_rate.is_a? Float
28
29
  frame_rate = Rational( 90000, ( 90000 / frame_rate ).to_i )
29
30
  end
30
- orig_new mrl, bitrate, width, height,
31
- frame_rate.denominator, frame_rate.numerator
31
+ orig_new mrl, video_bit_rate, width, height,
32
+ frame_rate.denominator, frame_rate.numerator,
33
+ have_audio ? audio_bit_rate : 0,
34
+ have_audio ? sample_rate : 0,
35
+ have_audio ? channels : 0
32
36
  end
33
37
 
34
38
  end
35
39
 
40
+ alias_method :write, :write_video
41
+
42
+ alias_method :orig_write_video, :write_video
43
+
44
+ def write_video( frame )
45
+ orig_write_video frame.to_yv12
46
+ end
47
+
48
+ alias_method :orig_write_audio, :write_audio
49
+
50
+ def write_audio( frame )
51
+ if frame.shape != [ channels, frame_size ]
52
+ raise "Audio frame must have #{channels} channels and #{frame_size} " +
53
+ "samples (but had #{frame.shape[0]} channels and #{frame.shape[1]} " +
54
+ "samples)"
55
+ end
56
+ orig_write_audio Sequence( UBYTE, frame.storage_size ).new( frame.memory )
57
+ end
58
+
36
59
  end
37
60
 
38
61
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 5
8
- - 4
9
- version: 0.5.4
7
+ - 6
8
+ - 0
9
+ version: 0.6.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-10-21 00:00:00 +01:00
17
+ date: 2010-11-13 00:00:00 +00:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -55,8 +55,8 @@ dependencies:
55
55
  - !ruby/object:Gem::Version
56
56
  segments:
57
57
  - 0
58
- - 7
59
- version: "0.7"
58
+ - 8
59
+ version: "0.8"
60
60
  type: :runtime
61
61
  version_requirements: *id003
62
62
  - !ruby/object:Gem::Dependency