Я пытаюсь передать видеофайл в формате mp4 в своем приложении nextJS. Файл хранится в GridFSBucket mongodb.
Итак, для этого я использую getServerSideProps(). При вызове страницы в браузере Chrome отображается видеоплеер, но видео не воспроизводится (продолжительность 0:00). Я вижу несколько сообщений журнала warn - You should not access 'res' after getServerSideProps resolves.. Но я не думаю, что это проблема, поскольку это всего лишь предупреждение.
Возможно, кто-то заметит фундаментальную ошибку в моем коде, поскольку я не знаю, как отладить проблему без каких-либо сообщений об ошибках.
[хеш].tsx
export const getServerSideProps: GetServerSideProps = async ({
res,
query: { hash }
}) => {
const database = await mongodb()
const Videos = database.collection('videos')
const { fileId } = await Videos.findOne({ uid: hash })
const Files = new GridFSBucket(database)
const id = new ObjectId(fileId)
const file: GridFSFile = await new Promise((resolve, reject) => {
Files.find({
_id: id
}).toArray((err, files) => {
if (err) reject(err)
resolve(files[0])
})
})
const { contentType } = file || {}
res.writeHead('Content-Type', contentType) // contentType is "video/mp4"
Files.openDownloadStream(id)
.on('data', (chunk) => {
res.write(chunk)
})
.on('end', () => {
res.end()
})
.on('error', (err) => {
throw err
})
return {
props: {}
}
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы можете увидеть в разделе «Как интегрировать MongoDB в ваше приложение Next.js / Пример 2: Страницы Next.js с MongoDB » (из Адо Кукича и Кушагра Кесав ) пример использования функция getServerSideProps.
Это хорошо работает для данных, которые вы хотите отобразить на стороне сервера, а затем отправить клиенту в формате HTML.
Однако ваш первоначальный вопрос касается потоковой передачи мультимедийных файлов (в частности, видео MP4), хранящихся в MongoDB GridFS, что представляет собой другой вариант использования, чем тот, который рассматривается в руководстве MongoDB.
getServerSideProps().При работе с потоковыми медиафайлами вам также необходимо правильно настроить HTTP-заголовки, такие как Content-Type, и, возможно, поддерживать запросы диапазона HTTP. Это не входит в типичный вариант использования getServerSideProps().
Если вы хотите использовать видеофайл из MongoDB GridFS, вам следует сделать это, настроив для этого выделенную конечную точку API.
Создайте новый маршрут API, например pages/api/videos/[hash].ts:
import { MongoClient, ObjectId, GridFSBucket } from 'mongodb';
export default async function handler(req, res) {
if (req.method !== 'GET') {
res.status(405).end(); //Method Not Allowed
return;
}
const hash = req.query.hash;
try {
const database = await mongodb(); // Replace with your mongodb connection logic
const Videos = database.collection('videos');
const { fileId } = await Videos.findOne({ uid: hash });
const Files = new GridFSBucket(database);
const id = new ObjectId(fileId);
const file = await new Promise((resolve, reject) => {
Files.find({ _id: id }).toArray((err, files) => {
if (err) reject(err);
resolve(files[0]);
});
});
const { contentType } = file || {};
res.writeHead(200, { 'Content-Type': contentType });
Files.openDownloadStream(id)
.on('data', (chunk) => {
res.write(chunk);
})
.on('end', () => {
res.end();
})
.on('error', (err) => {
res.status(500).json({ error: err.message });
});
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
}
Затем в приложении Next.js вы можете использовать тег HTML5 <video> для встраивания видео:
<video controls width = "250">
<source src = "/api/videos/your-hash-goes-here" type = "video/mp4">
Your browser does not support the video tag.
</video>
Следуя этому подходу, вы позволяете браузеру обрабатывать потоковое видео и изолируете логику обслуживания мультимедиа от логики получения данных вашей страницы, что лучше подходит для Next.js.
Я вижу плеер, который не может открыть поток. Таймер установлен на
0:00. Развеhttp://localhost:3002/api/videos/RUaYNне следует уже транслировать файл? Я также добавил немногоconsole.info()в[hash].tsxи вижу, что данные файла считываются правильно (возвращает тип контента видео/mp4), возвращает фрагменты и что-то возвращает в конце. Вот почему я предполагаю, что соединение MongoDB и логика[hash].tsxверны...
Если ваш маршрут API читает файл из MongoDB GridFS, передает фрагменты в потоковом режиме и достигает конца без ошибок, но видеоплеер по-прежнему не работает, то проблема может быть на уровне HTTP или клиента.
Поэтому убедитесь, что заголовки HTTP-ответа установлены правильно. В частности, убедитесь, что для Content-Type установлено значение video/mp4. Если вы используете другой тип MIME для своего видео, измените заголовок и тег <source> соответствующим образом.
И проверьте консоль браузера на наличие ошибок или предупреждений. В частности, используйте вкладку «Сеть» инструментов разработчика вашего браузера для проверки HTTP-запроса и ответа. Проверьте код состояния, заголовки и размер содержимого. Убедитесь, что запрос действительно достигает вашей конечной точки /api/videos/[hash].
Убедитесь, что видео имеет правильный формат (MP4) и кодек, понятный видеоплееру HTML5. Несоответствие здесь может привести к сбою воспроизведения, даже если логика на стороне сервера правильна.
В свой серверный код добавьте обработку ошибок и ведение журнала, чтобы перехватывать любые исключения или ошибки, которые могут возникнуть. Это даст вам больше информации о том, что может пойти не так. В настоящее время вы выдаете ошибку в событии ошибки потока, но не обрабатываете ее.
На стороне клиента добавьте прослушиватели событий для error, loadeddata, canplay и т. д. в элемент <video>, чтобы отслеживать его состояние и выявлять потенциальные ошибки. Например:
<video controls width = "600"
onError = {(e) => {
console.error("Video error:", e);
}}
onLoadedData = {() => {
console.info("Video data loaded");
}}>
<source src = {`/api/videos/${videoHash}`} type = "video/mp4" />
Your browser does not support the video tag.
</video>
Это дало мне подсказку... Я попробовал еще один пример файла mp4 - и он сработал. Значит, проблема должна быть в самом видеофайле. Когда я смотрю на свойства файла: рабочий файл имеет кодеки MPEG-4 AAC, H.264, мой файл имеет MPEG-4 Video.
Хорошо, проблема, похоже, кроется в видеокодеках. Кодеки — это алгоритмы, используемые для кодирования и декодирования (или сжатия и распаковки) потоков цифровых данных. Что касается видеофайлов, не все кодеки поддерживаются универсально во всех браузерах и медиаплеерах.
Кодеки H.264 (часто упакованные в файлы MP4) и AAC широко поддерживаются и являются стандартными для воспроизведения видео HTML5. С другой стороны, MPEG-4 Видео может не поддерживаться повсеместно, что может привести к проблеме, с которой вы столкнулись.
Возможно, вам придется перекодировать: используйте инструмент преобразования видео, чтобы перекодировать видео с помощью видеокодека H.264 и аудиокодека AAC. Такие инструменты, как FFmpeg, могут быть весьма полезны для этой цели.
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -strict experimental output.mp4
Вы также можете использовать инструменты медиаинформации, такие как ffprobe, чтобы проверить детали вашего видеофайла. Это даст вам представление о том, какие кодеки используются, о размерах видео и т. д. Вы можете сравнить это с рабочим файлом, чтобы понять различия.
Можно ли прочитать файл из корзины (потока) MongoDB, перекодировать его с помощью
ffmpegи сохранить обратно в базу данных в Node.js?Потому что хотелось бы в самом приложении создать функцию перекодирования сбойных видеофайлов.
Должно быть возможно прочитать видеофайл из GridFS MongoDB, перекодировать его с помощью FFmpeg, а затем сохранить обратно в GridFS, и все это в приложении Node.js.
Обычно вы используете пакет gridfs-stream для MongoDB и пакет fluent-ffmpeg для FFmpeg в Node.js.
const mongodb = require('mongodb');
const { GridFSBucket } = require('mongodb');
const ffmpeg = require('fluent-ffmpeg');
// Establish MongoDB connection
mongodb.MongoClient.connect('mongodb://localhost:27017/testDB', async function(err, client) {
if (err) {
console.error(err);
return;
}
const db = client.db('testDB');
// Initialize GridFS
const bucket = new GridFSBucket(db, {
bucketName: 'videos'
});
const fileId = new mongodb.ObjectId('your-object-id-here'); // Replace with your object ID
const fileName = are-encoded-video.mp4'; // New name for the re-encoded file
// Read the existing video stream from GridFS
const readStream = bucket.openDownloadStream(fileId);
// Create write stream to GridFS for the re-encoded video
const uploadStream = bucket.openUploadStream(fileName);
const newFileId = uploadStream.id;
// Set up FFmpeg to read from the GridFS readStream
ffmpeg(readStream)
.outputFormat('mp4') // You can specify the output format
.videoCodec('libx264')
.audioCodec('aac')
.on('end', () => {
console.info( are-encoding succeeded');
// Optionally, delete the old file from GridFS
bucket.delete(fileId, (err) => {
if (err) {
console.error('Error deleting old file:', err);
} else {
console.info('Old file deleted');
}
});
})
.on('error', (err) => {
console.error( are-encoding failed:', err);
})
.pipe(uploadStream); // Pipe the re-encoded video back to GridFS
});
Заменить:
'your-object-id-here' с идентификатором объекта видео, которое вы хотите перекодировать. are-encoded-video.mp4' с любым новым именем, которое вы хотите дать перекодированному файлу.'videos' с именем вашего контейнера GridFS.'testDB' с именем вашей базы данных MongoDB.В этом примере есть базовая обработка ошибок. В производственном сценарии вам, вероятно, захочется добавить более надежную обработку ошибок. Вам потребуется установить FFmpeg на компьютере, где будет выполняться этот код.
Включив эту функцию в свое приложение, вы можете программно перекодировать видео, которые не воспроизводятся из-за проблем с кодеком.
К сожалению, я получаю сообщение об ошибке:
Error: ffmpeg exited with code 1: Conversion failed!. Как узнать, почему это не удалось? Это сообщение об ошибке не очень полезно.
Полученное вами сообщение об ошибке Error: ffmpeg exited with code 1: Conversion failed! действительно является довольно общим и не дает много информации о том, что пошло не так в процессе перекодирования. Чтобы получить дополнительную информацию, вы можете записать поток стандартных ошибок (stderr) из FFmpeg. Пакет fluent-ffmpeg позволяет вам сделать это через свои обработчики событий.
Вот модифицированный фрагмент кода, который фиксирует выходные данные stderr:
ffmpeg(readStream)
.outputFormat('mp4')
.videoCodec('libx264')
.audioCodec('aac')
.on('stderr', function(stderrLine) {
console.info('Stderr output: ' + stderrLine);
})
.on('end', function() {
console.info( are-encoding succeeded');
})
.on('error', function(err, stdout, stderr) {
console.error( are-encoding failed:', err);
console.error('ffmpeg stdout:', stdout);
console.error('ffmpeg stderr:', stderr);
})
.pipe(uploadStream);
Обработчик событий .on('stderr', function(stderrLine) {...}) должен захватывать каждую строку вывода stderr.
Обработчик событий .on('error', function(err, stdout, stderr) {...}) собирает дополнительную информацию, если в процессе перекодирования возникает ошибка.
Эти выходные данные могут предоставить вам дополнительную информацию о том, почему FFmpeg не может перекодировать видео, что может варьироваться от неподдерживаемых кодеков до неправильных настроек FFmpeg.
Просматривая журналы stderr, вы сможете лучше понять, что вызывает сбой преобразования. Имея эту информацию, вы сможете соответствующим образом настроить параметры FFmpeg, чтобы решить проблему.
Я получаю вот такую ошибку:
[mp4 @ 0x12a7041d0] muxer does not support non seekable output [out#0/mp4 @ 0x600003bb4000] Could not write header (incorrect codec parameters ?): Invalid argument
У FFmpeg возникают проблемы с выходным форматом или настройками кодека при попытке записи в поток, недоступный для поиска. Потоки GridFS по своей природе не подлежат поиску.
Попробуйте сначала записать перекодированное видео в промежуточный временный файл, а затем загрузить этот файл в GridFS.
ffmpeg(readStream)
.outputFormat('mp4')
.videoCodec('libx264')
.audioCodec('aac')
.on('end', function() {
// Read the intermediate file and upload it to GridFS here.
})
.on('error', function(err) {
console.error('Re-encoding failed:', err);
})
.save('intermediate-file.mp4'); // Save to a temporary file first
После сохранения файла вы можете прочитать этот промежуточный файл и загрузить его обратно в GridFS. (и удалите промежуточный файл после завершения загрузки).
Разве http://localhost:3002/api/videos/RUaYN не следует уже транслировать файл? Я также добавил немного console.info() в [hash].tsx и вижу, что данные файла читаются правильно (возвращает тип контента video/mp4), возвращает фрагменты и что-то возвращает в конце. Вот почему я предполагаю, что соединение mongodb и логика [hash].tsx верны...
@ user3142695 ОК. Я отредактировал ответ, чтобы ответить на ваш комментарий.
Большое спасибо. Это дало мне подсказку... Я попробовал еще один пример файла mp4 - и он сработал. Значит, проблема должна быть в самом видеофайле. Когда я смотрю на свойства файла: у рабочего файла есть кодеки MPEG-4 AAC, H.264, у моего файла есть MPEG-4 Video. Не совсем знаю, как это исправить, но спасибо за вашу огромную помощь...
@ user3142695 ОК. Я отредактировал ответ, чтобы учесть ваш последний вывод. Хороший улов!
Возможно, у вас есть некоторый опыт в этом: можно ли прочитать файл из ведра (потока) mongodb, перекодировать его с помощью ffmpeg и сохранить обратно в базу данных в nodeJS? Потому что хотелось бы в самом приложении создать функцию перекодирования сбойных видеофайлов..
@user3142695 user3142695 Я отредактировал ответ, учитывая ваш последний комментарий.
Ты мой герой. К сожалению, я получаю ошибку re-encoding failed: Error: ffmpeg exited with code 1: Conversion failed!. Как узнать, почему это не удалось? Это сообщение об ошибке не очень полезно.
@user3142695 user3142695 Я отредактировал этот «бесконечный» ответ, чтобы ответить на ваш последний комментарий;)
Извините за этот бесконечный пост... Вот такая ошибка: [mp4 @ 0x12a7041d0] muxer does not support non seekable output [out#0/mp4 @ 0x600003bb4000] Could not write header (incorrect codec parameters ?): Invalid argument
@ user3142695 user3142695 Я отредактировал ответ, чтобы предложить обходной путь, который может помочь вам избежать этой ошибки.
Спасибо за отличный ответ. К сожалению, это ничего не меняет. Я вижу плеер, который не может открыть поток. Таймер установлен на 0:00.