背景视频处理与导轨

我想上传的视频转换后台,运行窗口。一些我在用的:背景视频处理与导轨

gem 'paperclip' 

gem 'delayed_job_active_record'

gem 'ffmpeg'

我已经编辑注册表,以允许FFmpeg的命令,从任何地方跑了,我得到我认为是ffmpeg的,因为它消失得太快的弹出,猜命令所以如果有人知道它有什么问题,请告诉我。但真正的问题是,它只是挂在那儿,它说:

[2012-12-09 22:47:03] ERROR invalid body size. 

[2012-12-09 22:47:03] ERROR Errno::ECONNABORTED: An established connection was a

borted by the software in your host machine.

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:396:i

n `write'

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:396:i

n `<<'

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:396:i

n `_write_data'

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:368:i

n `send_body_string'

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:249:i

n `send_body'

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:152:i

n `send_response'

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpserver.rb:110:in

`run'

C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/server.rb:191:in `blo

ck in start_thread'

有谁知道如何正确地得到这个工作?我已经阅读了一些教程,其中包含我需要的一些小部分,但我无法让它们一起工作。这里是我到目前为止,还是让我知道,如果你需要更多:

型号:

class Video < ActiveRecord::Base 

belongs_to :user

has_many :comments, dependent: :destroy

attr_accessible :video, :user_id, :video_file_name, :title, :public, :description, :views

has_attached_file :video, url: "https://stackoverflow.com/users/:user_id/videos/:id/:basename_:style.:extension"

#process_in_background :video #causes death

validates :video, presence: true

validates :description, presence: true, length: { minimum: 5, maximum: 100}

validates :title, presence: true, length: { minimum: 1, maximum: 15 }

validates_attachment_size :video, less_than: 1.gigabytes

validates_attachment :video, presence: true

default_scope order: 'created_at DESC'

Paperclip.interpolates :user_id do |attachment, style|attachment.instance.user_id

end

#before_post_process do |video|

# false if video.status == "converting"

#end

def perform

command = <<-end_command

start ffmpeg -i #{ '/public/users/:user_id/videos/:id/:basename_:style.:extension' } -ar 22050 -ab 32 -s 1280x720 -vcodec webm -r 25 -qscale 8 -f webm -y #{ '/public/users/:user_id/videos/:id/:basename_.webm' }

end_command

success = system(command)

logger.debug 'Converting File: ' + success.to_s

if success && $?.exitstatus.to_i == 0

#self.converted!

self.status = "converted"

else

#self.failure!

self.status = "failed"

end

end

handle_asynchronously :perform

def self.search(search)

if search

find(:all, conditions: ["public = 't' AND title LIKE ?", "%#{search}%"], order: "created_at DESC")

else

find(:all, conditions: ["public = 't'"], order: "created_at DESC")

end

end

def self.admin_search(search)

if search

find(:all, conditions: ['title LIKE ?', "%#{search}%"], order: "created_at DESC")

else

find(:all, order: "created_at DESC")

end

end

private

# This updates the stored filename with the new flash video file

def set_new_filename

#update_attribute(:filename, "#{filename}.#{id}.webm")

update_attribute(:content_type, "video/x-webm")

end

end

控制器:

class VideosController < ApplicationController 

before_filter :signed_in_user, only: [:upload, :update, :destroy]

before_filter :admin_user, only: :admin_index

def upload

@video = Video.new

# generate a unique id for the upload

@uuid = (0..29).to_a.map {|x| rand(10)}

end

def create

@video = Video.new(params[:video])

@video.user_id = current_user.id

if @video.save

@video.delay.perform

flash[:success] = "Uploaded Succefully!"

redirect_to @video.user

Delayed::Worker.new.start

else

render 'upload'

end

end

def show

@video = Video.find(params[:id])

@comments = @video.comments.paginate(page: params[:page], per_page: 6)

if [email protected]

if !signed_in? || current_user.id != @video.user_id && !current_user.admin && !current_user.approved?(@video.user)

flash[:notice] = "Video is private"

redirect_to root_path

end

end

end

def update

@video = Video.find(params[:id])

if @video.update_attributes(params[:video])

flash[:success] = "Video preferences saved"

else

flash[:fail] = "Failed to update video preferences"

end

redirect_to :back

end

def destroy

@video = Video.find(params[:id])

@video.destroy

flash[:deleted] = "Deleted Succefully!"

redirect_to :back

end

def index

@videos = Video.paginate(page: params[:page], per_page: 6).search(params[:search])

end

def admin_index

@videos = Video.paginate(page: params[:page], per_page: 6).admin_search(params[:search])

end

def ajax_video_comments

@video = Video.find(params[:id])

@comments = @video.comments.paginate(page: params[:page], per_page: 6)

respond_to do |format|

format.js { render partial: 'shared/comments', content_type: 'text/html' }

end

end

def ajax_video_watched

@video = Video.find(params[:id])

@video.views += 1

@video.save

end

private

def signed_in_user

redirect_to root_path, notice: "Please Login." unless signed_in?

end

def admin_user

redirect_to(root_path) unless current_user.admin?

end

end

回答:

你的表应该有这些列:

  • video_file_name
  • video_content_type
  • video_file_size
  • video_updated_at
  • video_meta

我增加了一些额外的回形针魔术给你的模型,很明显,你可以调整为ffmpeg的设置。这并不是所有的原始代码,但我不记得我在哪里找到零碎的东西,所以如果有人认识到它可以随意获得信贷。

class Video < ActiveRecord::Base 

belongs_to :user

has_many :comments, dependent: :destroy

attr_accessible :video, :user_id, :video_file_name,

:title, :public, :description, :views

has_attached_file :video,

url: "https://stackoverflow.com/users/:user_id/videos/:id/:basename_:style.:extension"

styles: {

:original => { :geometry => "1280x720", :format => 'mp4', :streaming => true, :convert_options => { :input => {}, :output => {'c:v' => 'libx264', vprofile: 'high', preset: 'medium', 'b:v' => '1250k', maxrate: '1250k', bufsize: '2500k', pix_fmt: 'yuv420p', flags: '+mv4+aic', threads: 'auto', 'b:a' => '128k', strict: '-2'}} },

:medium => { :geometry => "854x480", :format => 'mp4', :streaming => true, :convert_options => { :input => {}, :output => {'c:v' => 'libx264', vprofile: 'high', preset: 'medium', 'b:v' => '750k', maxrate: '750k', bufsize: '1500k', pix_fmt: 'yuv420p', flags: '+mv4+aic', threads: 'auto', 'b:a' => '128k', strict: '-2'}} },

:small => { :geometry => '640x360', :format => 'mp4', :streaming => true, :convert_options => { :input => {}, :output => {'c:v' => 'libx264', vprofile: 'high', preset: 'medium', 'b:v' => '250k', maxrate: '250k', bufsize: '500k', pix_fmt: 'yuv420p', flags: '+mv4+aic', threads: 'auto', 'b:a' => '128k', strict: '-2'}} },

:thumb => { :geometry => "160x90", :format => 'jpg', :time => 10 }

},

processors: [:ffmpeg, :qtfaststart]

validates :video, presence: true

validates :description, presence: true, length: { minimum: 5, maximum: 100}

validates :title, presence: true, length: { minimum: 1, maximum: 15 }

validates_attachment_size :video, less_than: 1.gigabytes

validates_attachment :video, presence: true

default_scope order: 'created_at DESC'

# cancel post-processing now, and set flag...

before_post_process do |video|

if video.status == nil

video.status = "queuing"

false # halts processing

end

end

# ...and perform after save in background

after_commit do |video|

if video.status == "queuing"

Delayed::Job.enqueue VideoJob.new(video.id), :queue => 'video'

video.status == "queued"

video.save(validations: false)

end

end

# generate styles (downloads original first)

def regenerate_styles!

self.video.reprocess!

end

# detect if our source file has changed

def video_changed?

self.video_file_size_changed? ||

self.video_file_name_changed? ||

self.video_content_type_changed? ||

self.video_updated_at_changed?

end

# Class to perform with delayed jobs

class VideoJob < Struct.new(:video_id)

def perform

video = Video.find(self.video_id)

video.status = "processing"

video.save(validations: false)

video.regenerate_styles!

end

def success(job)

video = Video.find(self.video_id)

video.status = "complete"

video.save(:validate => false)

end

def error(job, exception)

video = Video.find(self.video_id)

video.status = "error"

video.save(:validate => false)

end

end

end

回形针处理器(/lib/paperclip_processors/ffmpeg.rb):

module Paperclip 

class Ffmpeg < Processor

attr_accessor :geometry, :format, :whiny, :convert_options

# Creates a Video object set to work on the +file+ given. It

# will attempt to transcode the video into one defined by +target_geometry+

# which is a "WxH"-style string. +format+ should be specified.

# Video transcoding will raise no errors unless

# +whiny+ is true (which it is, by default. If +convert_options+ is

# set, the options will be appended to the convert command upon video transcoding.

def initialize file, options = {}, attachment = nil

@convert_options = {

:input => {},

:output => { :y => nil }

}

unless options[:convert_options].nil? || options[:convert_options].class != Hash

unless options[:convert_options][:input].nil? || options[:convert_options][:input].class != Hash

@convert_options[:input].reverse_merge! options[:convert_options][:input]

end

unless options[:convert_options][:output].nil? || options[:convert_options][:output].class != Hash

@convert_options[:output].reverse_merge! options[:convert_options][:output]

end

end

@geometry = options[:geometry]

@file = file

@keep_aspect = [email protected]? && @geometry[-1,1] != '!'

@pad_only = @keep_aspect && @geometry[-1,1] == '#'

@enlarge_only = @keep_aspect && @geometry[-1,1] == '<'

@shrink_only = @keep_aspect && @geometry[-1,1] == '>'

@whiny = options[:whiny].nil? ? true : options[:whiny]

@format = options[:format]

@time = options[:time].nil? ? 3 : options[:time]

@current_format = File.extname(@file.path)

@basename = File.basename(@file.path, @current_format)

@meta = identify

@pad_color = options[:pad_color].nil? ? "black" : options[:pad_color]

attachment.instance_write(:meta, @meta)

end

# Performs the transcoding of the +file+ into a thumbnail/video. Returns the Tempfile

# that contains the new image/video.

def make

src = @file

dst = Tempfile.new([@basename, @format ? ".#{@format}" : ''])

dst.binmode

parameters = []

# Add geometry

if @geometry

# Extract target dimensions

if @geometry =~ /(\d*)x(\d*)/

target_width = $1

target_height = $2

end

# Only calculate target dimensions if we have current dimensions

unless @meta[:size].nil?

current_geometry = @meta[:size].split('x')

# Current width and height

current_width = current_geometry[0]

current_height = current_geometry[1]

if @keep_aspect

if @enlarge_only

if current_width.to_i < target_width.to_i

# Keep aspect ratio

width = target_width.to_i

height = (width.to_f/(@meta[:aspect].to_f)).to_i

@convert_options[:output][:s] = "#{width.to_i/2*2}x#{height.to_i/2*2}"

else

return nil

end

elsif @shrink_only

if current_width.to_i > target_width.to_i

# Keep aspect ratio

width = target_width.to_i

height = (width.to_f/(@meta[:aspect].to_f)).to_i

@convert_options[:output][:s] = "#{width.to_i/2*2}x#{height.to_i/2*2}"

else

return nil

end

elsif @pad_only

# Keep aspect ratio

width = target_width.to_i

height = (width.to_f/(@meta[:aspect].to_f)).to_i

# We should add half the delta as a padding offset Y

pad_y = (target_height.to_f - height.to_f)/2

if pad_y > 0

@convert_options[:output][:vf] = "scale=#{width}:-1,pad=#{width.to_i}:#{target_height.to_i}:0:#{pad_y}:#@pad_color"

else

@convert_options[:output][:vf] = "scale=#{width}:-1,crop=#{width.to_i}:#{height.to_i}"

end

else

# Keep aspect ratio

width = target_width.to_i

height = (width.to_f/(@meta[:aspect].to_f)).to_i

@convert_options[:output][:s] = "#{width.to_i/2*2}x#{height.to_i/2*2}"

end

else

# Do not keep aspect ratio

@convert_options[:output][:s] = "#{target_width.to_i/2*2}x#{target_height.to_i/2*2}"

end

end

end

# Add format

case @format

when 'jpg', 'jpeg', 'png', 'gif' # Images

@convert_options[:input][:ss] = @time

@convert_options[:output][:vframes] = 1

@convert_options[:output][:f] = 'image2'

end

# Add source

parameters << @convert_options[:input].map { |k,v| "-#{k.to_s} #{v} "}

parameters << "-i ':source'"

parameters << @convert_options[:output].map { |k,v| "-#{k.to_s} #{v} "}

parameters << "':dest'"

parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")

begin

success = Paperclip.run("ffmpeg", parameters, :source => "#{File.expand_path(src.path)}", :dest => File.expand_path(dst.path))

rescue Cocaine::ExitStatusError => e

raise Paperclip::Error, "error while processing video for #{@basename}: #{e}" if @whiny

end

dst

end

def identify

meta = {}

command = "ffmpeg -i \"#{File.expand_path(@file.path)}\" 2>&1"

ffmpeg = IO.popen(command)

ffmpeg.each("\r") do |line|

# Matching lines like:

# Video: h264, yuvj420p, 640x480 [PAR 72:72 DAR 4:3], 10301 kb/s, 30 fps, 30 tbr, 600 tbn, 600 tbc

if line.include?(' Video: ')

start = line.index('Video:')

items = line[start, 150].split(',')

size = items[2].strip!.split(' ').first

meta[:size] = size.to_s

meta[:aspect] = size.split('x').first.to_f/size.split('x').last.to_f

end

# Matching Duration: 00:01:31.66, start: 0.000000, bitrate: 10404 kb/s

if line =~ /Duration:(\s.?(\d*):(\d*):(\d*\.\d*))/

meta[:length] = $2.to_s + ":" + $3.to_s + ":" + $4.to_s

end

end

meta

end

end

class Attachment

def meta

instance_read(:meta)

end

end

end

回形针处理器(/lib/paperclip_processors/qtfaststart.rb):

module Paperclip 

class Qtfaststart < Processor

attr_accessor :streaming, :format, :whiny

# Creates a Video object set to work on the +file+ given. It

# will attempt to reposition the moov atom in the video given

# if +streaming+ is set.

def initialize file, options = {}, attachment = nil

@streaming = options[:streaming]

@file = file

@whiny = options[:whiny].nil? ? true : options[:whiny]

@format = options[:format]

@current_format = File.extname(@file.path)

@basename = File.basename(@file.path, @current_format)

@meta = attachment.meta

attachment.instance_write(:meta, @meta)

end

# Performs the atom repositioning on +file+.

# Returns the Tempfile that contains the new video or the original

# file if +streaming+ wasn't set.

def make

return @file unless @streaming

src = @file

dst = Tempfile.new([@basename, @format ? ".#{@format}" : ''])

dst.binmode

parameters = []

# Add source

parameters << ":source"

# Add destination

parameters << ":dest"

parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")

Paperclip.log("[qtfaststart] #{parameters}")

begin

success = Paperclip.run("qt-faststart", parameters, :source => "#{File.expand_path(src.path)}", :dest => File.expand_path(dst.path))

rescue Cocaine::ExitStatusError => e

raise PaperclipError, "error while processing video for #{@basename}: #{e}" if @whiny

end

dst

end

end

class Attachment

def meta

instance_read(:meta)

end

end

end

您控制器可使用一些更多的清洁,但我只是作出调整,使其与我的其他变化工作。

class VideosController < ApplicationController 

before_filter :signed_in_user, only: [:upload, :update, :destroy]

before_filter :admin_user, only: :admin_index

def upload

@video = Video.new

# generate a unique id for the upload

@uuid = (0..29).to_a.map {|x| rand(10)}

end

def create

@video = Video.new(params[:video])

@video.user_id = current_user.id

if @video.save

flash[:success] = "Uploaded Succefully!"

redirect_to @video.user

else

render 'upload'

end

end

def show

@video = Video.find(params[:id])

@comments = @video.comments.paginate(page: params[:page], per_page: 6)

if [email protected]

if !signed_in? || current_user.id != @video.user_id && !current_user.admin && !current_user.approved?(@video.user)

flash[:notice] = "Video is private"

redirect_to root_path

end

end

end

def update

@video = Video.find(params[:id])

if @video.update_attributes(params[:video])

flash[:success] = "Video preferences saved"

else

flash[:fail] = "Failed to update video preferences"

end

redirect_to :back

end

def destroy

@video = Video.find(params[:id])

@video.destroy

flash[:deleted] = "Deleted Succefully!"

redirect_to :back

end

def index

@videos = Video.paginate(page: params[:page], per_page: 6).search(params[:search])

end

def admin_index

@videos = Video.paginate(page: params[:page], per_page: 6).admin_search(params[:search])

end

def ajax_video_comments

@video = Video.find(params[:id])

@comments = @video.comments.paginate(page: params[:page], per_page: 6)

respond_to do |format|

format.js { render partial: 'shared/comments', content_type: 'text/html' }

end

end

def ajax_video_watched

@video = Video.find(params[:id])

@video.views += 1

@video.save

end

private

def signed_in_user

redirect_to root_path, notice: "Please Login." unless signed_in?

end

def admin_user

redirect_to(root_path) unless current_user.admin?

end

end

你可以运行一个delayed_jobs工作线程,它对你来说最合适。我可能在这里犯了一些错误,但我试图使我的方法适应您当前的模型。

回答:

有点晚了,但我们使用paperclip-ffmeg宝石 - 一些你可能想看看

所有你需要做的就是把这些宝石到您的gemfile,然后你只需要定义处理器:ffmpeg

这是我们从代码中的活生生的例子:

class Attachment < ActiveRecord::Base 

has_attached_file :attachment,

styles: lambda { |a| a.instance.is_image? ? {:small => "x200>", :medium => "x300>", :large => "x400>"} : {:thumb => { :geometry => "100x100#", :format => 'jpg', :time => 10}, :medium => { :geometry => "300x300#", :format => 'jpg', :time => 10}}},

:processors => lambda { |a| a.is_video? ? [ :ffmpeg ] : [ :thumbnail ] }

def is_video?

attachment.instance.attachment_content_type =~ %r(video)

end

def is_image?

attachment.instance.attachment_content_type =~ %r(image)

end

end

以上是 背景视频处理与导轨 的全部内容, 来源链接: utcz.com/qa/262128.html

回到顶部