├── ext └── ffmpeg_ruby │ ├── ffmpeg_ruby.h │ ├── ffmpeg_ruby.c │ ├── ffmpeg_avstream.h │ ├── extconf.rb │ ├── ffmpeg_avcodec.h │ ├── ffmpeg_avformat.h │ ├── ffmpeg_avstream.c │ ├── ffmpeg_avformat.c │ └── ffmpeg_avcodec.c ├── lib └── ffmpeg-ruby.rb ├── .gitignore ├── tasks └── mac_specific.rake ├── Rakefile ├── test └── test.rb └── README.rdoc /ext/ffmpeg_ruby/ffmpeg_ruby.h: -------------------------------------------------------------------------------- 1 | #include "ruby.h" 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/ffmpeg-ruby.rb: -------------------------------------------------------------------------------- 1 | # Load the C library. 2 | $:.unshift "#{File.dirname(__FILE__)}/../ext/ffmpeg_ruby/" 3 | 4 | require "ffmpeg_ruby" 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Manifest 2 | ext/ffmpeg_ruby/Makefile 3 | *.dSYM 4 | ext/ffmpeg_ruby/ffmpeg_ruby.bundle 5 | ext/ffmpeg_ruby/*.o 6 | ext/ffmpeg_ruby/*.log 7 | pkg/ 8 | *.swp 9 | ffmpeg-ruby.gemspec 10 | -------------------------------------------------------------------------------- /tasks/mac_specific.rake: -------------------------------------------------------------------------------- 1 | task :compile_mac do 2 | puts "Compiling on a mac" 3 | `cd ext/ffmpeg_ruby && env ARCHFLAGS="-arch x86_64" ruby extconf.rb --with-avformat-dir=/opt/local && make` 4 | end 5 | 6 | task :clean_mac => :clean do 7 | puts "Cleaning on a mac" 8 | `rm -rf ext/ffmpeg_ruby/conftest.dSYM` 9 | end 10 | -------------------------------------------------------------------------------- /ext/ffmpeg_ruby/ffmpeg_ruby.c: -------------------------------------------------------------------------------- 1 | #include "ffmpeg_ruby.h" 2 | 3 | #include 4 | #include 5 | 6 | /** FFMpeg Module */ 7 | VALUE mFFMpeg; 8 | /** *** Ruby Interface *** **/ 9 | void Init_ffmpeg_ruby() 10 | { 11 | av_register_all(); 12 | mFFMpeg = rb_define_module("FFMpeg"); 13 | Init_ffmpeg_ruby_avcodec(mFFMpeg); 14 | Init_ffmpeg_ruby_avformat(mFFMpeg); 15 | Init_ffmpeg_ruby_avstream(mFFMpeg); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'echoe' 4 | 5 | Echoe.new("ffmpeg-ruby", "0.1.3") do |p| 6 | p.description = "FFMpeg Ruby Bridge. Call FFMpeg/LibAVCodec/LibAVFormat directly" 7 | p.url = "http://github.com/hackerdude/ffmpeg-ruby" 8 | p.author = "David Martinez" 9 | p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"] 10 | p.development_dependencies = [] # TODO How to do native dependencies? 11 | end 12 | 13 | 14 | Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each{|ext| load ext } 15 | 16 | -------------------------------------------------------------------------------- /ext/ffmpeg_ruby/ffmpeg_avstream.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | VALUE cFFMpegAVStream; 5 | 6 | void AVStream_mark(void *v); 7 | void AVStream_free(void *v); 8 | VALUE AVStream_canonical_codec_name(VALUE); 9 | VALUE AVStream_codec(VALUE); 10 | VALUE AVStream_codec_context(VALUE); 11 | 12 | 13 | // forward declaring some stuff from codec module 14 | void AVCodec_mark(void *v); 15 | void AVCodec_free(void *v); 16 | void AVCodecContext_mark(void *v); 17 | void AVCodecContext_free(void *v); 18 | extern VALUE cFFMpegAVCodec; 19 | extern VALUE cFFMpegAVCodecContext; 20 | VALUE avcodec_canonical_name(AVCodecContext *enc); 21 | -------------------------------------------------------------------------------- /ext/ffmpeg_ruby/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | #require 'rubygems' 3 | #require 'ruby-debug' 4 | #Debugger.start 5 | 6 | def boom(v) 7 | puts(v) 8 | exit 1 9 | end 10 | 11 | #debugger 12 | if RUBY_PLATFORM == "universal-darwin10.0" 13 | # TODO Set the archflags to -arch x86_64 ONLY if it's a 64-bit snow leopard machine. 14 | #ENV['ARCHFLAGS'] = "-arch i386 -arch x86_64" 15 | $CFLAGS.sub!("-arch x86_64", "") 16 | end 17 | unless ( have_header("libavformat/avformat.h") || 18 | find_header("libavformat/avformat.h", "/opt/local/include", "/usr/local/include", "/usr/include") ) and 19 | ( have_library("avformat", "av_register_all") or 20 | find_library("avformat", "av_register_all", "/opt/local/lib", "/usr/local/lib", "/usr/lib") ) 21 | boom(< 2 | #include 3 | 4 | VALUE cFFMpegAVFormatContext; 5 | VALUE cFFMpegAVOutputFormat; 6 | 7 | void AVOutputFormat_mark(void *v); 8 | void AVOutputFormat_free(void *v); 9 | 10 | void AVFormatContext_mark(void *v); 11 | void AVFormatContext_free(void *v); 12 | 13 | VALUE AVFormatContext_dump_format(VALUE); 14 | VALUE AVFormatContext_duration(VALUE); 15 | VALUE AVFormatContext_close_file(VALUE); 16 | VALUE AVFormatContext_new(VALUE, VALUE); 17 | VALUE AVFormatContext_codec_contexts(VALUE); 18 | VALUE AVFormatContext_streams(VALUE); 19 | VALUE AVFormatContext_title(VALUE); 20 | VALUE AVFormatContext_author(VALUE); 21 | VALUE AVFormatContext_copyright(VALUE); 22 | VALUE AVFormatContext_bit_rate(VALUE); 23 | VALUE AVFormatContext_album(VALUE); 24 | void Init_ffmpeg_ruby_avformat(VALUE module); 25 | 26 | // Forward declaring some necessary stuff from other modules 27 | void AVCodecContext_mark(void *v); 28 | void AVCodecContext_free(void *v); 29 | extern VALUE cFFMpegAVCodecContext; 30 | 31 | void AVStream_mark(void *v); 32 | void AVStream_free(void *v); 33 | extern VALUE cFFMpegAVStream; 34 | -------------------------------------------------------------------------------- /ext/ffmpeg_ruby/ffmpeg_avstream.c: -------------------------------------------------------------------------------- 1 | #include "ruby.h" 2 | #include 3 | #include "ffmpeg_avstream.h" 4 | 5 | void Init_ffmpeg_ruby_avstream(VALUE module) 6 | { 7 | cFFMpegAVStream = rb_define_class_under(module, "AVStream", rb_cObject); 8 | rb_define_method(cFFMpegAVStream, "canonical_codec_name", AVStream_canonical_codec_name, 0); 9 | rb_define_method(cFFMpegAVStream, "codec", AVStream_codec, 0); 10 | rb_define_method(cFFMpegAVStream, "codec_context", AVStream_codec_context, 0); 11 | } 12 | 13 | void AVStream_free(void *v) {} 14 | void AVStream_mark(void *v) {} 15 | 16 | VALUE AVStream_canonical_codec_name(VALUE self) 17 | { 18 | AVStream *ptr; 19 | AVCodec *c; 20 | 21 | Data_Get_Struct(self, AVStream, ptr); 22 | //char buf[256]; 23 | c =avcodec_canonical_name(ptr->codec); 24 | return c; 25 | } 26 | 27 | VALUE AVStream_codec(VALUE self) 28 | { 29 | AVStream *pStream; 30 | AVCodec *c; 31 | Data_Get_Struct(self, AVStream, pStream); 32 | c = avcodec_find_decoder(pStream->codec->codec_id); 33 | if (!c) { 34 | return Qnil; 35 | } 36 | VALUE rCodec = Data_Wrap_Struct(cFFMpegAVCodec, AVCodec_mark, AVCodec_free, c); 37 | rb_obj_call_init(rCodec,0,0); 38 | return rCodec; 39 | } 40 | 41 | VALUE AVStream_codec_context(VALUE self) 42 | { 43 | AVStream *pStream; 44 | AVCodecContext *c; 45 | Data_Get_Struct(self, AVStream, pStream); 46 | c = pStream->codec; 47 | VALUE rCodec = Data_Wrap_Struct(cFFMpegAVCodecContext, AVCodecContext_mark, AVCodecContext_free, c); 48 | rb_obj_call_init(rCodec,0,0); 49 | return rCodec; 50 | } 51 | -------------------------------------------------------------------------------- /test/test.rb: -------------------------------------------------------------------------------- 1 | require "#{File.dirname(__FILE__)}/../lib/ffmpeg-ruby" 2 | require 'rubygems' 3 | require 'ruby-debug' 4 | 5 | puts "Here are the video codecs I support, of the #{FFMpeg::AVCodec.supported_avcodecs.length} codecs:" 6 | FFMpeg::AVCodec.supported_video_codecs.each{|k,v| 7 | puts " - #{k}=#{v.long_name} (#{v.name}) of type #{FFMpeg::MAP_CODEC_TYPES[v.codec_type]}" 8 | } 9 | 10 | FFMpeg::AVCodec.supported_audio_codecs.each{|k,v| 11 | puts " - #{k}=#{v.long_name} (#{v.name}) of type #{FFMpeg::MAP_CODEC_TYPES[v.codec_type]}" 12 | } 13 | puts "Creating new AVFormatContext" 14 | filename = '/Users/david/channels-reader/public/videos/Channels_Playlist_WEB.mov' 15 | if ARGV.length > 0 16 | filename = ARGV[0] 17 | end 18 | f = FFMpeg::AVFormatContext.new(filename) 19 | "Format Information:" 20 | puts " Duration: #{f.duration}" 21 | puts " Title: #{f.title}" 22 | puts " Copyright: #{f.copyright}" 23 | puts " Author: #{f.author}" 24 | puts " Album: #{f.album}" 25 | puts " Bit Rate: #{f.bit_rate}" 26 | 27 | puts "\nStream information:" 28 | f.streams.each{|s| 29 | puts " Canonical Codec: #{s.canonical_codec_name}" 30 | puts " Codec Long Name: #{s.codec_context.long_name}" 31 | } 32 | 33 | puts "\nCodec Context Information for #{filename}" 34 | f.codec_contexts.each_with_index{|ctx,i| 35 | puts "Codec info for stream ##{i}" 36 | puts " Type: #{ctx.codec_type} (#{FFMpeg::MAP_CODEC_TYPES[ctx.codec_type]})" 37 | puts " Name: #{ctx.name}" 38 | puts " Long Name: #{ctx.long_name}" 39 | puts " Width: #{ctx.width}" 40 | puts " Height: #{ctx.height}" 41 | puts " Audio Channels: #{ctx.channels}" 42 | puts " Audio Sample Rate: #{ctx.sample_rate}" 43 | } 44 | 45 | 46 | 47 | puts "-- Now dumping the format:" 48 | f.dump_format 49 | 50 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Ruby FFMpeg Library 2 | 3 | "ffmpeg-ruby" is a ruby gem library (work in progress) that provides bindings allowing you to access the ffmpeg under ruby. FFMpeg is a cross-platform solution to record, convert and stream audio and video. It includes libavcodec - the leading audio/video codec library. 4 | 5 | This implementation is starting by doing the translation for some of the functions in libavcodec and libavformat to ruby. 6 | 7 | FFMpeg is provided by http://ffmpeg.org/ under the LGPL license. 8 | 9 | This bindings gem is graciously provided by http://www.channels.com under the LGPL license. 10 | 11 | == Installing 12 | 13 | $ gem install ffmpeg-ruby 14 | 15 | == Installation troubleshooting 16 | 17 | If you run into trouble, it could be the C bundle not building. This is known to happen on MacOS 64-bit or with the ffmpeg from macports. You basically need to run ruby extconf yourself then. 18 | 19 | 20 | $ ruby extconf.rb 21 | 22 | You may need to add +--with+ on mac, to point it to macports' /opt/local for the libraries. 23 | 24 | == Mac Notes 25 | 26 | Because of the way the library gets built, it needs to match the architecture type of your ruby. You can force it to do this by specifying the ++-arch++ flag. For a 64-bit ruby on Snow Leopard: 27 | 28 | $ sudo port install ffmpeg 29 | (go have coffee) 30 | $ sudo env ARCHFLAGS="-arch x86_64" gem install ffmpeg-ruby 31 | 32 | If your machine is 32 bit, the +ARCHFLAGS+ above should be ++ARCHFLAGS="-arch i386"+ 33 | 34 | This will provide you with a bundle you can require. 35 | 36 | == Usage 37 | 38 | See the code on +test/+ for usage. But basically it goes like this: 39 | 40 | require 'ffmpeg-ruby' 41 | 42 | # Get a list of the video codecs supported 43 | FFMpeg::AVCodec.supported_video_codecs 44 | 45 | # Get a list of the audio codecs supported 46 | FFMpeg::AVCodec.supported_video_codecs 47 | 48 | # Create new AVFormatContext 49 | f = FFMpeg::AVFormatContext.new("/path/to/my/video.mov") 50 | 51 | f.codec_contexts.each{ |ctx| 52 | puts ctx.name # The name of the codec this file uses. 53 | } 54 | 55 | -------------------------------------------------------------------------------- /ext/ffmpeg_ruby/ffmpeg_avformat.c: -------------------------------------------------------------------------------- 1 | #include "ruby.h" 2 | #include 3 | #include "ffmpeg_avformat.h" 4 | 5 | void AVFormatContext_mark(void *v) {} 6 | 7 | void AVFormatContext_free(void *v) { 8 | // TODO Figure out how to avoid double-closing. 9 | //AVFormatContext* f = (AVFormatContext*)v; 10 | //if (f->filename != NULL) 11 | //av_close_input_file(v); 12 | } 13 | 14 | void AVOutputFormat_mark(void *v){} 15 | void AVOutputFormat_free(void *v){} 16 | 17 | VALUE supported_avformats() 18 | { 19 | VALUE result = rb_hash_new(); 20 | AVOutputFormat *pNext = NULL; 21 | pNext = av_oformat_next(pNext); 22 | while (pNext != NULL) { 23 | VALUE sNextFormatName = rb_str_new2(pNext->name); 24 | //VALUE sNextLongName = rb_str_new2(pNext->long_name); 25 | VALUE format = Data_Wrap_Struct(cFFMpegAVOutputFormat, AVOutputFormat_mark, AVOutputFormat_free, pNext); 26 | rb_obj_call_init(result, 0, 0); 27 | rb_hash_aset(result, sNextFormatName, format); 28 | pNext = av_codec_next(pNext); 29 | } 30 | return result; 31 | } 32 | 33 | 34 | VALUE AVFormatContext_dump_format(VALUE self) { 35 | AVFormatContext *ptr; 36 | Data_Get_Struct(self, AVFormatContext, ptr); 37 | dump_format(ptr, 0, ptr->filename, 0); 38 | return Qnil; 39 | } 40 | VALUE AVFormatContext_duration(VALUE self) { 41 | AVFormatContext *ptr; 42 | Data_Get_Struct(self, AVFormatContext, ptr); 43 | //ptr->filename = NULL; 44 | return INT2NUM(ptr->duration); 45 | } 46 | VALUE AVFormatContext_close_file(VALUE self) { 47 | AVFormatContext *ptr; 48 | Data_Get_Struct(self, AVFormatContext, ptr); 49 | av_close_input_file(ptr); 50 | //ptr->filename = NULL; 51 | return Qnil; 52 | } 53 | VALUE AVFormatContext_new(VALUE klaas, VALUE ruby_filename) { 54 | Check_Type(ruby_filename, T_STRING); 55 | char* filename = StringValuePtr(ruby_filename); 56 | 57 | AVFormatContext *pFormatCtx; 58 | // Open video file 59 | if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0) { 60 | rb_raise(rb_eRuntimeError, "Could not open video file %s", filename); 61 | return NULL; 62 | } 63 | 64 | // Retrieve stream information 65 | if(av_find_stream_info(pFormatCtx)<0) { 66 | rb_raise(rb_eRuntimeError, "Could find the stream information in file %s", filename); 67 | return NULL; // Couldn't find stream information 68 | } 69 | 70 | VALUE result = Data_Wrap_Struct(klaas, AVFormatContext_mark, AVFormatContext_free, pFormatCtx); 71 | rb_obj_call_init(result, 0, 0); 72 | return result; 73 | } 74 | 75 | VALUE AVFormatContext_streams(VALUE self) { 76 | AVFormatContext *ptr; 77 | Data_Get_Struct(self, AVFormatContext, ptr); 78 | VALUE result = rb_ary_new(); 79 | int i; 80 | for(i=0; inb_streams; i++) { 81 | // Get a pointer to the codec context for the video stream 82 | AVStream *pStream = ptr->streams[i]; 83 | VALUE rStream = Data_Wrap_Struct(cFFMpegAVStream, AVStream_mark, AVStream_free, pStream); 84 | rb_obj_call_init(rStream,0,0); 85 | rb_ary_push(result, rStream); 86 | } 87 | return result; 88 | } 89 | 90 | /* Return all the AVCodecContext entries for all streams in the file */ 91 | VALUE AVFormatContext_codec_contexts(VALUE self) { 92 | AVFormatContext *ptr; 93 | Data_Get_Struct(self, AVFormatContext, ptr); 94 | VALUE result = rb_ary_new(); 95 | int i, videoStream; 96 | videoStream=-1; 97 | for(i=0; inb_streams; i++) { 98 | // Get a pointer to the codec context for the video stream 99 | AVCodecContext *pCodecCtx = ptr->streams[i]->codec; 100 | AVCodec *pCodec; 101 | pCodec = avcodec_find_decoder(pCodecCtx->codec_id); 102 | pCodecCtx->codec = pCodec; 103 | 104 | // Turn it into something ruby can understand 105 | VALUE rCodec = Data_Wrap_Struct(cFFMpegAVCodecContext, AVCodecContext_mark, AVCodecContext_free, pCodecCtx); 106 | rb_obj_call_init(rCodec,0,0); 107 | rb_ary_push(result, rCodec); 108 | } 109 | //if(videoStream==-1) { 110 | //rb_raise(rb_eRuntimeError, "No video streams here!"); 111 | //return Qnil; 112 | //} 113 | return result; 114 | } 115 | 116 | VALUE AVFormatContext_title(VALUE self) { 117 | AVFormatContext *ptr; 118 | Data_Get_Struct(self, AVFormatContext, ptr); 119 | return rb_str_new2(ptr->title); 120 | } 121 | 122 | VALUE AVFormatContext_author(VALUE self) { 123 | AVFormatContext *ptr; 124 | Data_Get_Struct(self, AVFormatContext, ptr); 125 | return rb_str_new2(ptr->author); 126 | } 127 | 128 | VALUE AVFormatContext_copyright(VALUE self) { 129 | AVFormatContext *ptr; 130 | Data_Get_Struct(self, AVFormatContext, ptr); 131 | return rb_str_new2(ptr->copyright); 132 | } 133 | 134 | VALUE AVFormatContext_bit_rate(VALUE self) { 135 | AVFormatContext *ptr; 136 | Data_Get_Struct(self, AVFormatContext, ptr); 137 | return INT2NUM(ptr->bit_rate); 138 | } 139 | 140 | VALUE AVFormatContext_album(VALUE self) { 141 | AVFormatContext *ptr; 142 | Data_Get_Struct(self, AVFormatContext, ptr); 143 | return rb_str_new2(ptr->album); 144 | } 145 | 146 | 147 | void Init_ffmpeg_ruby_avformat(VALUE module) 148 | { 149 | 150 | cFFMpegAVFormatContext = rb_define_class_under(module, "AVFormatContext", rb_cObject); 151 | rb_define_singleton_method(cFFMpegAVFormatContext, "new", AVFormatContext_new, 1); 152 | rb_define_method(cFFMpegAVFormatContext, "close_file", AVFormatContext_close_file, 0); 153 | rb_define_method(cFFMpegAVFormatContext, "streams", AVFormatContext_streams, 0); 154 | rb_define_method(cFFMpegAVFormatContext, "duration", AVFormatContext_duration, 0); 155 | rb_define_method(cFFMpegAVFormatContext, "title", AVFormatContext_title, 0); 156 | rb_define_method(cFFMpegAVFormatContext, "copyright", AVFormatContext_copyright, 0); 157 | rb_define_method(cFFMpegAVFormatContext, "author", AVFormatContext_author, 0); 158 | rb_define_method(cFFMpegAVFormatContext, "album", AVFormatContext_album, 0); 159 | rb_define_method(cFFMpegAVFormatContext, "bit_rate", AVFormatContext_bit_rate, 0); 160 | rb_define_method(cFFMpegAVFormatContext, "codec_contexts", AVFormatContext_codec_contexts, 0); 161 | rb_define_method(cFFMpegAVFormatContext, "dump_format", AVFormatContext_dump_format, 0); 162 | 163 | cFFMpegAVOutputFormat = rb_define_class_under(module, "AVOutputFormat", rb_cObject); 164 | rb_define_singleton_method(cFFMpegAVOutputFormat, "supported_avformats", supported_avformats, 0); 165 | } 166 | -------------------------------------------------------------------------------- /ext/ffmpeg_ruby/ffmpeg_avcodec.c: -------------------------------------------------------------------------------- 1 | #include "ruby.h" 2 | #include 3 | 4 | VALUE cFFMpegAVCodec; 5 | VALUE cFFMpegAVCodecContext; 6 | 7 | void AVCodec_mark(void *v) {} 8 | void AVCodec_free(void *v) {} 9 | 10 | void AVCodecContext_mark(AVCodecContext v) {} 11 | void AVCodecContext_free(AVCodecContext v) { 12 | } 13 | 14 | VALUE AVCodecContext_codec_type(VALUE self) 15 | { 16 | AVCodecContext *ptr; 17 | Data_Get_Struct(self, AVCodecContext, ptr); 18 | VALUE result = INT2NUM(ptr->codec_type); 19 | return result; 20 | } 21 | 22 | VALUE codec_type_map() 23 | { 24 | VALUE result = rb_hash_new(); 25 | rb_hash_aset(result, INT2NUM(CODEC_TYPE_VIDEO), rb_str_new2("Video")); 26 | rb_hash_aset(result, INT2NUM(CODEC_TYPE_AUDIO), rb_str_new2("Audio")); 27 | rb_hash_aset(result, INT2NUM(CODEC_TYPE_DATA), rb_str_new2("Data")); 28 | rb_hash_aset(result, INT2NUM(CODEC_TYPE_SUBTITLE), rb_str_new2("Subtitle")); 29 | rb_hash_aset(result, INT2NUM(CODEC_TYPE_ATTACHMENT), rb_str_new2("Attachment")); 30 | return result; 31 | } 32 | 33 | 34 | VALUE AVCodecContext_codec_id(VALUE self) 35 | { 36 | AVCodecContext *ptr; 37 | Data_Get_Struct(self, AVCodecContext, ptr); 38 | VALUE result = INT2NUM(ptr->codec_id); 39 | return result; 40 | } 41 | 42 | VALUE AVCodecContext_fourcc_tag(VALUE self) 43 | { 44 | AVCodecContext *enc; 45 | char buf1[128]; 46 | 47 | Data_Get_Struct(self, AVCodecContext, enc); 48 | // TODO 49 | if( isprint(enc->codec_tag&0xFF) && isprint((enc->codec_tag>>8)&0xFF) 50 | && isprint((enc->codec_tag>>16)&0xFF) && isprint((enc->codec_tag>>24)&0xFF)){ 51 | snprintf(buf1, sizeof(buf1), "%c%c%c%c", 52 | enc->codec_tag & 0xff, 53 | (enc->codec_tag >> 8) & 0xff, 54 | (enc->codec_tag >> 16) & 0xff, 55 | (enc->codec_tag >> 24) & 0xff); 56 | } else { 57 | snprintf(buf1, sizeof(buf1), "0x%04x", enc->codec_tag); 58 | } 59 | VALUE result = rb_str_new2(buf1); 60 | return result; 61 | } 62 | 63 | VALUE AVCodecContext_width(VALUE self) 64 | { 65 | AVCodecContext *ptr; 66 | Data_Get_Struct(self, AVCodecContext, ptr); 67 | VALUE result = INT2NUM(ptr->width); 68 | return result; 69 | } 70 | 71 | VALUE AVCodecContext_height(VALUE self) 72 | { 73 | AVCodecContext *ptr; 74 | Data_Get_Struct(self, AVCodecContext, ptr); 75 | VALUE result = INT2NUM(ptr->height); 76 | return result; 77 | } 78 | 79 | VALUE AVCodecContext_channels(VALUE self) 80 | { 81 | AVCodecContext *ptr; 82 | Data_Get_Struct(self, AVCodecContext, ptr); 83 | VALUE result = INT2NUM(ptr->channels); 84 | return result; 85 | } 86 | 87 | VALUE AVCodecContext_sample_rate(VALUE self) 88 | { 89 | AVCodecContext *ptr; 90 | Data_Get_Struct(self, AVCodecContext, ptr); 91 | VALUE result = INT2NUM(ptr->sample_rate); 92 | return result; 93 | } 94 | 95 | VALUE AVCodec_long_name(VALUE self) 96 | { 97 | char* tm; 98 | AVCodec *ptr; 99 | Data_Get_Struct(self, AVCodec, ptr); 100 | tm = ptr->long_name; 101 | return rb_str_new2(tm); 102 | } 103 | 104 | VALUE AVCodec_name(VALUE self) 105 | { 106 | AVCodec *ptr; 107 | Data_Get_Struct(self, AVCodec, ptr); 108 | return rb_str_new2(ptr->name); 109 | } 110 | 111 | VALUE AVCodec_codec_type(VALUE self) 112 | { 113 | char* tm; 114 | AVCodec *ptr; 115 | Data_Get_Struct(self, AVCodec, ptr); 116 | return INT2NUM(ptr->type); 117 | } 118 | 119 | VALUE AVCodecContext_codec_long_name(VALUE self) 120 | { 121 | char* tm; 122 | AVCodecContext *ptr; 123 | Data_Get_Struct(self, AVCodecContext, ptr); 124 | if (ptr->codec == NULL) 125 | return Qnil; 126 | tm = ptr->codec->long_name; 127 | return rb_str_new2(tm); 128 | } 129 | 130 | VALUE AVCodecContext_codec_name(VALUE self) 131 | { 132 | char* tm; 133 | AVCodecContext *ptr; 134 | Data_Get_Struct(self, AVCodecContext, ptr); 135 | if (ptr->codec == NULL) 136 | return Qnil; 137 | tm = ptr->codec->name; 138 | return rb_str_new2(tm); 139 | } 140 | 141 | VALUE supported_avcodecs() 142 | { 143 | VALUE result = rb_hash_new(); 144 | AVCodec *pNextCodec = NULL; 145 | pNextCodec = av_codec_next(pNextCodec); 146 | while (pNextCodec != NULL) { 147 | VALUE sNextCodecName = rb_str_new2(pNextCodec->name); 148 | //VALUE sNextCodecLongName = rb_str_new2(pNextCodec->long_name); 149 | VALUE codec = Data_Wrap_Struct(cFFMpegAVCodec, AVCodecContext_mark, AVCodecContext_free, pNextCodec); 150 | rb_hash_aset(result, sNextCodecName, codec); 151 | pNextCodec = av_codec_next(pNextCodec); 152 | } 153 | return result; 154 | } 155 | 156 | VALUE supported_video_codecs() 157 | { 158 | VALUE result = rb_hash_new(); 159 | AVCodec *pNextCodec = NULL; 160 | pNextCodec = av_codec_next(pNextCodec); 161 | while (pNextCodec != NULL) { 162 | if (pNextCodec->type==CODEC_TYPE_VIDEO) { 163 | VALUE sNextCodecName = rb_str_new2(pNextCodec->name); 164 | //VALUE sNextCodecLongName = rb_str_new2(pNextCodec->long_name); 165 | VALUE codec = Data_Wrap_Struct(cFFMpegAVCodec, AVCodecContext_mark, AVCodecContext_free, pNextCodec); 166 | rb_hash_aset(result, sNextCodecName, codec); 167 | } 168 | pNextCodec = av_codec_next(pNextCodec); 169 | } 170 | return result; 171 | } 172 | 173 | VALUE supported_audio_codecs() 174 | { 175 | VALUE result = rb_hash_new(); 176 | AVCodec *pNextCodec = NULL; 177 | pNextCodec = av_codec_next(pNextCodec); 178 | while (pNextCodec != NULL) { 179 | if (pNextCodec->type==CODEC_TYPE_AUDIO) { 180 | VALUE sNextCodecName = rb_str_new2(pNextCodec->name); 181 | //VALUE sNextCodecLongName = rb_str_new2(pNextCodec->long_name); 182 | VALUE codec = Data_Wrap_Struct(cFFMpegAVCodec, AVCodecContext_mark, AVCodecContext_free, pNextCodec); 183 | rb_hash_aset(result, sNextCodecName, codec); 184 | } 185 | pNextCodec = av_codec_next(pNextCodec); 186 | } 187 | return result; 188 | } 189 | /* 190 | * Similar to avcodec_string on ffmpeg, but only 191 | * gets the name using thse same logic. Uses the AVI tags 192 | */ 193 | VALUE avcodec_canonical_name(AVCodecContext *enc) 194 | { 195 | const char *codec_name; 196 | AVCodec *p; 197 | char buf1[128]; 198 | 199 | p = avcodec_find_decoder(enc->codec_id); 200 | if (p) { 201 | codec_name = p->name; 202 | } else if (enc->codec_id == CODEC_ID_MPEG2TS) { 203 | /* fake mpeg2 transport stream codec (currently not 204 | * registered) */ 205 | codec_name = "mpeg2ts"; 206 | // } else if (enc->codec_name[0] != '\0') { 207 | // codec_name = enc->codec_name; 208 | } else { 209 | /* output avi tags */ 210 | if( isprint(enc->codec_tag&0xFF) && isprint((enc->codec_tag>>8)&0xFF) 211 | && isprint((enc->codec_tag>>16)&0xFF) && isprint((enc->codec_tag>>24)&0xFF)){ 212 | snprintf(buf1, sizeof(buf1), "%c%c%c%c", 213 | enc->codec_tag & 0xff, 214 | (enc->codec_tag >> 8) & 0xff, 215 | (enc->codec_tag >> 16) & 0xff, 216 | (enc->codec_tag >> 24) & 0xff); 217 | } else { 218 | snprintf(buf1, sizeof(buf1), "0x%04x", enc->codec_tag); 219 | } 220 | codec_name = buf1; 221 | } 222 | 223 | return rb_str_new2(codec_name); // TODO on the ruby side, check enc->mb_decision to add the (hq) if we want to. 224 | } 225 | 226 | 227 | void Init_ffmpeg_ruby_avcodec(VALUE module) 228 | { 229 | 230 | cFFMpegAVCodec = rb_define_class_under(module, "AVCodec", rb_cObject); 231 | rb_define_method(cFFMpegAVCodec, "name", AVCodec_name, 0); 232 | rb_define_method(cFFMpegAVCodec, "long_name", AVCodec_long_name, 0); 233 | rb_define_method(cFFMpegAVCodec, "codec_type", AVCodec_codec_type, 0); 234 | rb_define_singleton_method(cFFMpegAVCodec, "supported_avcodecs", supported_avcodecs, 0); 235 | rb_define_singleton_method(cFFMpegAVCodec, "supported_video_codecs", supported_video_codecs, 0); 236 | rb_define_singleton_method(cFFMpegAVCodec, "supported_audio_codecs", supported_audio_codecs, 0); 237 | cFFMpegAVCodecContext = rb_define_class_under(module, "AVCodecContext", rb_cObject); 238 | rb_define_method(cFFMpegAVCodecContext, "long_name", AVCodecContext_codec_long_name, 0); 239 | rb_define_method(cFFMpegAVCodecContext, "name", AVCodecContext_codec_name, 0); 240 | rb_define_method(cFFMpegAVCodecContext, "codec_type", AVCodecContext_codec_type, 0); 241 | rb_define_method(cFFMpegAVCodecContext, "fourcc_tag", AVCodecContext_fourcc_tag, 0); 242 | rb_define_method(cFFMpegAVCodecContext, "codec_id", AVCodecContext_codec_id, 0); 243 | rb_define_method(cFFMpegAVCodecContext, "width", AVCodecContext_width, 0); 244 | rb_define_method(cFFMpegAVCodecContext, "height", AVCodecContext_height, 0); 245 | rb_define_method(cFFMpegAVCodecContext, "channels", AVCodecContext_channels, 0); 246 | rb_define_method(cFFMpegAVCodecContext, "sample_rate", AVCodecContext_sample_rate, 0); 247 | 248 | // Codec Types, for AVCodecContext 249 | rb_define_const(module, "CODEC_TYPE_VIDEO", INT2NUM(CODEC_TYPE_VIDEO)); 250 | rb_define_const(module, "CODEC_TYPE_AUDIO", INT2NUM(CODEC_TYPE_AUDIO)); 251 | rb_define_const(module, "CODEC_TYPE_DATA", INT2NUM(CODEC_TYPE_DATA)); 252 | rb_define_const(module, "CODEC_TYPE_SUBTITLE", INT2NUM(CODEC_TYPE_SUBTITLE)); 253 | rb_define_const(module, "CODEC_TYPE_ATTACHMENT", INT2NUM(CODEC_TYPE_ATTACHMENT)); 254 | rb_define_const(module, "MAP_CODEC_TYPES", codec_type_map()); 255 | 256 | } 257 | --------------------------------------------------------------------------------