hornetseye-ffmpeg 0.5.4 → 0.6.0

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