FranceBB
6th December 2022, 23:18
Hi fellas,
it's been a while but I finally found the time to make a guide about DCP creation via Avisynth and to debunk some of the myths around it.
We're gonna have three main cases which we're gonna deal with today:
- Case 1 - BT709 SDR
- Case 2 - BT2020 HDR HLG
- Case 3 - BT2020 HDR PQ
Now, before we jump into any of this, let's start by saying that regardless of your source, a DCP will always always always be one with the very same specifications: DCI P3 D63 gamma 2.6 in XYZ, which means that no matter what you're gonna get as an input, you're always gonna have to convert and of course this is gonna be a piece of cake for Avisynth.
Let's start with the basics:
a DCP is made of the following components:
- ASSETMAP.xml
- CPL.xml
- PKL.xml
- VOLINDEX.xml
- Audio.mxf
- Video.mxf
to create those we're gonna use Avisynth, FFMpeg and OpenDCP.
The idea is therefore to:
- Use Avisynth to handle DCI P3 D63 γ 2.6 XYZ conversion
- FFMpeg to encode the uncompressed XYZ 16bit stream in tiff lossless
- open JPEG encoder to encode the XYZ 16bit tiff into XYZ 12bit .j2k
- open mxf to append and mux the individual .j2k as .mxf
The audio will be the same:
- Avisynth for indexing
- FFMpeg to save each channel as discrete mono PCM 24bit 48'000Hz
- open mxf to interleave the mono PCM and mux the final audio file in .mxf
When everything is done, we'll then use open xml to create the various .xml files that make up the DCP.
Now, in a DCP you can only have 2 resolutions and those are Flat and Scope for either 2K or 4K:
2K:
Flat (1998 × 1080) = 1.85 LB
Scope (2048 × 858) = 2.39 LB
4K:
Flat (3996 × 2160) = 1.85 LB
Scope (4096 × 1716) = 2.39 LB
which means that the very first thing you have to do when you write your Avisynth Script is to resize while keeping the Aspect Ratio.
The second thing you'll need to address is the frame rate.
Historically, only 24p and 25p were supported, however nowadays 30p, 48p, 50p and 60p are officially supported. Keep in mind, though, that NOT all cinemas will accept "high frame rate" (i.e anything above 25p) 'cause perhaps they haven't upgraded yet, so... always check with them.
Anyway, in a nutshell, the following framerates are officially supported as per specs:
- 24p
- 25p
- 30p
- 48p
- 50p
- 60p
Let's start with the BT.709 SDR use case.
### BT709 SDR ###
If you're dealing with BT709 SDR files, you're in luck 'cause it's gonna be the open jpeg encoder that will handle everything for you, which makes it the easiest thing to work with in general. All you need to do in Avisynth is make sure that the Aspect Ratio and framerate are correct and to deliver a 16bit RGB Full Range output stream 0-65280.
AVS Script:
#Indexing
video=LWLibavVideoSource("V:\01_INGEST_MAM\MMA\Test_BT709_SDR.mxf", fpsnum=25000, fpsden=1000)
FL=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\01.Test.wav")
FR=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\02.Test.wav")
CC=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\03.Test.wav")
LFE=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\04.Test.wav")
LS=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\05.Test.wav")
RS=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\06.Test.wav")
audio=MergeChannels(FL, FR, CC, LFE, LS, RS)
AudioDub(video, audio)
#Removing original black bars
Crop(0, 44, -0, -44)
#Resize to 4K Flat 1.85 LB
Spline64ResizeMT(3996, 2160)
#Converting to RGB Planar 16bit Full Range
ConvertBits(16)
ConverttoPlanarRGB(matrix="PC.709")
We can encode it with the following command:
ffmpeg.exe -i "AVS Script.avs" -an -r 25 -pix_fmt rgb48 -vcodec tiff -y "tiff\%%06d.tiff"
pause
this will create a series of RGB48 BT709 SDR full range .tiff into the a folder called "tiff".
At this point, we're gonna encode them into JPEG2000 and the encoder will handle the conversion to DCI P3 XYZ and then dither down to 12bit planar and encode.
opendcp_j2k.exe -i "tiff" -o "j2k" -r 25 -b 250M -p cinema4k -t 1
pause
this will take the tiff frames and encode them all in 250 Mbit/s JPEG2000 Intra with the 4K profile (set the -t parameter to the maximum number of threads in your CPU).
Please note that you can use any bitrate from 1 Mbit/s to 250 Mbit/s, however for 2K you shouldn't exceed 125 Mbit/s, while with 4K you can go up to 250 Mbit/s.
Will something happen if you encode a 2K at 250 Mbit/s?
Well, the answer is: "it depends".
If the cinema you're sending it to supports 4K, then it will almost certainly be able to play it anyway, however if the cinema you're sending it to doesn't support 4K, chances are that it won't play it (or even worse it will fill the buffer over time and it will stutter).
In a nutshell, if you're encoding a 2K, don't risk it and don't exceed 125 Mbit/s, while if you're encoding a 4K, feel free to go all the way up to 250 Mbit/s if you feel like the movie needs it.
Keep in mind that the MJPEG2000 12bit encode will take... a lot 'cause MJPEG2000 uses the Wavelet Transform which is a Trasform that doesn't divide the frames in blocks and macroblocks of 4x4 and 8x8 etc, but rather encodes the whole frame as one. This, of course, prevents blocking and other Discrete Cosine Transform specific artifacts even at lower bitrates, however it will be hugely expensive in terms of encoding (and decoding on playback if you're not using hardware decoding).
Anyway, remember that we're encoding with an Intra profile, which means that all frames are encoded as individual images and there's no motion compensation whatsoever, so you'll have to "append" all those images in a real MJPEG2000 Intra Class stream and mux that into .mxf and to do this you can use:
"opendcp_mxf.exe" -i "j2k" -o "Video.mxf" -r 25
pause
again, always remember to specify the framerate with -r (in this case 25p).
At this point, you'll have a Video.mxf and all is left to do is to handle the audio.
Hopefully, at this point you have already indexed the audio in Avisynth. The audio can be 2.0, 5.1 or 7.1.
If it's stereo 2.0, you'll have only:
- Left Full Mix
- Right Full Mix
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
otherwise if it's a 5.1 you'll have:
- FL
- FR
- CC
- LFE
- LS
- RS
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.2:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\03.center.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.3:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\04.lfe.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.4:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\05.leftsur.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.5:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\06.rightsur.wav"
pause
The split comes from the fact that in Avisynth you'll always have 1 stream with all the channels in it, however to mux into mxf you'll need to split and then perform the interleave separately.
Once you have all the individual channels, it's time to mux into MXF:
opendcp_mxf.exe -i "audiosplit" -o "Audio.mxf" -r 25
pause
again, always always always remember to specify the framerate with -r (in this case 25).
At this point, you have the video, you have the audio, all is left to create is the xml:
"opendcp_xml.exe" --title "TestA" --issuer "TestB" --kind feature --reel "video.mxf" "Audio.mxf"
pause
and please replace "TestA" with the name of the movie and "TestB" with the name of the company you work for (in my case, it would be Sky Plc).
And... congratulations, you're done.
If you want to playback the DCP you can use DCP O Matic (free and open source) and use the player to playback the content (please remember to enable OpenGL acceleration in preferences).
### BT2020 HDR HLG ###
For HLG contents, the conversion is still fairly straightforward, the only thing you have to keep in mind is that your result MUST be in DCI P3 XYZ with 2.6 gamma before you pass it to the OpenJPEG encoder, but you're in luck 'cause you can use my LUTs to perform the conversion (https://forum.doom9.org/showthread.php?t=176091).
Here's a 3840x2160 HDR HLG movie in XAVC Intra Class 300 which had 2.35 LB borders for which I performed the conversion:
#Indexing
video=LWLibavVideoSource("H.264 UHD HDR HLG BT2020 YUV422 25p 10bit.mxf", fpsnum=25000, fpsden=1000)
audio=LWLibavAudioSource("H.264 UHD HDR HLG BT2020 YUV422 25p 10bit.mxf", stream_index=1)
AudioDub(video, audio)
#Removing black bars
Crop(0, 278, -0, -278)
#Going to Cinema 4K Scope 2.39 LB
Spline64ResizeMT(4096, 1716)
#Going to RGB Full Range with 16bit precision
z_ConvertFormat(pixel_type="RGBP16", colorspace_op="2020:std-b67:2020:limited=>rgb:std-b67:2020:full", resample_filter_uv="spline64", dither_type="error_diffusion")
#From BT2020 HLG to DCI P3 D63 gamma 2.6 XYZ
Cube("A:\MEDIA\temp\BT2020_HLG_to_DCI_XYZ.cube", fullrange=true)
https://i.imgur.com/rW9gvoJ.png
at this point it's gonna be:
ffmpeg.exe -i "AVS Script.avs" -an -r 25 -pix_fmt rgb48 -vcodec tiff -y "tiff\%%06d.tiff"
opendcp_j2k.exe -i "tiff" -o "j2k" -r 25 -b 250M -p cinema4k -t 1 --no_xyz --colorspace p3 --dpx linear
"opendcp_mxf.exe" -i "j2k" -o "Video.mxf" -r 25
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.2:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\03.center.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.3:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\04.lfe.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.4:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\05.leftsur.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.5:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\06.rightsur.wav"
opendcp_mxf.exe -i "audiosplit" -o "Audio.mxf" -r 25
"opendcp_xml.exe" --title "TestA" --issuer "TestB" --kind feature --reel "video.mxf" "Audio.mxf"
pause
and as always, please rename TestA and TestB with the name of the movie and the name of the company you work for, respectively.
Original source in BT2020 HLG HDR YUV 10bit (displayed as is without interpreting the colour curve and matrix):
https://i.imgur.com/gLffohq.png
DCI P3 D63 XYZ 16bit output from Avisynth (displayed as is without interpreting the colour curve, matrix and colorspace):
https://i.imgur.com/EY7ASPa.png
DCI P3 D63 XYZ 12bit MJPEG2000 Intra Class (displayed as is without interpreting the colour curve, matrix, colorspace and density of each colour channel):
https://i.imgur.com/yzkKC2O.png
Now, since everything above is "cool" but makes little sense in terms of comparison, let's interpret those values with a proper player, shall we?
DCI P3 D63 XYZ 12bit (correctly displayed):
https://i.imgur.com/KWm4t6l.png
### BT2020 HDR PQ ###
With PQ, technically, things get a bit more complicated. This is because in theory with PQ you need to know the MaxCLL (i.e the nits) to perform the conversion and you should have 1 LUT per nit for a total of 10'000 LUTs. Practically, this isn't doable and I made one single LUT for now, however I'm planning to create a LUT every 1000 nits, so one for 1000, one for 2000, one for 3000 and so on.
Anyway, not to waste any time, here's an HDR PQ movie which I had to crop, resize and convert from 23,976 to 24p before encoding:
#Indexing
video=LWLibavVideoSource("H.265 UHD HDR PQ BT2020 YUV422 23,976p 10bit.mxf", fpsnum=24000, fpsden=1001)
audio=LWLibavAudioSource("H.265 UHD HDR PQ BT2020 YUV422 23,976p 10bit.mxf")
AudioDub(video, audio)
#Cropping to get to 4K Flat 1.85 LB
Crop(0, 44, -0, -44)
Spline64ResizeMT(3996, 2160)
#Going to RGB Full Range with 16bit precision
z_ConvertFormat(pixel_type="RGBP16", colorspace_op="2020:st2084:2020:limited=>rgb:st2084:2020:full", resample_filter_uv="spline64", dither_type="error_diffusion")
#Going from BT2020 HDR PQ to DCI P3 D63 gamma 2.6 XYZ
Cube("A:\MEDIA\temp\BT2020_PQ_to_DCI_XYZ.cube", fullrange=true)
#Speed up from 23,976 to 24p
ResampleAudio(48000)
AssumeFPS(24, 1, true)
ResampleAudio(48000)
https://i.imgur.com/UFnVxN5.png
At this point, it's all the same, but of course this time with -r 24 as it's 24p:
ffmpeg.exe -i "AVS Script.avs" -an -r 24 -pix_fmt rgb48 -vcodec tiff -y "tiff\%%06d.tiff"
opendcp_j2k.exe -i "tiff" -o "j2k" -r 24 -b 250M -p cinema4k -t 1 --no_xyz --colorspace p3 --dpx linear
"opendcp_mxf.exe" -i "j2k" -o "Video.mxf" -r 24
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.2:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\03.center.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.3:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\04.lfe.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.4:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\05.leftsur.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.5:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\06.rightsur.wav"
opendcp_mxf.exe -i "audiosplit" -o "Audio.mxf" -r 24
"opendcp_xml.exe" --title "TestA" --issuer "TestB" --kind feature --reel "video.mxf" "Audio.mxf"
pause
and again, as always, please rename TestA and TestB with the name of the movie and the name of the company you work for, respectively.
Original source in BT2020 PQ HDR YUV 10bit (displayed as is without interpreting the colour curve and matrix):
https://i.imgur.com/RVp8K0f.png
DCI P3 D63 XYZ 16bit output from Avisynth (displayed as is without interpreting the colour curve, matrix and colorspace):
https://i.imgur.com/FexDHAa.png
DCI P3 D63 XYZ 12bit MJPEG2000 Intra Class (displayed as is without interpreting the colour curve, matrix, colorspace and density of each colour channel):
https://i.imgur.com/HDoAa7T.png
Now, since everything above is "cool" but makes little sense in terms of comparison, let's interpret those values with a proper player, shall we?
DCI P3 D63 XYZ 12bit (correctly displayed):
https://i.imgur.com/0ClGKHA.png
Download Links:
- FranceBB LUT Collection (https://forum.doom9.org/showthread.php?t=176091)
- FranceBB MJPEG2000 Package with executables, BATs and AVS Scripts (https://mega.nz/file/jMUUhBhZ#p8FJ1VP9m89-0ZLcJM757kp6D9JtTf-onFAb73UWEpI) - Mirror (Backup Only) (https://drive.google.com/file/d/1PBfnKEslOHMEvbhPxJY4lkBgLprzBmVi/view?usp=share_link)
- OpenDCP GitHub release (https://github.com/FranceBB/OpenDCP)
- DCP O Matic GitHub (https://github.com/cth103/dcpomatic) - Builds (https://dcpomatic.com/download)
it's been a while but I finally found the time to make a guide about DCP creation via Avisynth and to debunk some of the myths around it.
We're gonna have three main cases which we're gonna deal with today:
- Case 1 - BT709 SDR
- Case 2 - BT2020 HDR HLG
- Case 3 - BT2020 HDR PQ
Now, before we jump into any of this, let's start by saying that regardless of your source, a DCP will always always always be one with the very same specifications: DCI P3 D63 gamma 2.6 in XYZ, which means that no matter what you're gonna get as an input, you're always gonna have to convert and of course this is gonna be a piece of cake for Avisynth.
Let's start with the basics:
a DCP is made of the following components:
- ASSETMAP.xml
- CPL.xml
- PKL.xml
- VOLINDEX.xml
- Audio.mxf
- Video.mxf
to create those we're gonna use Avisynth, FFMpeg and OpenDCP.
The idea is therefore to:
- Use Avisynth to handle DCI P3 D63 γ 2.6 XYZ conversion
- FFMpeg to encode the uncompressed XYZ 16bit stream in tiff lossless
- open JPEG encoder to encode the XYZ 16bit tiff into XYZ 12bit .j2k
- open mxf to append and mux the individual .j2k as .mxf
The audio will be the same:
- Avisynth for indexing
- FFMpeg to save each channel as discrete mono PCM 24bit 48'000Hz
- open mxf to interleave the mono PCM and mux the final audio file in .mxf
When everything is done, we'll then use open xml to create the various .xml files that make up the DCP.
Now, in a DCP you can only have 2 resolutions and those are Flat and Scope for either 2K or 4K:
2K:
Flat (1998 × 1080) = 1.85 LB
Scope (2048 × 858) = 2.39 LB
4K:
Flat (3996 × 2160) = 1.85 LB
Scope (4096 × 1716) = 2.39 LB
which means that the very first thing you have to do when you write your Avisynth Script is to resize while keeping the Aspect Ratio.
The second thing you'll need to address is the frame rate.
Historically, only 24p and 25p were supported, however nowadays 30p, 48p, 50p and 60p are officially supported. Keep in mind, though, that NOT all cinemas will accept "high frame rate" (i.e anything above 25p) 'cause perhaps they haven't upgraded yet, so... always check with them.
Anyway, in a nutshell, the following framerates are officially supported as per specs:
- 24p
- 25p
- 30p
- 48p
- 50p
- 60p
Let's start with the BT.709 SDR use case.
### BT709 SDR ###
If you're dealing with BT709 SDR files, you're in luck 'cause it's gonna be the open jpeg encoder that will handle everything for you, which makes it the easiest thing to work with in general. All you need to do in Avisynth is make sure that the Aspect Ratio and framerate are correct and to deliver a 16bit RGB Full Range output stream 0-65280.
AVS Script:
#Indexing
video=LWLibavVideoSource("V:\01_INGEST_MAM\MMA\Test_BT709_SDR.mxf", fpsnum=25000, fpsden=1000)
FL=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\01.Test.wav")
FR=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\02.Test.wav")
CC=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\03.Test.wav")
LFE=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\04.Test.wav")
LS=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\05.Test.wav")
RS=WAVSource("V:\01_INGEST_MAM\MMA\audio\Italian\06.Test.wav")
audio=MergeChannels(FL, FR, CC, LFE, LS, RS)
AudioDub(video, audio)
#Removing original black bars
Crop(0, 44, -0, -44)
#Resize to 4K Flat 1.85 LB
Spline64ResizeMT(3996, 2160)
#Converting to RGB Planar 16bit Full Range
ConvertBits(16)
ConverttoPlanarRGB(matrix="PC.709")
We can encode it with the following command:
ffmpeg.exe -i "AVS Script.avs" -an -r 25 -pix_fmt rgb48 -vcodec tiff -y "tiff\%%06d.tiff"
pause
this will create a series of RGB48 BT709 SDR full range .tiff into the a folder called "tiff".
At this point, we're gonna encode them into JPEG2000 and the encoder will handle the conversion to DCI P3 XYZ and then dither down to 12bit planar and encode.
opendcp_j2k.exe -i "tiff" -o "j2k" -r 25 -b 250M -p cinema4k -t 1
pause
this will take the tiff frames and encode them all in 250 Mbit/s JPEG2000 Intra with the 4K profile (set the -t parameter to the maximum number of threads in your CPU).
Please note that you can use any bitrate from 1 Mbit/s to 250 Mbit/s, however for 2K you shouldn't exceed 125 Mbit/s, while with 4K you can go up to 250 Mbit/s.
Will something happen if you encode a 2K at 250 Mbit/s?
Well, the answer is: "it depends".
If the cinema you're sending it to supports 4K, then it will almost certainly be able to play it anyway, however if the cinema you're sending it to doesn't support 4K, chances are that it won't play it (or even worse it will fill the buffer over time and it will stutter).
In a nutshell, if you're encoding a 2K, don't risk it and don't exceed 125 Mbit/s, while if you're encoding a 4K, feel free to go all the way up to 250 Mbit/s if you feel like the movie needs it.
Keep in mind that the MJPEG2000 12bit encode will take... a lot 'cause MJPEG2000 uses the Wavelet Transform which is a Trasform that doesn't divide the frames in blocks and macroblocks of 4x4 and 8x8 etc, but rather encodes the whole frame as one. This, of course, prevents blocking and other Discrete Cosine Transform specific artifacts even at lower bitrates, however it will be hugely expensive in terms of encoding (and decoding on playback if you're not using hardware decoding).
Anyway, remember that we're encoding with an Intra profile, which means that all frames are encoded as individual images and there's no motion compensation whatsoever, so you'll have to "append" all those images in a real MJPEG2000 Intra Class stream and mux that into .mxf and to do this you can use:
"opendcp_mxf.exe" -i "j2k" -o "Video.mxf" -r 25
pause
again, always remember to specify the framerate with -r (in this case 25p).
At this point, you'll have a Video.mxf and all is left to do is to handle the audio.
Hopefully, at this point you have already indexed the audio in Avisynth. The audio can be 2.0, 5.1 or 7.1.
If it's stereo 2.0, you'll have only:
- Left Full Mix
- Right Full Mix
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
otherwise if it's a 5.1 you'll have:
- FL
- FR
- CC
- LFE
- LS
- RS
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.2:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\03.center.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.3:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\04.lfe.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.4:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\05.leftsur.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.5:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\06.rightsur.wav"
pause
The split comes from the fact that in Avisynth you'll always have 1 stream with all the channels in it, however to mux into mxf you'll need to split and then perform the interleave separately.
Once you have all the individual channels, it's time to mux into MXF:
opendcp_mxf.exe -i "audiosplit" -o "Audio.mxf" -r 25
pause
again, always always always remember to specify the framerate with -r (in this case 25).
At this point, you have the video, you have the audio, all is left to create is the xml:
"opendcp_xml.exe" --title "TestA" --issuer "TestB" --kind feature --reel "video.mxf" "Audio.mxf"
pause
and please replace "TestA" with the name of the movie and "TestB" with the name of the company you work for (in my case, it would be Sky Plc).
And... congratulations, you're done.
If you want to playback the DCP you can use DCP O Matic (free and open source) and use the player to playback the content (please remember to enable OpenGL acceleration in preferences).
### BT2020 HDR HLG ###
For HLG contents, the conversion is still fairly straightforward, the only thing you have to keep in mind is that your result MUST be in DCI P3 XYZ with 2.6 gamma before you pass it to the OpenJPEG encoder, but you're in luck 'cause you can use my LUTs to perform the conversion (https://forum.doom9.org/showthread.php?t=176091).
Here's a 3840x2160 HDR HLG movie in XAVC Intra Class 300 which had 2.35 LB borders for which I performed the conversion:
#Indexing
video=LWLibavVideoSource("H.264 UHD HDR HLG BT2020 YUV422 25p 10bit.mxf", fpsnum=25000, fpsden=1000)
audio=LWLibavAudioSource("H.264 UHD HDR HLG BT2020 YUV422 25p 10bit.mxf", stream_index=1)
AudioDub(video, audio)
#Removing black bars
Crop(0, 278, -0, -278)
#Going to Cinema 4K Scope 2.39 LB
Spline64ResizeMT(4096, 1716)
#Going to RGB Full Range with 16bit precision
z_ConvertFormat(pixel_type="RGBP16", colorspace_op="2020:std-b67:2020:limited=>rgb:std-b67:2020:full", resample_filter_uv="spline64", dither_type="error_diffusion")
#From BT2020 HLG to DCI P3 D63 gamma 2.6 XYZ
Cube("A:\MEDIA\temp\BT2020_HLG_to_DCI_XYZ.cube", fullrange=true)
https://i.imgur.com/rW9gvoJ.png
at this point it's gonna be:
ffmpeg.exe -i "AVS Script.avs" -an -r 25 -pix_fmt rgb48 -vcodec tiff -y "tiff\%%06d.tiff"
opendcp_j2k.exe -i "tiff" -o "j2k" -r 25 -b 250M -p cinema4k -t 1 --no_xyz --colorspace p3 --dpx linear
"opendcp_mxf.exe" -i "j2k" -o "Video.mxf" -r 25
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.2:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\03.center.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.3:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\04.lfe.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.4:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\05.leftsur.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.5:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\06.rightsur.wav"
opendcp_mxf.exe -i "audiosplit" -o "Audio.mxf" -r 25
"opendcp_xml.exe" --title "TestA" --issuer "TestB" --kind feature --reel "video.mxf" "Audio.mxf"
pause
and as always, please rename TestA and TestB with the name of the movie and the name of the company you work for, respectively.
Original source in BT2020 HLG HDR YUV 10bit (displayed as is without interpreting the colour curve and matrix):
https://i.imgur.com/gLffohq.png
DCI P3 D63 XYZ 16bit output from Avisynth (displayed as is without interpreting the colour curve, matrix and colorspace):
https://i.imgur.com/EY7ASPa.png
DCI P3 D63 XYZ 12bit MJPEG2000 Intra Class (displayed as is without interpreting the colour curve, matrix, colorspace and density of each colour channel):
https://i.imgur.com/yzkKC2O.png
Now, since everything above is "cool" but makes little sense in terms of comparison, let's interpret those values with a proper player, shall we?
DCI P3 D63 XYZ 12bit (correctly displayed):
https://i.imgur.com/KWm4t6l.png
### BT2020 HDR PQ ###
With PQ, technically, things get a bit more complicated. This is because in theory with PQ you need to know the MaxCLL (i.e the nits) to perform the conversion and you should have 1 LUT per nit for a total of 10'000 LUTs. Practically, this isn't doable and I made one single LUT for now, however I'm planning to create a LUT every 1000 nits, so one for 1000, one for 2000, one for 3000 and so on.
Anyway, not to waste any time, here's an HDR PQ movie which I had to crop, resize and convert from 23,976 to 24p before encoding:
#Indexing
video=LWLibavVideoSource("H.265 UHD HDR PQ BT2020 YUV422 23,976p 10bit.mxf", fpsnum=24000, fpsden=1001)
audio=LWLibavAudioSource("H.265 UHD HDR PQ BT2020 YUV422 23,976p 10bit.mxf")
AudioDub(video, audio)
#Cropping to get to 4K Flat 1.85 LB
Crop(0, 44, -0, -44)
Spline64ResizeMT(3996, 2160)
#Going to RGB Full Range with 16bit precision
z_ConvertFormat(pixel_type="RGBP16", colorspace_op="2020:st2084:2020:limited=>rgb:st2084:2020:full", resample_filter_uv="spline64", dither_type="error_diffusion")
#Going from BT2020 HDR PQ to DCI P3 D63 gamma 2.6 XYZ
Cube("A:\MEDIA\temp\BT2020_PQ_to_DCI_XYZ.cube", fullrange=true)
#Speed up from 23,976 to 24p
ResampleAudio(48000)
AssumeFPS(24, 1, true)
ResampleAudio(48000)
https://i.imgur.com/UFnVxN5.png
At this point, it's all the same, but of course this time with -r 24 as it's 24p:
ffmpeg.exe -i "AVS Script.avs" -an -r 24 -pix_fmt rgb48 -vcodec tiff -y "tiff\%%06d.tiff"
opendcp_j2k.exe -i "tiff" -o "j2k" -r 24 -b 250M -p cinema4k -t 1 --no_xyz --colorspace p3 --dpx linear
"opendcp_mxf.exe" -i "j2k" -o "Video.mxf" -r 24
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.0:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\01.left.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.1:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\02.right.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.2:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\03.center.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.3:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\04.lfe.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.4:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\05.leftsur.wav"
"ffmpeg.exe" -i "AVS Script.avs" -map 0:a:0 -map_channel 0.1.5:0.0.0 -acodec pcm_s24le -ar 48000 -y "audiosplit\06.rightsur.wav"
opendcp_mxf.exe -i "audiosplit" -o "Audio.mxf" -r 24
"opendcp_xml.exe" --title "TestA" --issuer "TestB" --kind feature --reel "video.mxf" "Audio.mxf"
pause
and again, as always, please rename TestA and TestB with the name of the movie and the name of the company you work for, respectively.
Original source in BT2020 PQ HDR YUV 10bit (displayed as is without interpreting the colour curve and matrix):
https://i.imgur.com/RVp8K0f.png
DCI P3 D63 XYZ 16bit output from Avisynth (displayed as is without interpreting the colour curve, matrix and colorspace):
https://i.imgur.com/FexDHAa.png
DCI P3 D63 XYZ 12bit MJPEG2000 Intra Class (displayed as is without interpreting the colour curve, matrix, colorspace and density of each colour channel):
https://i.imgur.com/HDoAa7T.png
Now, since everything above is "cool" but makes little sense in terms of comparison, let's interpret those values with a proper player, shall we?
DCI P3 D63 XYZ 12bit (correctly displayed):
https://i.imgur.com/0ClGKHA.png
Download Links:
- FranceBB LUT Collection (https://forum.doom9.org/showthread.php?t=176091)
- FranceBB MJPEG2000 Package with executables, BATs and AVS Scripts (https://mega.nz/file/jMUUhBhZ#p8FJ1VP9m89-0ZLcJM757kp6D9JtTf-onFAb73UWEpI) - Mirror (Backup Only) (https://drive.google.com/file/d/1PBfnKEslOHMEvbhPxJY4lkBgLprzBmVi/view?usp=share_link)
- OpenDCP GitHub release (https://github.com/FranceBB/OpenDCP)
- DCP O Matic GitHub (https://github.com/cth103/dcpomatic) - Builds (https://dcpomatic.com/download)