Тема: AHK: Объединение MTS, MPG и AVI файлов с sd-карты либо из папки
Скрипт объединяет mts, mpg и avi файлы записанные на камеры Sony без потери в качестве и рассинхронов.
Если находит и MTS и AVI и MPG файлы, то просит выбрать нужный формат.
Нужно иметь установленный ffmpeg.
Можно выводить аудио отдельно для последующей обработки в звуковом редакторе.
Если в MTS было запихнуто PCM, то происходит перекодировка аудио в ac3 320k.
В MPG аудио конвертируется в MP2 320kb.
MTS, MPG и AVI должны быть записаны с одинаковыми кодеками и параметрами.
Можно выбрать с какого по какой файл объединять.
Если эти поля пустые, то берется первый и последний файлы.
Выбирать можно с помощью кнопки Browse - откроется сразу нужная папка со структуры SD карты, либо скопировав файл и вставив его путь - правой кнопкой мыши.
InputFolder := "H:\"
OutputFolder := "D:\sizeti"
AudioSeparate := 0 ; 0 - false, 1 - true
ffmpeg := "C:\Program Files\ffmpeg\bin\ffmpeg.exe"
if !FileExist(ffmpeg)
{
MsgBox, 262144,, Установите ffmpeg
ExitApp
}
#SingleInstance force
SetBatchLines, -1
SetTitleMatchMode, 2
DetectHiddenWindows, on
OnExit, Exit
OnMessage(0x204, "WM_RBUTTONDOWN")
OnMessage(0x205, "WM_RBUTTONUP")
InputFolderDefault := RegexReplace(InputFolder,"\\$")
Gui, +AlwaysOnTop
Gui, Add, Text, x10 y10, Output Name:
Gui, Add, Edit, vName w400 x10 y30
Gui, Add, Text, x10 y60, From:
Gui, Add, Edit, vFrom w400 x10 y80
Gui, Add, Button,gBrowse v1 w80 x420 y80, Browse
Gui, Add, Text, x10 y110, To:
Gui, Add, Edit, vTo w400 x10 y130
Gui, Add, Button,gBrowse v2 w80 x420 y130, Browse
Gui, Add, Text, x10 y160, Input Folder:
Gui, Add, Edit, vInputFolder w400 x10 y180, %InputFolder%
Gui, Add, Button,gBrowse v3 w80 x420 y180, Browse
Gui, Add, Text, x10 y210, Output Folder:
Gui, Add, Edit, vOutputFolder w400 x10 y230, %OutputFolder%
Gui, Add, Button,gBrowse v4 w80 x420 y230, Browse
Gui, Add, CheckBox, x105 y265 w100 h20 vWav Checked%AudioSeparate%, Wav отдельно
Gui, Add, CheckBox, x212 y265 w50 h20 vAvi gCheck, AVI
Gui, Add, CheckBox, x263 y265 w50 h20 vMts gCheck, MTS
Gui, Add, CheckBox, x318 y265 w50 h20 vMpg gCheck, MPG
Gui, Add, CheckBox, x373 y265 w50 h20 vAll gCheck Checked, ALL
Gui, Add, Button, x10 w40 y267 Default, OK
Gui, Show
Gui, 2: +OwnDialogs
Gui, 2: Add, Progress, x6 y40 w460 h20 Range0-1000000 vProgress
Gui, 2: Add, Text, x6 y10 w240 h20 vDo, Идет проверка файлов
Menu, CMenu, Add, Paste, Handler
return
Handler:
GuiControl,,%Control%, %ClipBoard%
return
Check:
If (A_GuiControl="AVI")
{
GuiControlGet, State,,AVI
If State
{
GuiControl,,MTS, 0
GuiControl,,MPG, 0
GuiControl,,ALL, 0
}
Else
GuiControl,,ALL, 1
}
Else if (A_GuiControl="MTS")
{
GuiControlGet, State,,MTS
If State
{
GuiControl,,AVI, 0
GuiControl,,MPG, 0
GuiControl,,ALL, 0
}
Else
GuiControl,,ALL, 1
}
Else if (A_GuiControl="MPG")
{
GuiControlGet, State,,MPG
If State
{
GuiControl,,MTS, 0
GuiControl,,AVI, 0
GuiControl,,ALL, 0
}
Else
GuiControl,,ALL, 1
}
Else
{
GuiControl,,MTS, 0
GuiControl,,AVI, 0
GuiControl,,MPG, 0
GuiControl,,ALL, 1
}
return
Browse:
Gui, -AlwaysOnTop +OwnDialogs
if (A_GuiControl = 3) or (A_GuiControl = 4)
{
if (A_GuiControl = 3)
Prompt := "Select Input Folder"
else
Prompt := "Select Output Folder"
FileSelectFolder, SelectedFile, ::{20d04fe0-3aea-1069-a2d8-08002b30309d}, 0, %Prompt%
}
else
{
GuiControlGet, Avi
GuiControlGet, Mpg
GuiControlGet, Mts
GuiControlGet, All
GuiControlGet, InputFolder
InputFolder := RegexReplace(InputFolder,"\\$")
if !FileExist(InputFolder)
{
MsgBox, Input Folder не существует
GuiControl, Focus, InputFolder
return
}
if (InputFolder = InputFolderDefault)
{
if (all = 1)
{
targetFolder := InputFolder "\PRIVATE\SONY\DVF\100DVF"
if !FileExist(targetFolder "\*.avi")
{
targetFolder := InputFolder "\PRIVATE\AVCHD\BDMV\STREAM"
if !FileExist(targetFolder "\*.mts")
{
targetFolder := InputFolder "\MP_ROOT\100PNV01"
if !FileExist(targetFolder "\*.mpg")
targetFolder := InputFolder
}
}
}
if (avi = 1)
{
targetFolder := InputFolder "\PRIVATE\SONY\DVF\100DVF"
if !FileExist(targetFolder "\*.avi")
targetFolder := InputFolder
}
if (mts = 1)
{
targetFolder := InputFolder "\PRIVATE\AVCHD\BDMV\STREAM"
if !FileExist(targetFolder "\*.mts")
targetFolder := InputFolder
}
if (mpg = 1)
{
targetFolder := InputFolder "\MP_ROOT\100PNV01"
if !FileExist(targetFolder "\*.mpg")
targetFolder := InputFolder
}
}
else
targetFolder := InputFolder
if (A_GuiControl = 1)
Prompt := "Open a file From"
else
Prompt := "Open a file To"
FileSelectFile, SelectedFile,, %targetFolder%, %Prompt%, Video File (*.AVI; *.MTS; *.MPG)
}
if (A_GuiControl = 1) and (SelectedFile != "")
GuiControl,,From,%SelectedFile%
if (A_GuiControl = 2) and (SelectedFile != "")
GuiControl,,To,%SelectedFile%
if (A_GuiControl = 3) and (SelectedFile != "")
GuiControl,,InputFolder,%SelectedFile%
if (A_GuiControl = 4) and (SelectedFile != "")
GuiControl,,OutputFolder,%SelectedFile%
Gui, +AlwaysOnTop -OwnDialogs
Return
GuiClose:
Gui, destroy
ExitApp
2GuiClose:
Gui, 2: destroy
ExitApp
ButtonOK:
Gui, +OwnDialogs
Gui, Submit, NoHide
StringReplace, name, name, %A_SPACE%, _, All
isValidFileName(Name)
If ErrorLevel
{
MsgBox, В имени файла недоступные символы.`nПереименуйте!
GuiControl, Focus, Name
return
}
If (Name = "")
{
MsgBox, Забыли написать имя файла!
GuiControl, Focus, Name
return
}
OutputFolder := RegexReplace(OutputFolder,"\\$")
if !FileExist(OutputFolder)
{
MsgBox, Output Folder не существует
GuiControl, Focus, OutputFolder
return
}
if FileExist(OutputFolder "\" Name ".avi") or FileExist(OutputFolder "\" Name ".mts") or FileExist(OutputFolder "\" Name ".mpg") or FileExist(OutputFolder "\" Name ".wav")
{
MsgBox, Такое имя уже существует.`nПереименуйте!
GuiControl, Focus, Name
return
}
InputFolder := RegexReplace(InputFolder,"\\$")
if !FileExist(InputFolder)
{
MsgBox, Input Folder не существует
GuiControl, Focus, InputFolder
return
}
if (from != "")
{
if !FileExist(From)
{
MsgBox, Файла с таким названием, как в "От" не найдено.
GuiControl, Focus, From
Return
}
If !RegExMatch(From, "^\Q" InputFolder "\E")
{
MsgBox, Путь "От" и Input Folder различаются
GuiControl, Focus, From
Return
}
if (SubStr(from, StrLen(from)-3) != ".avi") and (SubStr(from, StrLen(from)-3) != ".mts") and (SubStr(from, StrLen(from)-3) != ".mpg")
{
MsgBox, В "От" выбран не видеофайл.
GuiControl, Focus, From
Return
}
}
if (to != "")
{
if !FileExist(to)
{
MsgBox, Файла с таким названием, как в "До" не найдено.
GuiControl, Focus, to
Return
}
If !RegExMatch(to, "^\Q" InputFolder "\E")
{
MsgBox, Путь "До" и Input Folder различаются
GuiControl, Focus, to
Return
}
if (SubStr(to, StrLen(to)-3) != ".avi") and (SubStr(to, StrLen(to)-3) != ".mts") and (SubStr(to, StrLen(to)-3) != ".mpg")
{
MsgBox, В "До" выбран не видеофайл.
GuiControl, Focus, to
Return
}
}
if (from != "") and (to != "")
{
if (RegExReplace(from, "(.*)\\.*$","$1") != RegExReplace(to, "(.*)\\.*$","$1"))
{
MsgBox, Директории "От" и "До" различаются.
GuiControl, Focus, From
Return
}
if (From > To)
{
MsgBox, Начальный файл больше конечного.
GuiControl, Focus, From
return
}
}
If (SubStr(InputFolder, 1, 2) != InputFolderDefault)
SdCard := 1
FileList := found := PrevDir := ListDir := prev := dir := List := PrevList := VarList1 := VarList2 := count := size := DifferentCodec := VideoSize := AudioSize := AudioSizeWav := ""
Global Container := ["avi", "mts", "mpg"]
If (from != "") or (to != "")
{
If ((from != "") and (SubStr(from, StrLen(from)-3) != ".AVI")) or ((to != "") and (SubStr(to, StrLen(to)-3) != ".AVI"))
Remove("avi")
If ((from != "") and (SubStr(from, StrLen(from)-3) != ".MTS")) or ((to != "") and (SubStr(to, StrLen(to)-3) != ".MTS"))
Remove("mts")
If ((from != "") and (SubStr(from, StrLen(from)-3) != ".MPG")) or ((to != "") and (SubStr(to, StrLen(to)-3) != ".MPG"))
Remove("mpg")
}
else if (all = 0)
{
If (avi = 0)
Remove("avi")
If (mts = 0)
Remove("mts")
If (mpg = 0)
Remove("mpg")
}
Loop, Files, %InputFolder%\*.*, R
{
Loop % Container.MaxIndex()
{
If (A_LoopFileExt = Container[A_Index])
{
FileList .= A_LoopFileLongPath "|" A_LoopFileSizeMB "`n"
break
}
}
}
Sort, FileList
StringTrimRight, FileList, FileList, 1
Loop % Container.MaxIndex()
{
ext := Container[A_Index]
Loop, parse, FileList, `n
{
StringSplit, ALoopField, A_LoopField, |
SplitPath, ALoopField1, ALoopFileName, ALoopFileDir, ALoopFileExt
if (ext != ALoopFileExt)
Continue
if (found = 1)
{
if (SdCard != 1)
MsgBox, На sd карте находятся avi, mts и mpg файлы.`nВыбирите какие-нибудь одни.
else
MsgBox, В указанной папке находятся avi, mts и mpg файлы.`nВыбирите какие-нибудь одни.
Return
}
if (from != "") and (from != ALoopField1)
Continue
from := ""
if (PrevDir != ALoopFileDir) ; если новая директория
{
if (StrLen(ListDir) > prev) ; если длина последней директории больше предпоследней
{
prev := StrLen(ListDir)
dir := PrevDir
List := PrevList VarList1
}
else
List .= VarList2
PrevList .= VarList2
ListDir := VarList1 := VarList2 := ""
}
PrevDir := ALoopFileDir
ListDir .= ALoopFileDir
VarList1 .= ALoopFileName "|"
VarList2 .= ALoopField1 "|"
Count++
Size += ALoopField2
if (to = ALoopField1)
break
}
if (found = "")
{
if (StrLen(ListDir) > prev)
{
dir := PrevDir
List := PrevList VarList1
}
else
List .= VarList2
PrevList .= VarList2
StringTrimRight, List, List, 1
StringTrimRight, PrevList, PrevList, 1
If (PrevList != "")
{
found = 1
foundExt := ext
}
}
If (A_Index != Container.MaxIndex())
Continue
If (found = "")
{
if (SdCard != 1)
MsgBox, На sd карте нету видео!
else
MsgBox, В указанной папке нету видео!
Return
}
else
{
ext := foundExt
if (Count > 5400)
{
MsgBox, Слишком много файлов.`nМаксимум 5400
Return
}
DriveSpaceFree, FreeSpace, %OutputFolder%
if (ext = "mpg") and (StrLen(List)>16000)
FreeSpace /= 5
else if (ext = "mpg")
FreeSpace /= 4
else if (ext = "mts") or (StrLen(List)>16000)
FreeSpace /= 2.2
else
FreeSpace /= 1.2
if (size > FreeSpace)
{
MsgBox, Ошибка!!! Не хватает места!
return
}
Gui, Hide
Gui, 2: Show, x229 y182 h87 w477
ProgressCount := 1000000/Count
Run, %comspec% /k ,,Hide UseErrorLevel, cPid
WinWait, ahk_pid %cPid%,, 10
DllCall("AttachConsole","uint",cPid)
hCon:=DllCall("CreateFile","str","CONOUT$","uint",0xC0000000,"uint",7,"uint",0,"uint",3,"uint",0,"uint",0)
objShell := ComObjCreate("WScript.Shell")
Loop, parse, PrevList, |
{
ALoopField := A_LoopField
objExec := objShell.Exec("""" ffmpeg """ -i """ A_LoopField """")
strStdErr := ""
while, !objExec.StdErr.AtEndOfStream
strStdErr := objExec.StdErr.ReadAll()
RegExMatch(strStdErr, "s)Duration: (.+?)`,.+?bitrate: (.+?) kb/s.+?(Stream #0:0.+ (.+?) kb/s.+?)\R", match)
Codec := match3
if A_Index = 1
{
FirstALoopField := A_LoopField
FirstCodec := Codec
pcm := RegExMatch(FirstCodec, "Audio: pcm")
if (ext = "mpg")
{
if !InStr(FirstCodec, "720x576")
{
Gui, 2: Hide
MsgBox, Объединить невозможно, так как MPG не 720x576
Gui, Show
Return
}
if InStr(FirstCodec, "16:9")
aspect := "16:9"
else if InStr(FirstCodec, "4:3")
aspect := "4:3"
else
{
Gui, 2: Hide
MsgBox, Неизвестен aspect `n %FirstCodec%
Gui, Show
Return
}
}
}
else
{
loop, parse, Codec, `n, `r
{
if !InStr(FirstCodec, RegexReplace(A_LoopField, "^(\s+?|)Stream #0:\d"))
{
if (DifferentCodec = "")
DifferentCodec := FirstALoopField "`nvs`n"
DifferentCodec .= ALoopField "`n"
break
}
}
}
Duration_array := StrSplit(match1, ":")
Duration := Duration_array[1]*3600 + Duration_array[2]*60 + Duration_array[3]
if (ext = "mpg")
VideoSize += Duration*3800
else
VideoSize += Duration*match2*0.125
if (ext = "mpg")
AudioSize += Duration*1536*0.125
else if (pcm = 0) or (ext = "avi")
AudioSize += Duration*match4*0.125
else
AudioSize += Duration*320*0.125
if (wav = 1)
AudioSizeWav += Duration*1536*0.125
GuiControl, 2:, Progress, +%ProgressCount%
}
VideoSize *=0.9
AudioSize *=0.9
DllCall("CloseHandle", "uint", hCon)
DllCall("FreeConsole")
Process, Close, %cPid%
if (DifferentCodec != "")
{
Gui, 2: Hide
MsgBox, Объединить невозможно, так как у видео разные кодеки.`n%DifferentCodec%
Gui, Show
Return
}
if (AudioSizeWav > 4147200)
{
Gui, 2: Hide
MsgBox, Wav отдельно сохранить нельзя, так как больше 4 гб.
wav = 0
Gui, 2: Show
}
if (StrLen(List)>16000) or (ext = "mts")
{
loop
{
TempFolder := OutputFolder "\TEMP\" RegExReplace(RandomStr(), "[^A-Za-z0-9]", "i")
if !FileExist(TempFolder)
{
FileCreateDir, %TempFolder%
break
}
}
}
if (StrLen(List)>16000)
{
GuiControl, 2:, Progress, 0
GuiControl, 2:, Do, Идет копирование
List := "", dir := TempFolder
setformat,integer,hex
a:= 0x20000
Loop, parse, PrevList, |
{
b := Chr(a)
List .= b "|"
a++
RunWait, %comspec% /c COPY "%A_LoopField%" "%TempFolder%\%b%", , Hide, CopyPID
FileGetSize, SizeF, %TempFolder%\%b%, M
ProgressCount := 1000000/size*SizeF
GuiControl, 2:, Progress, +%ProgressCount%
}
setformat,integer,d
}
SetTimer, CheckSize, 1000
If (ext = "avi") or (ext = "mpg")
{
GuiControl, 2:, Progress, 0
GuiControl, 2:, Do, Идет экспорт видео
FinalSize := VideoSize + AudioSize
Filename := OutputFolder "\" Name ".avi"
if (ext = "avi")
RunWait, %ffmpeg% -i "concat:%list%" -c copy -map_metadata -1 %Filename%, %dir%, hide
if (ext = "mpg")
RunWait, %ffmpeg% -i "concat:%list%" -f avi -vcodec dvvideo -s pal -r pal -aspect %aspect% -pix_fmt yuv420p -vtag dvsd -vf fieldorder=bff -acodec pcm_s16le -ac 2 -ar 48000 -map_metadata -1 %Filename%, %dir%, hide
If (wav = 1)
{
GuiControl, 2:, Progress, 0
GuiControl, 2:, Do, Идет экспорт аудио
FinalSize := AudioSizeWav
Filename := OutputFolder "\" Name ".wav"
if (ext = "avi")
RunWait, %ffmpeg% -i "concat:%list%" -c:a copy -vn -map_metadata -1 %Filename%, %dir%, hide
if (ext = "mpg")
RunWait, %ffmpeg% -i "concat:%list%" -c:a pcm_s16le -ac 2 -ar 48000 -vn -map_metadata -1 %Filename%, %dir%, hide
}
}
If (ext = "mts")
{
GuiControl, 2:, Progress, 0
GuiControl, 2:, Do, Извлекается видео поток
FinalSize := VideoSize
Filename := TempFolder "\v.mts"
RunWait, %ffmpeg% -i "concat:%list%" -c:v copy -an -map_metadata -1 %Filename%, %dir%, hide
GuiControl, 2:, Progress, 0
GuiControl, 2:, Do, Извлекается аудио поток
FinalSize := AudioSize
Filename := TempFolder "\a.ac3"
if (pcm = 0)
RunWait, %ffmpeg% -i "concat:%list%" -c:a copy -vn -map_metadata -1 %Filename%, %dir%, hide
else
RunWait, %ffmpeg% -i "concat:%list%" -c:a ac3 -b:a 320k -ar 48000 -ac 2 -vn -map_metadata -1 %Filename%, %dir%, hide
if (dir = TempFolder)
{
Loop, %TempFolder%
{
If (A_LoopFileName != "a.ac3") and (A_LoopFileName != "v.mts")
FileDelete, %A_LoopFileLongPath%
}
}
GuiControl, 2:, Progress, 0
GuiControl, 2:, Do, Идет экспорт видео
FinalSize := VideoSize + AudioSize
Filename := OutputFolder "\" Name "." ext
RunWait, %ffmpeg% -i v.mts -i a.ac3 -c copy -map_metadata -1 %Filename%, %TempFolder%, hide
If (wav = 1)
{
GuiControl, 2:, Progress, 0
GuiControl, 2:, Do, Идет экспорт аудио
FinalSize := AudioSizeWav
Filename := OutputFolder "\" Name ".wav"
RunWait, %ffmpeg% -i "concat:%list%" -c:a pcm_s16le -ac 2 -ar 48000 -vn -map_metadata -1 %Filename%, %dir%, hide
}
}
Gui, 2: Destroy
MsgBox, 262144,, done
ExitApp
}
}
CheckSize:
FileGetSize, FileSize, %Filename%, K
ProgressCount := 1000000/FinalSize*FileSize
GuiControl, 2:, Progress, %ProgressCount%
return
Exit:
WinClose, ahk_pid %CopyPID%
WinWaitClose, ahk_pid %CopyPID%
WinClose, ahk_exe ffmpeg.exe
WinWaitClose, ahk_exe ffmpeg.exe
FileRemoveDir, %TempFolder%, 1
ExitApp
Remove(ext)
{
for key, val in Container
{
if (val = ext)
{
Container.RemoveAt(A_Index)
return
}
}
}
isValidFileName(_fileName, _isLong=true)
{
forbiddenChars := _isLong ? "[<>|""\\/:*?]" : "[;=+<>|""\]\[\\/']"
ErrorLevel := RegExMatch( _fileName , forbiddenChars )
Return ! ErrorLevel
}
RandomStr(l = 10, i = 48, x = 122)
{
Loop, %l%
{
Random, r, i, x
s .= Chr(r)
}
Return, s
}
WM_RBUTTONDOWN(){
if (A_GuiControl = "From") or (A_GuiControl = "To") or (A_GuiControl = "InputFolder") or (A_GuiControl = "OutputFolder")
{
Global Control := A_GuiControl
GuiControl, Focus, %Control%
Return 0
}
}
WM_RBUTTONUP(){
if (A_GuiControl = "From") or (A_GuiControl = "To") or (A_GuiControl = "InputFolder") or (A_GuiControl = "OutputFolder")
{
Global Control := A_GuiControl
GuiControl, Focus, %Control%
Menu, cMenu, Show
}
}