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 +16 -16
- data/ext/avinput.cc +44 -27
- data/ext/avinput.hh +4 -2
- data/ext/avoutput.cc +153 -19
- data/ext/avoutput.hh +17 -5
- data/lib/hornetseye-ffmpeg/avinput.rb +2 -2
- data/lib/hornetseye-ffmpeg/avoutput.rb +26 -3
- metadata +6 -6
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.
|
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 '\-', '_'}
|
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 =
|
31
|
-
|
32
|
-
|
33
|
-
|
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#{
|
35
|
+
$CXXFLAGS = "#{$CXXFLAGS} -I#{CFG[ 'archdir' ]}"
|
37
36
|
end
|
38
|
-
$LIBRUBYARG =
|
39
|
-
|
40
|
-
$
|
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.
|
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.
|
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 #{
|
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
|
-
|
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[
|
155
|
-
|
156
|
-
picture.
|
157
|
-
|
158
|
-
picture.linesize[
|
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 ),
|
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
|
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
|
30
|
-
int timeBaseNum, int timeBaseDen
|
31
|
-
|
32
|
-
|
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 =
|
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 "
|
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 << ": "
|
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::
|
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 ),
|
291
|
+
RUBY_METHOD_FUNC( wrapNew ), 9 );
|
200
292
|
rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
|
201
|
-
rb_define_method( cRubyClass, "
|
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(
|
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::
|
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)->
|
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
|
44
|
-
int width, int height, int timeBaseNum, int timeBaseDen
|
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
|
-
|
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
|
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,16 +23,39 @@ module Hornetseye
|
|
23
23
|
|
24
24
|
alias_method :orig_new, :new
|
25
25
|
|
26
|
-
def new( mrl,
|
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,
|
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
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
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-
|
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
|
-
-
|
59
|
-
version: "0.
|
58
|
+
- 8
|
59
|
+
version: "0.8"
|
60
60
|
type: :runtime
|
61
61
|
version_requirements: *id003
|
62
62
|
- !ruby/object:Gem::Dependency
|