News:

--

Main Menu

Add second audio track to MKV file

Started by Dewey, April 05, 2016, 04:13:12 PM

Previous topic - Next topic

Dewey

Hi,

I have some mkv files which have an AC3 encoding where I'd like to add a second audio track for playing on my iPad (AC3 not supported). I've managed to achieve this successfully via the GUI (Audio->Select Track->Enable Track 2), where I can then select a pre-saved version of the existing audio file for use in Track 2 as AVIdemux doesn't allow me to create 2 tracks from the single source.

I'd like to automate this with a script as I've quite a few files to get through, but when I try it with a script it throws the attached error :( Any ideas what is going wrong??

I'm running, Ver 2.6.12 64bit on win10

I'm calling it via the following batch cmd;
set avidemux="C:\Program Files\Avidemux\avidemux.exe"
for %%f in (*.mkv) do %avidemux% --load "%%f" --save-raw-audio "E:\iPadConvCache\tmp.wav" --run Test2.js --save "Output\%%f" --quit


I used the GUI to get my base project script up and then trimmed it down to this;
adm = Avidemux()
adm.videoCodec("Copy")
adm.audioClearTracks()
adm.audioAddExternal("E:\iPadConvCache\tmp.wav");
adm.audioAddTrack(0)
adm.audioCodec(0, "copy");
adm.audioAddTrack(1)
adm.audioCodec(1, "Lame", "bitrate=192", "preset=0", "quality=1", "disableBitReservoir=False");
adm.audioSetMixer(1, "STEREO");
adm.setContainer("MKV")


If I only add one audio track in my script it works... which makes me think there's potentially something wrong with my syntax. Its difficult to find information on this, especially whether 'adm.audioAddExternal' is been applied correctly as it doesn't seem to be specifying which track I want to assign it to like the other audio functions have.

Would appreciate anyone's insight here, or other ways of achieving my goal :)

Cheers,
D

mean

It's all about index
There are N tracks coming from the original video (from 0 to N-1)
Each time you use adm.audioAddExternal(), it gets the next index

So if you have only one track and do
adm.audioClearTracks()
adm.audioAddExternal("/work/samples/2mn.mp3")  # index 1
adm.audioAddExternal("/work/samples/2mn.ac3")  # index 2

and then you map them
adm.audioAddTrack(0) <- track 0 from original file becomes track 0
adm.audioAddTrack(2) <- track 2 (2mn.ac3) becomes track 1
adm.audioAddTrack(1) <- track 1 (2mn.mp3) becomes track 2

There seems to be a bug in the py script generation code when multiple external tracks are present




Jan Gruuthuse

or make it a 2 step script, each step using its own project.py (no re-encoding, only copy video/audio)
1st step load source video add track 2 -> save new video
2nd step load new saved video from step1, replace track 1 -> save video.

Dewey

ok, thanks @mean. I think I understand how the indexing works now.

However... I'm still getting the same error :(

Simplifying the code a little based on what @Jan has suggested I have amended my script to just try and tackle the first step; ie add a second audio track;

adm = Avidemux()
adm.videoCodec("Copy")
adm.audioClearTracks()
adm.audioAddExternal("E:\iPadConvCache\tmp.wav");
adm.audioAddTrack(1)
adm.setContainer("MKV")


So based on my understanding this should add the audio to index identifier 1 and then I can map this to track 1 (2nd track). The first, original, track will remain at index 0. [not sure what audioClearTracks does and/or if I need to specify anything with regards to track 0]

Appreciate the help :)


Jan Gruuthuse

I guess you need to keep information of present track.
- track (0) would be existing/present audiotrack
- track (1) loading wav file, converting to mp3
adm = Avidemux()
adm.videoCodec("Copy")
adm.audioClearTracks()
adm.setSourceTrackLanguage(0,"eng")
adm.audioAddExternal("E:\iPadConvCache\tmp.wav")
adm.setSourceTrackLanguage(1,"deu")
adm.audioAddTrack(0)
adm.audioCodec(0, "copy");
adm.audioSetDrc(0, 0)
adm.audioSetShift(0, 0,0)
adm.audioAddTrack(1)
adm.audioCodec(1, "Lame", "bitrate=128", "preset=0", "quality=1", "disableBitReservoir=False");
adm.audioSetDrc(1, 0)
adm.audioSetShift(1, 0,0)
adm.setContainer("MKV", "forceDisplayWidth=False", "displayWidth=1280")

Jan Gruuthuse

Or you could
- load wav as 1st track and convert to mp3 [= track(0)]
- take existing audio track in video to 2nd track [= track(1)]

adm = Avidemux()
adm.videoCodec("Copy")
adm.audioClearTracks()
adm.setSourceTrackLanguage(0,"eng")
adm.audioAddExternal("E:\iPadConvCache\tmp.wav")
adm.setSourceTrackLanguage(1,"eng")
adm.audioAddTrack(1)
adm.audioCodec(0, "Lame", "bitrate=192", "preset=0", "quality=1", "disableBitReservoir=False");
adm.audioSetDrc(0, 0)
adm.audioSetShift(0, 0,0)
adm.audioAddTrack(0)
adm.audioCodec(1, "copy");
adm.audioSetDrc(1, 0)
adm.audioSetShift(1, 0,0)
adm.setContainer("MKV", "forceDisplayWidth=False", "displayWidth=1280")


I guess, adm.audioClearTracks() is clearing previous/remaining from previous operations

mean

#6
 you have source index : 0--N from the source video, n+1,,... from external audio tracks
and the target index, in the output file

Clear empties the target index

So if you do

adm = Avidemux()
adm.audioAddExternal("E:\iPadConvCache\tmp.wav");

  you have source index [0]  =  from video
  you have source index [1]  = from  tmp.wav

adm.audioClearTracks()
Target index empty


adm.audioAddTrack(1)

Add source index 1, that becomes target index 0 => only one track, tmp.wav

Dewey

Hi Jan, unfortunately neither works - I get the same error :(

Taking a step back, and remembering that I'm using win64, what is the convention for a script file as I see on-line this varies;

  • Should the script file have an extension?, ie '.js' (which is what I'm using), as I read one page that said AVIdemux wouldn't recognise your script if it had an extension.
  • What is the correct header information to begin the script file? I have seen online versions that state #PY and //AD...

         
  • Using #PY; I get the error msg I first posted, followed by a runtime error of the .exe and the program crashing.
  • Using //AD; I get this new attached error, which after clicking ok it continues to save the output mkv file but doesn't appear to have made the requested changes

Very confusing...  :o

If I can get this coding convention right first it'll probably help remove spurious issues.

Cheers,
D

Dewey

Thanks @mean. I think I understand how it works. I've just tried what you suggested but end up with the same error... I'm starting to think there is a basic setup issue that is clouding my ability to test what you guys are suggesting - See post above ^^

Jan Gruuthuse

#9
do what you have to do in Avidemux GUI, load video, change audio tracks, ...

Once done, in Avidemux Menu: File
you should find some Project items
Save as in each found project item.
Close video and load the Saved Project in corresponding found project item.
It would give you an indication on what works and what isn't working.

Problematic issues could be (none standard Eglish OS):
- names (file & path) with spaces or non standard us ascii character in it.
- don't apply/call adm.audioClearTracks() second time in script on loaded video (would erase previously made settings)
- user owner access rights to certain locations
...

This would also give you an idea of used syntax in the working script (Project)

Dewey

nothing is non-standard, so I don't think its that...

however, upon recreating the script via the GUI as you suggested I was trying different combinations and decided to leave the video loading code line, adm.loadVideo("blah blah.mkv"), in the script and Bingo! Everything worked perfectly :)

So... I'm guessing this line of code needs to be in there to define the file. The question is, how do I parse it in as a variable given I'm launching from a batch file that cycles through the entire directory of files.

This is my batch file code;
set avidemux="C:\Program Files\Avidemux\avidemux.exe"
for %%f in (*.mkv) do %avidemux% --load "%%f" --save-raw-audio "E:\iPadConvCache\tmp.wav" --run Test2.js --save "Output\%%f" --quit


Not sure why I'm seeing this behavior but happy that we've almost found the solution  :)

Thanks,
D

mean


Dewey

Any idea on the syntax to save the audio file? If I loop through the files in the script I'll need to extract the audio in there too (currently the batch file is extracting cmd is doing it).

If it's not possible I guess I can set it up in 2 passes, first extract audios via the batch cmd then run the script.

mean

adm.saveAudio(tracknumber, outputfile)

i.e.

adm.saveAudio(0,"foo.mp3")

Dewey

thanks @mean. I tried this (adm.saveAudio) but for some reason it wouldn't let me save and use in the script, would just drop out - I guess for the same reason it won't let me do it in the GUI... forcing me to do it in 2 steps, which is ok.

Back to the main issue, I've taken a look at the code you posted on github, modified it for my needs but its given the same error message I posted originally. Interestingly, if I add adm.loadVideo("blah blah.mkv") to the #MAIN code section where everything is specified, again it ignores what I'm passing to it (in the for loop) and it works! This time however, thanks to the fact that your code does some printing to the log file I think I've found the problem, the specification of the file location is having issues with the forward/back slashes - I remember reading somewhere issues with this across platforms.

[Script] Tinypy INFO - E:/iPadConvCache/\Test.mkv=>E:/iPadConvCache/\Test.mkv.mkv

Any ideas on how I can correct this? I've tried using the replace function but it throws an error, not sure if .replace is part of an additional library that I need to load first.
     filein = filein.replace("/\","/");

I've checked the log (for a successful run and erroneous) and because the file string is wrong (in this current case and most likely in the original batch loading version I was playing with) it hasn't successfully loaded the video and thus when you load the external audio it gets an Index of 0 (rather than 1, in success state) and then throws a 'Pool index is out of bound error' because its got no file (video/audio streams) to apply it to - this is my theory anyway ;)

New Code;
#PY  <- Needed to identify #
#--automatically built--

ext="mkv";
#
def convert(filein):
    filein = filein.replace("/\","/");
fileout=filein+".mkv"
    print(filein+"=>"+fileout)
    if(0 == adm.loadVideo(filein)):
        ui.displayError("oops","cannot load "+filein)
        raise
       
    adm.save(fileout)
    print("Done")

# Main
adm = Avidemux()
adm.videoCodec("Copy")
adm.audioClearTracks()
adm.audioAddExternal("E:/iPadConvCache/tmp.wav")
adm.audioAddTrack(0)
adm.audioCodec(0, "copy");
adm.audioSetDrc(0, 0)
adm.audioSetShift(0, 0,0)
adm.audioAddTrack(1)
adm.audioCodec(1, "Lame", "bitrate=192", "preset=0", "quality=1", "disableBitReservoir=False");
adm.audioSetMixer(1, "STEREO");
adm.audioSetDrc(1, 0)
adm.audioSetShift(1, 0,0)
adm.setContainer("MKV", "forceDisplayWidth=False", "displayWidth=1280")

#
folder="E:/iPadConvCache/"
list=get_folder_content(folder,ext)
for i in list:
convert(i)
print("Done")