Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2862

【Ruby】FAT32用にmp4を4GB以内に分割する【ffmpeg】

$
0
0

FAT32の壁

通常であればFAT32なんて使わずに他のフォーマットを使えばいいわけで、そしてリミットである1ファイル4GB以上の動画ファイルを扱うなんてことはあまりないと思うが…

たまに、この問題に遭遇する。
例えばFire TV(2015)で使えるmicro SDがまさにそうで、ハードウェアとしては4Kに対応しているのに4GBの上限というのは結構面倒臭い。

制約としては以下を考慮している

  1. mp4の収録時間とファイルサイズでいい感じに分割する(ffmpegの-fsオプションは使わない)
  2. 再エンコードしない
  3. 分割したファイルがちゃんと再生される

例えば1については

ffmpeg -i input.mp4 -fsファイルサイズ output.mp4

このオプションを使えば分割できるんだけど、これじゃなくて例えば合計が32GBで収録時間が1時間20分なら4分割して20分ずつの連番ファイルにしたい。
もちろん、単純にこれをやると微妙に4GBを超えるファイルができる可能性があるのでバッファを入れる

2については、当たり前で再エンコードしてたら時間がもったいない。
3については、ffmpegのよくある罠で、オプションを書く位置や指定の仕方によって最初の数秒音声のみだけになるようなファイルができてしまうからだ。(フレーム指定すればいいんだけど)

とりあえずffmpegとbashでmp4の収録時間を取得する

ffmpeg -i hogehoge.mp4 2>&1 | grep"Duration

これで、返ってくる

特定の時間でちゃんと分割

とりあえず、20分ずつ分割する例

NGな例(2番目のファイルの最初数秒がは音声のみで動画が真っ黒になる)

ffmpeg -i imput.mp4 -ss 00:00:00 -to 00:20:00 -c copy output1.mp4
ffmpeg -i imput.mp4 -ss 00:20:00 -to 00:40:00 -c copy output2.mp4

なので、

ffmpeg -ss 00:00:00 -i imput.mp4 -t 1200 -c copy output1.mp4
ffmpeg -ss 00:20:00 -i imput.mp4 -t 1200 -c copy output1.mp4

上記のようにやる必要がある。

ということで、Rubyでサクっとやる

とりあえず3.8GBを上限とした書き殴りのスクリプト

# coding: utf-8require'time'require'filesize'DEBUG=falseLIMIT_GB=3.8classMaindefinitialize(argv)@file=argv@dir_name=File.dirname(@file)@base_name=File.basename(@file)enddefget_total_timeret=`ffmpeg -i #{@file} 2>&1 | grep "Duration"`total_time=ret.gsub(/,.+|\n|\..+/,"").gsub(/.+Duration: /,"")returntotal_timeenddefget_split_countfile_size=File.stat(@file).sizefile_size=Filesize.from("#{file_size} B").to_f('GB')+1.0file_size=file_size.to_s.gsub(/\..+/,"").to_isplit_count=(file_size/LIMIT_GB).round+1returnsplit_countenddefcalculation_total_minutestotal_times_array=get_total_time.split(":").reverse# add buffertotal_minutes=total_times_array[1].to_i+1iftotal_times_array[2]total_minutes+=total_times_array[2].to_i*60endreturntotal_minutesenddefget_settingsetting=Hash.new# add buffersplit_count=get_split_count+1total_minutes=calculation_total_minutessplit_minute=total_minutes/split_countsetting['split_count']=split_countsetting['split_minute']=split_minutereturnsettingenddefsec2hms(sec)time=secsec=time%60time/=60mins=time%60time/=60hours=time%24return[sprintf("%02d",hours),sprintf("%02d",mins),sprintf("%02d",sec)].join(":")enddefsplit_mp4setting=get_settingsetting['split_count'].timesdo|n|begin_time=sec2hms(n*setting['split_minute']*60)cmd="ffmpeg -ss #{begin_time} -i '#{@file}' -t #{setting['split_minute']*60} -c copy '#{@file.gsub('.mp4',"")}_#{sprintf("%03d",n+1)}.mp4'"ifDEBUGputscmdelse`#{cmd}`endendendendif!ARGV[0]puts"---------------------------"puts"Not find argv"puts""puts"ruby #{__FILE__} target.mp4"puts"---------------------------"sleep0.5exitendifARGV[0]!~/mp4$/puts"---------------------------"puts"Please choose mp4 file"puts"---------------------------"sleep0.5exitendmain=Main.new(ARGV[0])main.split_mp4

サクッとやれればいいのでコーディングがアレなのは失礼


Viewing all articles
Browse latest Browse all 2862

Trending Articles