Reputation: 1
At first I thought maybe the issue is that videos are without audio so I changed the config settings but when ever I upload the video I get internal error
Here's the code for reference, is there error on my part or random failure of transcoding job
const express = require('express');
const multer = require('multer');
const { Storage } = require('@google-cloud/storage');
const { TranscoderServiceClient } = require('@google-cloud/video-transcoder');
const path = require('path');
const fs = require('fs');
require('dotenv').config();
// Validate Environment Variables
if (!process.env.GOOGLE_CLOUD_PROJECT_ID ||
!process.env.GOOGLE_CLOUD_PRIVATE_KEY ||
!process.env.GOOGLE_CLOUD_EMAIL ||
!process.env.BUCKET_NAME) {
console.error('Missing required environment variables.');
process.exit(1);
}
// Initialize App
const app = express();
app.use(express.json());
// Google Cloud Storage Setup
const storage = new Storage({
projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
credentials: {
type: "service_account",
project_id: process.env.GOOGLE_CLOUD_PROJECT_ID,
private_key: process.env.GOOGLE_CLOUD_PRIVATE_KEY.replace(/\\n/g, '\n'),
client_email: process.env.GOOGLE_CLOUD_EMAIL,
},
});
const bucketName = process.env.BUCKET_NAME; // Replace with your bucket name
const bucket = storage.bucket(bucketName);
// Transcoder API Client
const transcoderClient = new TranscoderServiceClient({
credentials: {
type: "service_account",
project_id: process.env.GOOGLE_CLOUD_PROJECT_ID,
private_key: process.env.GOOGLE_CLOUD_PRIVATE_KEY.replace(/\\n/g, '\n'),
client_email: process.env.GOOGLE_CLOUD_EMAIL,
},
});
// Multer Setup for File Uploads
const upload = multer({
dest: 'uploads/', // Temporary folder for storing files
});
// Function to check if the file has audio (e.g., using ffprobe or similar)
const hasAudio = (filePath) => {
return new Promise((resolve, reject) => {
const { exec } = require('child_process');
exec(`ffprobe -v error -select_streams a:0 -show_entries stream=codec_type -of default=noprint_wrappers=1:nokey=1 ${filePath}`, (error, stdout, stderr) => {
if (error) {
reject(error);
}
resolve(stdout.trim() === 'audio');
});
});
};
const getVideoProperties = (filePath) => {
return new Promise((resolve, reject) => {
const { exec } = require('child_process');
exec(
`ffprobe -v error -select_streams v:0 -show_entries stream=width,height,bit_rate,r_frame_rate -of json ${filePath}`,
(error, stdout, stderr) => {
if (error) {
return reject(error);
}
try {
const metadata = JSON.parse(stdout);
const videoStream = metadata.streams[0];
const frameRate = videoStream.r_frame_rate.split('/').map(Number);
resolve({
width: videoStream.width,
height: videoStream.height,
bitrate: parseInt(videoStream.bit_rate, 10),
frameRate: frameRate[0] / frameRate[1],
});
} catch (err) {
reject(err);
}
}
);
});
};
const transcodingConfig = (outputUri, hasAudio, videoProperties) => {
const { width, height, bitrate, frameRate } = videoProperties;
const config = {
elementaryStreams: [
{
key: 'video-stream0',
videoStream: {
h264: {
heightPixels: height,
widthPixels: width,
bitrateBps: bitrate,
frameRate: Math.round(frameRate),
},
},
},
],
muxStreams: [
{
key: 'output-stream',
container: 'mp4',
elementaryStreams: ['video-stream0'],
},
],
};
if (hasAudio) {
config.elementaryStreams.push({
key: 'audio-stream',
audioStream: {
codec: 'aac',
bitrateBps: 64000,
},
});
config.muxStreams[0].elementaryStreams.push('audio-stream');
}
return config;
};
// Upload and Transcode Endpoint
app.post('/upload', upload.single('file'), async (req, res) => {
try {
const file = req.file;
if (!file) {
return res.status(400).json({ error: 'No file uploaded' });
}
const inputUri = `gs://${bucketName}/`;
const outputUri = `gs://${bucketName}/`;
// Check if the uploaded video file has audio
const fileHasAudio = await hasAudio(file.path);
// Extract video properties
const videoProperties = await getVideoProperties(file.path);
// Generate transcoding config based on dynamic properties
const jobConfig = transcodingConfig(outputUri, fileHasAudio, videoProperties);
// Upload raw file to GCS
await bucket.upload(file.path, { destination: file.filename });
// Transcoding Job
const location = 'asia-south1';
const [response] = await transcoderClient.createJob({
parent: transcoderClient.locationPath(process.env.GOOGLE_CLOUD_PROJECT_ID, location),
job: {
inputUri: inputUri,
outputUri: outputUri,
config: jobConfig,
},
});
// Cleanup Local File
fs.unlinkSync(file.path);
res.status(200).json({
message: 'File uploaded and transcoding job created',
jobId: response.name,
outputUri,
});
} catch (error) {
console.error('Error during transcoding:', error);
res.status(500).json({ error: 'Internal server error', details: error.message });
}
});
// Method to get the state of the transcoding job
async function getJobState(jobId) {
try {
const projectId = process.env.GOOGLE_CLOUD_PROJECT_ID;
const location = 'asia-south1'; // Ensure this matches your location
// Use the transcoder client to get the job state
const [job] = await transcoderClient.getJob({
name: transcoderClient.jobPath(projectId, location, jobId),
});
// If the job failed, include error details
if (job.state === 'FAILED') {
console.error('Transcoding job failed:', job.error);
}
return job;
} catch (error) {
console.error('Error fetching job state:', error);
throw new Error('Failed to retrieve job state');
}
}
// API endpoint to fetch the state of a transcoding job
app.get('/job-state/:jobId', async (req, res) => {
const { jobId } = req.params;
try {
// Get the state of the transcoding job
const job = await getJobState(jobId);
// Prepare the response
const response = {
jobName: job.name,
state: job.state,
outputUri: job.outputUri,
createTime: job.createTime,
updateTime: job.updateTime,
};
// Include error details if the job failed
if (job.state === 'FAILED') {
response.errorDetails = job.error;
}
// Send the job state in the response
res.status(200).json(response);
} catch (error) {
res.status(500).json({
error: 'Error retrieving job state',
details: error.message,
});
}
});
// Start the Server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
I was trying to make transcoding job and I sometimes get failure with code 13 and sometimes successful job.
Upvotes: 0
Views: 27