I am currently working on a project and almost ready to deploy, but there is one problem: the code which is working perfectly on my Ubuntu 16.04 (Elementary OS Loki) machine does not work in Docker container. What might be the problem in my code?
Here is my Dockerfile:
FROM beevelop/nodejs-python:latest
RUN apt-get -y update && apt-get install -y libav-tools
RUN ffmpeg --help
# Global install yarn package manager
RUN apt-get update && apt-get install -y curl apt-transport-https && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn
WORKDIR /app
COPY package.json .
RUN yarn
COPY . .
RUN yarn prod:build
EXPOSE 8080
CMD [ "yarn", "start" ]
I run sudo docker exec -it <container-name> ffmpeg --help and it outputs the expected help print. In my code, I download a Youtube video in mp4 format with this code:
const download = idParam => (
new Promise((fulfill, reject) => {
const id = `http://www.youtube.com/watch?v=${idParam}`
let videoMeta
youtubedl(id, ['--format=36'], { cwd: __dirname })
.on('info', (info) => {
const customDimensions = calculateCustomDimensions(info.format)
videoMeta = { ...info, ...customDimensions }
})
.pipe(fs.createWriteStream(`temp/${idParam}.mp4`))
.on('finish', () => {
console.log('Dirname: ', __dirname)
fulfill(videoMeta)
})
.on('error', (err) => {
console.log('There is an error:', err)
console.log('Dirname: ', __dirname)
reject(err)
})
})
)
And then try to process the video with fluent-ffmpeg:
const processVideo = (videoName, cropStart, cropEnd) => (
new Promise((resolve, reject) => {
try {
console.log('Trying to run ffmpeg binary.')
ffmpeg()
.addInput(`temp/${videoName}.mp4`)
.seekInput(cropStart)
.duration(cropEnd)
.outputOption('-movflags frag_keyframe+faststart')
.on('end', () => {
console.log('Processing has finished with ffmpeg. ')
console.log('Resolved for upload: ', [`temp/cropped_${videoName}.mp4`, `temp/${videoName}.mp4`])
console.log('Going out of processVideo script.')
resolve([`temp/cropped_${videoName}.mp4`, `temp/${videoName}.mp4`])
})
.on('error', (err, stdout, stderr) => {
console.log('Error. Output option olmasına rağmen: ', err.message)
console.log('ffmpeg output:\n', stdout)
console.log('ffmpeg stderr:\n', stderr)
})
.save(`temp/cropped_${videoName}.mp4`)
} catch (e) {
console.log('There has been an error while converting ffmpeg binary!')
console.log(e.msg)
console.log(e.code)
reject(e)
}
})
)
It gives the following error:
Entering download script.
WARNING: Assuming --restrict-filenames since file system encoding cannot encode all characters. Set the LC_ALL environment variable to fix this.
`info.resolution` is deprecated, use `info.format`
Dirname: /app/lib/server/scripts
Download script is done.
Custom dimensions of the downloaded video: 320 180
Crop start and end of the downloaded video: 00:00:00.517 00:00:00.998
Entering processVideo script.
Trying to run ffmpeg binary.
Error: ffmpeg exited with code 1:
ffmpeg output:
ffmpeg stderr:
ffmpeg version 2.8.11-0ubuntu0.16.04.1 Copyright (c) 2000-2017 the FFmpeg developers
built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.4) 20160609
configuration: --prefix=/usr --extra-version=0ubuntu0.16.04.1 --build-suffix=-ffmpeg --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librtmp --enable-libschroedinger --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-libx264 --enable-libopencv
libavutil 54. 31.100 / 54. 31.100
libavcodec 56. 60.100 / 56. 60.100
libavformat 56. 40.101 / 56. 40.101
libavdevice 56. 4.100 / 56. 4.100
libavfilter 5. 40.101 / 5. 40.101
libavresample 2. 1. 0 / 2. 1. 0
libswscale 3. 1.101 / 3. 1.101
libswresample 1. 2.101 / 1. 2.101
libpostproc 53. 3.100 / 53. 3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'temp/kfchvCyHmsc.mp4':
Metadata:
major_brand : 3gp6
minor_version : 256
compatible_brands: isom3gp6
creation_time : 2015-03-26 09:22:44
Duration: 00:00:08.27, start: 0.000000, bitrate: 218 kb/s
Stream #0:0(und): Video: mpeg4 (Simple Profile) (mp4v / 0x7634706D), yuv420p, 320x180 [SAR 1:1 DAR 16:9], 187 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 24k tbc (default)
Metadata:
creation_time : 2015-03-26 09:22:44
handler_name : IsoMedia File Produced by Google, 5-11-2011
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 22050 Hz, mono, fltp, 31 kb/s (default)
Metadata:
creation_time : 2015-03-26 09:22:44
handler_name : IsoMedia File Produced by Google, 5-11-2011
[libx264 # 0x2641fc0] using SAR=1/1
[libx264 # 0x2641fc0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 AVX2 LZCNT BMI2
[libx264 # 0x2641fc0] profile High, level 1.2
[libx264 # 0x2641fc0] 264 - core 148 r2643 5c65704 - H.264/MPEG-4 AVC codec - Copyleft 2003-2015 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=23 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
[aac # 0x2642f00] The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it.
To go into more detail this excerpt from your console output/log explains the reason why it failed:
[aac # 0x2642f00] The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it.
Your ffmpeg was old, as you already found out. Using the outdated ffmpeg would have required -strict -2 (or the alias -strict experimental) to encode using the built-in FFmpeg AAC encoder, but your command was lacking this.
Eventually the AAC encoder was improved and the experimental status was removed. Using a recent version allows you to encode AAC without -strict. See the FFmpeg Download page for links to already compiled recent builds of ffmpeg for Linux, macOS, and Windows.
For more info see FFmpeg Wiki: AAC.
I have just found out that the two versions of ffmpeg on my machine and Docker was differing. Upgraded the one in Docker to 3.3.4 and solved!
Related
I have a lot of issues about audio transfer to server and frontend.
I am building a solution that is extracting and merging a file received from frontend.
I am using local ffmpeg file with node.js child_process package.
My Env:
framework: nest.js
runtime: node.js
lang: typescript
here is console.log(file: Express.Multer.File).
I am storing to S3 bucket these files. So I am not using multer's memory, disk storage.
// console.log(file);
{
fieldname: 'file',
originalname: 'input1.mp3',
encoding: '7bit',
mimetype: 'audio/mpeg'
}
import cp from "child_process";
type Section = { from: number, to: number };
const files: Array<Express.Multer.File> = [];
export const setupAudioFileSection = async (file: Express.Multer.File, section: Section) => {
try {
const file1 = await cp.exec(`ffmpeg -i ${file} -y -ss 0 -t ${section.from}`);
const file2 = await cp.exec(`ffmpeg -i ${file} -y -ss ${section.from} -t ${section.to}`);
const file3 = await cp.exec(`ffmpeg -i ${file} -y -ss ${section.to}`);
files.push(file1);
files.push(file3);
return file2;
} catch (error) {
console.log(error);
throw new Error('setupAudioFileSection error');
}
But, My Local ffmpeg is printing the error. because of [object Object]. I think local ffmpeg is not recognized the audio file received from client. So, I am resolving this issue by writing a file to disk or memory directly. but I am confusing this solution whether is right or not.
error Error: Command failed: ffmpeg -i [object Object] -y -ss 10 -t 20 -f mp3 ./TestOutput
ffmpeg version 5.1.1 Copyright (c) 2000-2022 the FFmpeg developers
built with Apple clang version 13.1.6 (clang-1316.0.21.2.5)
configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/5.1.1 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-neon
libavutil 57. 28.100 / 57. 28.100
libavcodec 59. 37.100 / 59. 37.100
libavformat 59. 27.100 / 59. 27.100
libavdevice 59. 7.100 / 59. 7.100
libavfilter 8. 44.100 / 8. 44.100
libswscale 6. 7.100 / 6. 7.100
libswresample 4. 7.100 / 4. 7.100
libpostproc 56. 6.100 / 56. 6.100
[object: No such file or directory
How can I write file on my server and read? if that can be, I can resolve [object Object] of ffmpeg issue maybe. Please some answer or advise my issue. Thank you!
I'm trying to run the following code:
const { exec } = require('child_process');
[
{
token: '1015010204030817080302030300',
},
].forEach(async ({ token }) => {
exec(
`ffmpeg -i ${token}.mp4 -vf "fps=35,scale=414:-1:flags=lanczos,palettegen" ${token}.png`,
(err, stdout, stderr) => {
if (err) {
//some err occurred
console.error(err);
} else {
// the *entire* stdout and stderr (buffered)
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
}
}
);
exec(
`ffmpeg -t 29 -i ${token}.mp4 -i ${token}.png -filter_complex "fps=35,scale=414:-1:flags=lanczos[x];[x][1:v]paletteuse" ${token}.gif`,
(err, stdout, stderr) => {
if (err) {
//some err occurred
console.error(err);
} else {
// the *entire* stdout and stderr (buffered)
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
}
}
);
});
but I get the following error: 1015010204030817080302030300.mp4: No such file or directory
here is the complete error
Error: Command failed: ffmpeg -i 1015010204030817080302030300.mp4 -vf "fps=35,scale=414:-1:flags=lanczos,palettegen" 1015010204030817080302030300.png
ffmpeg version 4.4.1 Copyright (c) 2000-2021 the FFmpeg developers
built with Apple clang version 13.0.0 (clang-1300.0.29.3)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4.1_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox
libavutil 56. 70.100 / 56. 70.100
libavcodec 58.134.100 / 58.134.100
libavformat 58. 76.100 / 58. 76.100
libavdevice 58. 13.100 / 58. 13.100
libavfilter 7.110.100 / 7.110.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 9.100 / 5. 9.100
libswresample 3. 9.100 / 3. 9.100
libpostproc 55. 9.100 / 55. 9.100
1015010204030817080302030300.mp4: No such file or directory
at ChildProcess.exithandler (child_process.js:295:12)
at ChildProcess.emit (events.js:210:5)
at maybeClose (internal/child_process.js:1021:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:283:5) {
killed: false,
code: 1,
signal: null,
cmd: 'ffmpeg -i 1015010204030817080302030300.mp4 -vf "fps=35,scale=414:-1:flags=lanczos,palettegen" 1015010204030817080302030300.png'
}
Error: Command failed: ffmpeg -t 29 -i 1015010204030817080302030300.mp4 -i 1015010204030817080302030300.png -filter_complex "fps=35,scale=414:-1:flags=lanczos[x];[x][1:v]paletteuse" 1015010204030817080302030300.gif
ffmpeg version 4.4.1 Copyright (c) 2000-2021 the FFmpeg developers
built with Apple clang version 13.0.0 (clang-1300.0.29.3)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4.1_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox
libavutil 56. 70.100 / 56. 70.100
libavcodec 58.134.100 / 58.134.100
libavformat 58. 76.100 / 58. 76.100
libavdevice 58. 13.100 / 58. 13.100
libavfilter 7.110.100 / 7.110.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 9.100 / 5. 9.100
libswresample 3. 9.100 / 3. 9.100
libpostproc 55. 9.100 / 55. 9.100
1015010204030817080302030300.mp4: No such file or directory
at ChildProcess.exithandler (child_process.js:295:12)
at ChildProcess.emit (events.js:210:5)
at maybeClose (internal/child_process.js:1021:16)
at Socket.<anonymous> (internal/child_process.js:430:11)
at Socket.emit (events.js:210:5)
at Pipe.<anonymous> (net.js:659:12) {
killed: false,
code: 1,
signal: null,
cmd: 'ffmpeg -t 29 -i 1015010204030817080302030300.mp4 -i 1015010204030817080302030300.png -filter_complex "fps=35,scale=414:-1:flags=lanczos[x];[x][1:v]paletteuse" 1015010204030817080302030300.gif'
But if I copy paste the complete comando from the error and paste on the console it execute without any errors. So I assuming that the problem is with Nodejs but I don't know what is happening I also try to créate and asyncronus exec but I get a Unhandled promise rejection. error
The "current working directory" of the Node process is important when using exec as it defaults to process.cwd()
The two commands:
$ node /path/to/my/script.js
and
$ cd /path/to/my
$ node ./script.js
Will cause exec to start in different directories which impacts the relative path being supplied to ffmpeg.
Another way to fix this is to be specific with full directory paths for ffmpeg which is what I have presented below.
Additionally, exec is an asynchronous function. The function will return before the results are available so you need to wait for the results. This is much easier with promises and await than your example codes callback API.
const path = require('path')
const util = require('util')
const exec = util.promisify(require('child_process').exec)
async function runCmd(cmd, directory){
console.log('Run command "%s" in directory "%s"', cmd, directory)
try {
const options = {}
if (directory) {
options.cwd = directory
}
const res = await exec(cmd, options)
console.debug('stdout', res.stdout)
console.debug('stderr', res.stderr)
if ( res.exitCode !== 0 ) {
const err = new Error(`Run command failed with exit code ${res.exitCode}`)
err.cmd = cmd
err.process = res
throw err
}
console.log('Ran command "%s"', cmd)
return res
}
catch (err) {
console.error(`Run failed "${cmd}"`, directory, err)
throw err
}
}
async function processToken(token) {
// `__dirname` is the directory of the current JS file. It might
// need '..' depending on your source structure or just a fully defined path
const file_prefix = path.join(__dirname, 'video', token)
// Check the file first
await fs.stat(`${file_prefix}.mp4`)
await runCmd(`ffmpeg -i "${file_prefix}.mp4" -vf "fps=35,scale=414:-1:flags=lanczos,palettegen" "${file_prefix}.png"`)
await runCmd(`ffmpeg -t 29 -i "${file_prefix}.mp4" -i "${file_prefix}.png" -filter_complex "fps=35,scale=414:-1:flags=lanczos[x];[x][1:v]paletteuse" "${file_prefix}.gif"`)
}
processToken('1015010204030817080302030300.mp4')
.catch(err => { console.error(err); process.exit(1) })
I would also recommend using an async wrapper for spawn rather then exec as it provides more control and less "shell" gotchas, but I'll leave that for now.
I am trying to convert an MP4 file to HLS using ffmpeg.
Code:
var stream = createReadStream(filePath);
ffmpeg(stream)
.on('stderr', function(stderrLine) {
console.log('Stderr output: ' + stderrLine);
})
.on('end', function() {
console.log('done processing input stream');
})
.on('error', function(err) {
console.log('an error happened: ' + err.message);
})
.save(join(__basedir, "public", `file.m3u8`));
OutPut:
Stderr output: ffmpeg version git-2020-05-22-38490cb Copyright (c) 2000-2020 the FFmpeg developers
Stderr output: built with gcc 9.3.1 (GCC) 20200513
Stderr output: configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libsrt --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --disable-w32threads --enable-libmfx --enable-ffnvcodec --enable-cuda-llvm --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt --enable-amf
Stderr output: libavutil 56. 46.100 / 56. 46.100
Stderr output: libavcodec 58. 86.101 / 58. 86.101
Stderr output: libavformat 58. 43.100 / 58. 43.100
Stderr output: libavdevice 58. 9.103 / 58. 9.103
Stderr output: libavfilter 7. 82.100 / 7. 82.100
Stderr output: libswscale 5. 6.101 / 5. 6.101
Stderr output: libswresample 3. 6.100 / 3. 6.100
Stderr output: libpostproc 55. 6.100 / 55. 6.100
Stderr output: [mov,mp4,m4a,3gp,3g2,mj2 # 000001b68c53cb00] overread end of atom 'stsd' by 34 bytes
Stderr output: [mov,mp4,m4a,3gp,3g2,mj2 # 000001b68c53cb00] stream 0, offset 0x30: partial file
Stderr output: [mov,mp4,m4a,3gp,3g2,mj2 # 000001b68c53cb00] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 1920x1080, 2528 kb/s): unspecified pixel format
Stderr output: Consider increasing the value for the 'analyzeduration' and 'probesize' options
Stderr output: Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:0':
Stderr output: Metadata:
Stderr output: major_brand : isom
Stderr output: minor_version : 512
Stderr output: compatible_brands: isomiso2avc1mp41
Stderr output: encoder : Lavf56.25.101
Stderr output: Duration: 00:03:00.97, start: 0.000000, bitrate: N/A
Stderr output: Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 1920x1080, 2528 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 48k tbc (default)
Stderr output: Metadata:
Stderr output: handler_name : VideoHandler
Stderr output: Stream #0:1(eng): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 127 kb/s (default)
Stderr output: Metadata:
Stderr output: handler_name : SoundHandler
Stderr output: Stream mapping:
Stderr output: Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stderr output: Stream #0:1 -> #0:1 (aac (native) -> aac (native))
Stderr output: [mov,mp4,m4a,3gp,3g2,mj2 # 000001b68c53cb00] stream 0, offset 0x30: partial file
Stderr output: pipe:0: Invalid data found when processing input
Stderr output: Cannot determine format of input stream 0:0 after EOF
Stderr output: Error marking filters as finished
Stderr output: Conversion failed!
Stderr output:
an error happened: ffmpeg exited with code 1: pipe:0: Invalid data found when processing input
Cannot determine format of input stream 0:0 after EOF
Error marking filters as finished
Conversion failed!
I've also tried with moveflag option
.outputOptions("-movflags isml+frag_keyframe") also with -movflags faststart
I've read through
How do you use Node.js to stream an MP4 file with ffmpeg?
But i didn't quite understand what and how to do it.
To achieve HLS from mp4 video we can use the below code.
We will pass the video as input with ffmpeg tags options.
In this example, I am converting a 480p resolution related playlist.
we can use the same code by just updating resolutions for 720p and 360p
after creating all three resolutions we will add MASTER PLAYLIST which contains all three versions with different BANDWIDTH options.
this bandwidth will switch videos versions automatically based on your internet speed.
MASTER_PLAYLIST_SAMPLE
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
https://PROJECT_PATH/hls/5001/101/hlsThumbs/360/sample_video_360.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480
https://PROJECT_PATH/hls/5001/101/hlsThumbs/480/sample_video_480.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
https://PROJECT_PATH/hls/5001/101/hlsThumbs/720/sample_video_720.m3u8
#EXT-X-ENDLIST
VERSIONING_FILE_SAMPLE
let ffmpegPath = require('#ffmpeg-installer/ffmpeg').path;
let ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffmpegPath);
const creatorId = 5001;
const videoId = 101;
const videoFileName = "sample_video.mp4";
const version480Name = "sample_video_480.m3u8"
ffmpeg('public/assets/hls/'+creatorId+'/'+videoId+'/original/'+videoFileName, { timeout: 432000 }).addOptions([
'-strict -2',
'-preset:v veryfast',
'-profile:v baseline', // baseline profile (level 3.0) for H264 video codec
'-level 3.0',
'-s 842x480', // 842px width, 480px height output video dimensions
'-start_number 0', // start the first .ts segment at index 0
'-hls_time 10', // 10 second segment duration
'-hls_list_size 0', // Maxmimum number of playlist entries (0 means all entries/infinite)
'-f hls' // HLS format
]).output('public/assets/hls/'+creatorId+'/'+videoId+'/hlsThumbs/480/'+version480Name)
.on('error', function(err, stdout, stderr) {
console.log('An error occurred: ' + err);
console.log("ffmpeg stdout:\n" + stdout);
console.log("ffmpeg stderr:\n" + stderr);
console.log("Hls Cannot be converted for this video");
callback(err);
})
.on('progress', function(progress) {
console.log('HLS Processing : 480p ----'+ JSON.stringify(progress) + ' done'); })
.on('end', callback1).run();
You can debug your .m3u8 HLS URL from this link - HLS URL TESTER
I'm trying to extract audio from video. This code works well:
ffmpeg('1.mp4').output('1.mp3')
.noVideo()
.format('mp3')
.outputOptions('-ab','192k')
.run();
But if I read the file with a stream like this:
var video = fs.createReadStream('1.mp4');
var audio = fs.createWriteStream('1.mp3');
ffmpeg(video).output(audio)
.noVideo()
.format('mp3')
.outputOptions('-ab','192k')
.run();
The output file weighs 1KB and does not have anything in it.
How can I extract audio using streams?
For further investigation, I added a callback for different sort of errors while transcoding, as well as other events. First hint was the following callback on progress event:
{ frames: NaN,
currentFps: NaN,
currentKbps: NaN,
targetSize: 0,
timemark: '00:00:00.00' }
As well, there was a stderr event:
ffmpeg version n4.1 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 8.2.1 (GCC) 20180831
configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
libavutil 56. 22.100 / 56. 22.100
libavcodec 58. 35.100 / 58. 35.100
libavformat 58. 20.100 / 58. 20.100
libavdevice 58. 5.100 / 58. 5.100
libavfilter 7. 40.101 / 7. 40.101
libswscale 5. 3.100 / 5. 3.100
libswresample 3. 3.100 / 3. 3.100
libpostproc 55. 3.100 / 55. 3.100
[mov,mp4,m4a,3gp,3g2,mj2 # 0x562c9e18de80] stream 0, offset 0x30: partial file
[mov,mp4,m4a,3gp,3g2,mj2 # 0x562c9e18de80] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 240x180, 374 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:0':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf54.63.104
Duration: 00:00:14.72, start: 0.000000, bitrate: N/A
Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 240x180, 374 kb/s, 25 fps, 25 tbr, 12800 tbn, 25600 tbc (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 75 kb/s (default)
Metadata:
handler_name : SoundHandler
Stream mapping:
Stream #0:1 -> #0:0 (aac (native) -> mp3 (libmp3lame))
[mov,mp4,m4a,3gp,3g2,mj2 # 0x562c9e18de80] stream 1, offset 0xa1e: partial file
pipe:0: Invalid data found when processing input
Output #0, mp3, to 'pipe:1':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
TSSE : Lavf58.20.100
Stream #0:0(und): Audio: mp3 (libmp3lame), 44100 Hz, stereo, fltp, 192 kb/s (default)
Metadata:
handler_name : SoundHandler
encoder : Lavc58.35.100 libmp3lame
size= 0kB time=00:00:00.00 bitrate=N/A speed= 0x
{ frames: NaN,
currentFps: NaN,
currentKbps: NaN,
targetSize: 0,
timemark: '00:00:00.00' }
video:0kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Output file is empty, nothing was encoded (check -ss / -t / -frames parameters if used)
The latter explains the empty file you're seeing in the output. As to why the error only happens while streaming the file, and not while opening it through another method, this answer explains the details: https://stackoverflow.com/a/40028894/1494833
Unfortunately the library doesn't yet support movflags input option as recommended above (https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/823).
ffmpeg doesn't like using using streams as inputs in my experience. the following code worked for me, first let ffmpeg do its thing, then create/play with streams for the input/output:
let video = `${files[0].filename}`;
readstream.pipe(fs.createWriteStream(`./${video}`))
.on('error', () => {
console.log("Some error occurred in download:" + error);
res.send(error);
})
.on('finish', () => {
let audio = `${video.split(".")[0]}.mp3`
ffmpeg(`${video}`).output(audio)
.noVideo()
.format(`mp3`)
.on(`end`, (stdout, stderr) => {
let newReadstream = fs.createReadStream(`${audio}`);
})
.run()
}
Assuming that you are using the ffmpeg package which is basically a wrapper for running ffmpeg in node, you would have to do it somewhat like this:
This code works on my system using the same file you provided in the comments above but you may have to make some changes (Node v8.12.0, ffmpeg v0.0.4).
const ffmpeg = require('ffmpeg');
try {
let process = new ffmpeg('3.mp4');
process.then(function (video) {
// Callback mode
video.fnExtractSoundToMP3('3.mp3', function (error, file) {
if (!error)
console.log('Audio file: ' + file);
});
}, function (err) {
console.log('Error: ' + err);
});
} catch (e) {
console.log(e.code);
console.log(e.msg);
}
I want to stream a file that is being uploaded to ffmpeg. I'm using node.js and it's not working!
I ended up testing piping an input to ffmpeg from a local file, and this doens't work either. Here's my code:
var processVideo = function(videoStream, resultPath) {
var cmdParams = [
'-i', '-',
'-y',
'-f', 'mp4',
'-vcodec', 'libx264',
'-vf', 'scale=-1:720',
'-f', 'mp4',
resultPath
];
var ffmpeg = child_process.spawn('ffmpeg', cmdParams);
var data = '';
ffmpeg.stdout
.on('data', function(chunk) { data += chunk; })
.on('end', function() { console.log('result', data); });
var err = '';
ffmpeg.stderr
.on('data', function(chunk) { err += chunk; })
.on('end', function() { console.log('error', err);});
videoStream.pipe(ffmpeg.stdin);
};
processVideo(fs.createReadStream(pathToLocalMP4File), localPathToResultFile);
The output I get is
error ffmpeg version 2.8 Copyright (c) 2000-2015 the FFmpeg developers
built with Apple LLVM version 7.0.0 (clang-700.1.76)
configuration: --prefix=/usr/local/Cellar/ffmpeg/2.8 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libvo-aacenc --enable-libxvid --enable-libtheora --enable-vda
libavutil 54. 31.100 / 54. 31.100
libavcodec 56. 60.100 / 56. 60.100
libavformat 56. 40.101 / 56. 40.101
libavdevice 56. 4.100 / 56. 4.100
libavfilter 5. 40.101 / 5. 40.101
libavresample 2. 1. 0 / 2. 1. 0
libswscale 3. 1.101 / 3. 1.101
libswresample 1. 2.101 / 1. 2.101
libpostproc 53. 3.100 / 53. 3.100
[mov,mp4,m4a,3gp,3g2,mj2 # 0x7fd1e2802a00] stream 0, offset 0x20: partial file
[mov,mp4,m4a,3gp,3g2,mj2 # 0x7fd1e2802a00] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 1920x1080, 16242 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:':
Metadata:
major_brand : mp42
minor_version : 537134592
compatible_brands: mp42
creation_time : 2015-12-26 12:47:49
Duration: 00:00:04.00, bitrate: N/A
Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 1920x1080, 16242 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 60k tbc (default)
Metadata:
creation_time : 2015-12-26 12:47:49
encoder : AVC Coding
Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 32000 Hz, mono, fltp, 46 kb/s (default)
Metadata:
creation_time : 2015-12-26 12:47:49
[buffer # 0x7fd1e1c08e20] Unable to parse option value "-1" as pixel format
Last message repeated 1 times
[buffer # 0x7fd1e1c08e20] Error setting option pix_fmt to value -1.
[graph 0 input from stream 0:0 # 0x7fd1e1c08f60] Error applying options to the filter.
Error opening filters!
result
I tried to set the -pix_fmt option, or -analyzeduration and -probesize as suggested by the error code and this question, to no avail.
However ffmpeg -i pathToLocalMP4File -y -f mp4 -pix_fmt yuv420p -vcodec libx264 -vf scale=-1:720 -f mp4 localPathToResultFile works perfectly in the terminal...
Any idea??