2016年8月20日 星期六

A note to batch convert using ffmpeg under windows 7

I was trying to compress my recordings from the 2015 NCKU Wind Band Camp to save space. I choose the ffmpeg to do the job because it seems quite efficient and the format is pretty standard. (The Any Video Coverter just don't work, some weird snapshot problem ketp hunting me)
Since the ffmpeg is a command line tool without gui, the batch script conversion uses the .bat file that can be found easily via google, e.g. here, here and here.
for %%a in ("*.mov") do (
    ffmpeg -i "%%a" -c:v libx264 -c:a aac -b:a 128k "output\%%~na.mp4"
)
pause
Here, for all matches of .mov file, the batch variable %%a (double percentage sign %% for batch, % for command line) is fed into ffmpeg. The encoder for video is assigned to be 264  -c:v libx264 with audio codec -b:a aac  and audio bitrate -b:a 128k of 128K. The %%~na means "Expands %%a to a file name only", which is defined in the For loop tutorial of m$ command line.

The above script is placed in the folder, containing
  • pre-processed videos
  • ffmpeg.exe 
  • A "output" subdirectory.
  • "batch_convert.bat" that contains the script above
The process under windows went pretty well.

However, the problem is that, all the file generated by the ffmpeg would have the windows timestamp of "Date Created", "Date Modified", and "Date Accessed" will be set to current time. But I usually manage my files by the "Date Modified" that record the original time the video is shot.

After some deep dive into the windows terminal system, and some hours are spent, I finally found the solution to copy the windows "Date Modified" timestamp to the ffmpeg output file. The trick is to use something called the Power Shell.
The final product is a batch script looks like this:
for %%a in ("*.mov") do (
    ffmpeg -i "%%a" -c:v libx264 -c:a aac -b:a 128k "output\%%~na.mp4"
    powershell.exe -Command "(Get-Item "output\%%~na.mp4").LastWriteTime=(Get-Item %%~na.MOV).LastWriteTime
)
pause
The batch file will first convert the file using ffmpeg, and the same filename "shell variable"(excuse me for the un-professional term" is sent to the powershell command. In the powershell program, the ".LastWriteTime" file object properties is copied directly from source to the output.
I have also tried to assign the "DateTime object" to a powershell variable, unfortunately didn't get it work. Nevertheless, the above script worked.

Keywords: windows, batch, .bat, ffmpeg, Date modified, timestamp, copy timestamp

最近為了整理2015成大管樂營的影片,我想把相機直出的錄影轉檔壓縮。經過一番搜尋,我選擇了ffmpeg,他的編碼包非常完整,而且輸出的格式看起來也很標準。由於ffmpeg沒有操作介面,我們必須使用指令來執行。另外,由於要處裡多個檔案,必須要使用windows下的批次執行檔腳本(Batch 編程)。批次執行檔的寫法可以很輕易地以GOOGLE找到,例如 這裡這裡這裡
for %%a in ("*.mov") do (
    ffmpeg -i "%%a" -c:v libx264 -c:a aac -b:a 128k "output\%%~na.mp4"
)
pause
在這個批次檔中,所有符合.mov附檔名的檔案會被依序以變數 %%a 掃到(批次檔中變數用雙百分比符號%%;指令列用%),並且把變數丟給 ffmpeg。影像編碼器用指令 -c:v libx264 設定為264;音訊編碼器設定成AAC -b:a aac ;而音訊位元率設成128K  -b:a 128k。在批次檔中"%%~na" 表示「僅表示%%a中檔名的部分」 詳細的資訊可以在 m$ 對於批次迴圈的解釋中找到
以上的批次腳本以及以下東西被放在同一個資料夾中:
  • 待轉檔影片
  • ffmpeg.exe 
  • 名為"output"的資料夾
  • 包含以上腳本的"batch_convert.bat"
基本上這個程序能夠順利執行

然而,由於我個人在整理照片時,通常是使用windows下檔案性質中的「修改日期」做為參考。這個時間通常是紀錄影片照片拍攝地當下,不會被修改。然而,ffmpeg產生出來的檔案,「修改日期」、「存取日期」、「建立日期」都理所當然的被設定在轉檔的當下。

經過一番搜尋與數小時的windows指令系統研究之後,我終於找到能夠將原始「修改日期」時間戳複製到ffmpeg輸出檔案的方法。訣竅是使用Power Shell。
最後,我的批次腳本如下所示:
for %%a in ("*.mov") do (
    ffmpeg -i "%%a" -c:v libx264 -c:a aac -b:a 128k "output\%%~na.mp4"
    powershell.exe -Command "(Get-Item "output\%%~na.mp4").LastWriteTime=(Get-Item %%~na.MOV).LastWriteTime
)
pause
這個批次檔會先與前述相同,使用ffmpeg做重新編碼。接下來,同一個檔案的名稱被送到powershell裡面。使用powershell強大的功能,我們可以直接呼叫檔案物件中 ".LastWriteTime" 屬性,並且直接複製到新的檔案。
我也曾經嘗試要把這個檔案屬性複製到一個powershell變數中,可惜沒有成功,產生出來的變數都是null object。不知道哪裡弄錯了。總之,以上腳本可以使用

關鍵字:windows, 批次檔, .bat, ffmpeg, 修改日期, 時間戳,  複製時間戳

沒有留言:

張貼留言