Skip to content

Commit

Permalink
encore profile generation draft
Browse files Browse the repository at this point in the history
  • Loading branch information
oshinongit committed Nov 1, 2023
1 parent 3dff5bc commit c4fc697
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 159 deletions.
34 changes: 21 additions & 13 deletions examples/encoreExample.ts → examples/encore/encoreExample.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EncorePipelineConfiguration } from '../src/pipelines/encore/encore-pipeline-configuration';
import { EncorePipeline } from '../src/pipelines/encore/encore-pipeline';
import { BitrateResolutionPair } from '../src/models/bitrate-resolution-pair';
import { EncorePipelineConfiguration } from '../../src/pipelines/encore/encore-pipeline-configuration';
import { EncorePipeline } from '../../src/pipelines/encore/encore-pipeline';
import createJob from '../../src/create-job';
import fs from 'fs';

async function transcodeInputsAndAnalyze() {

Expand All @@ -18,18 +19,25 @@ async function transcodeInputsAndAnalyze() {
encoreInstancePostCreationDelay_ms: 10000
};

const bitrateResolutionPairs: BitrateResolutionPair[] = [
{resolution: { width: 1280, height: 720}, bitrate: 600000},
{resolution: { width: 640, height: 360}, bitrate: 600000},
{resolution: { width: 768, height: 432, range: {
const inlineProfile = fs.readFileSync('encoreProfile.yml', 'utf8');

const resolutions = [{
width: 1280,
height: 720,
range: {
min: 500000,
max: 600000
}}, bitrate: 500000}
]
}
}];

const bitrates = [
500000,
600000,
800000
]

const pipeline: EncorePipeline = new EncorePipeline(configuration);
await pipeline.transcode(configuration.inputs[0], { width: 1280, height: 720}, 600000, "output", undefined, bitrateResolutionPairs);

await pipeline.transcode(configuration.inputs[0], { width: 1280, height: 720}, 600000, "output", undefined, inlineProfile, resolutions, bitrates);
}
transcodeInputsAndAnalyze();

transcodeInputsAndAnalyze();
17 changes: 17 additions & 0 deletions examples/encore/encoreProfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: X264Encode
description: Program profile
scaling: bicubic
encodes:
- type: X264Encode
suffix: _x264_
twoPass: true
height:
width:
params:
b:v:
maxrate:
minrate:
r: 25
fps_mode: cfr
pix_fmt: yuv420p
force_key_frames: expr:not(mod(n,96))
12 changes: 12 additions & 0 deletions examples/encore/pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
encore:
apiAddress: https://api-encore.stage.osaas.io,
token: null,
instanceId: dummy,
profilesUrl: profilesUrl,
outputFolder: /usercontent/demo,
baseName: _demo,
inputs: ['https://testcontent.eyevinn.technology/mp4/stswe-tvplus-promo.mp4'],
duration: 120,
priority: 0,
encorePollingInterval_ms: 30000,
encoreInstancePostCreationDelay_ms: 10000,
1 change: 0 additions & 1 deletion src/analysis/brute-force.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export default async function analyzeBruteForce(directory: string, reference: st
if (options.pipelineVariables) {
// Create all combinations of bitrate, resolution, and variables
Object.entries(options.pipelineVariables).forEach(([variableName, values]) => {
//console.log(`variableName: ${variableName}`);
pairs = pairs.flatMap(pair =>
values.map( value => {
const variables = pair.ffmpegOptionVariables ? {...pair.ffmpegOptionVariables} : {};
Expand Down
85 changes: 18 additions & 67 deletions src/encoreYamlGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,28 @@
import * as fs from 'fs';
import { EncoreTranscodeType, EncoreProgramProfile } from './models/encoreProfileTypes';
import YAML from 'yaml';
import { Resolution } from '../src/models/resolution';

export class EncoreYAMLGenerator {
generateYAML(profile: EncoreProgramProfile): string {
const yamlContent = `
name: ${profile.name}
description: ${profile.description}
scaling: ${profile.scaling}
encodes:
${profile.encodes
.map((encode) => `
- type: ${encode.type}
suffix: ${encode.suffix}
twoPass: ${encode.twoPass}
height: ${encode.height}
params:${Object.entries(encode.params)
.map(([key, value]) => `
${key}: ${value}`)
.join('')}
`)
.join('')}
`;

return yamlContent;
}

createEncodeObject(
type: string,
suffix: string,
twoPass: boolean,
height: number,
params: { [key: string]: string }
): EncoreTranscodeType {
return {
type,
suffix,
twoPass,
height,
params,
};
}
modifyEncoreProfileAttributes(profileEncodesObject: string, resolution: Resolution, bitRate: number): string {

saveToFile(profile: EncoreProgramProfile, filePath: string) {
const generatedYAML = this.generateYAML(profile);
fs.writeFileSync(filePath, generatedYAML);
}
}
const yamlContent = profileEncodesObject;
//console.log(profileEncodesObject);
const data = YAML.parse(yamlContent);
console.log(YAML.stringify(data.params))

data.height = resolution.height;
data.width = resolution.width;
// data.params['b:v'] = bitRate;
data.params['maxrate'] = resolution.range?.max;
data.params['minrate'] = resolution.range?.min;

// Example usage:
function example() {
const yamlGenerator = new EncoreYAMLGenerator();
const updatedYAML = YAML.stringify(data);

const encodeObject = yamlGenerator.createEncodeObject('X264Encode', '_x264_3100', true, 1080, {
'b:v': '3100k',
maxrate: '4700k',
bufsize: '6200k',
r: '25',
fps_mode: 'cfr',
pix_fmt: 'yuv420p',
force_key_frames: 'expr:not(mod(n,96))',
preset: 'medium',
});

const encodeObjects: EncoreTranscodeType[] = [];
encodeObjects.push(encodeObject);

const programProfile: EncoreProgramProfile = {
name: 'encoreProgram',
description: 'Program profile',
scaling: 'bicubic',
encodes: encodeObjects,
};
return updatedYAML;
}

yamlGenerator.saveToFile(programProfile, 'encoreProfile.yml');
saveToFile(filePath: string, yaml: string) {
fs.writeFileSync(filePath, yaml);
}
}
17 changes: 17 additions & 0 deletions src/load-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs';
import YAML from 'yaml';
import LocalPipeline from './pipelines/local/local-pipeline';
import AWSPipeline from './pipelines/aws/aws-pipeline';
import { EncorePipeline } from './pipelines/encore/encore-pipeline';
import { Pipeline } from './pipelines/pipeline';
import logger from './logger';
import { LocalPipelineConfiguration } from './pipelines/local/local-pipeline-configuration';
Expand Down Expand Up @@ -30,6 +31,20 @@ export type PipelineProfile = {
pythonPath: string;
ffmpegEncoder: 'libx264' | 'h264_videotoolbox';
};

encore?: {
apiAddress: string;
token: string;
instanceId: string;
profilesUrl: string;
outputFolder: string;
baseName: string;
inputs: Array<string>;
duration: number;
priority: number;
encorePollingInterval_ms: number;
encoreInstancePostCreationDelay_ms: number
}
};

/**
Expand All @@ -53,6 +68,8 @@ async function loadPipeline(pipelineFilename: string, encodingProfile?: string):
return new AWSPipeline({ ...pipelineProfile.aws, mediaConvertSettings: encodingProfileData });
} else if (pipelineProfile.local !== undefined) {
return new LocalPipeline({ ...pipelineProfile.local, ffmpegOptions: encodingProfileData });
} else if (pipelineProfile.encore !== undefined) {
return new EncorePipeline(pipelineProfile.encore);
} else {
throw new Error(`Invalid pipeline: ${JSON.stringify(pipelineProfile)}`);
}
Expand Down
14 changes: 0 additions & 14 deletions src/models/encoreProfileTypes.ts

This file was deleted.

84 changes: 20 additions & 64 deletions src/pipelines/encore/encore-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { EncoreInstance } from '../../models/encoreInstance';
import { EncoreJobs, EncoreJob } from '../../models/encoreJobs';
import { EncorePipelineConfiguration } from './encore-pipeline-configuration';
import { EncoreYAMLGenerator } from '../../encoreYamlGenerator';
import { EncoreTranscodeType, EncoreProgramProfile } from '../../models/encoreProfileTypes';
import { Pipeline } from '../pipeline';
import { Resolution } from '../../models/resolution';
import { QualityAnalysisModel } from '../../models/quality-analysis-model';
import logger from '../../logger';
import fs from 'fs';
import YAML from 'yaml';
//const ISOBoxer = require('codem-isoboxer');
const { Readable } = require('stream'); //Typescript import library contains different functions.
//If someone knows how to achieve the same functionality in Typescript syntax let me know.
Expand All @@ -20,72 +20,28 @@ export function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

function createYAML(bitrateResolutions: BitrateResolutionPair[]): string {
function createProfile(inlineProfile: string, resolutions: Resolution[], bitRates: number[]): string {

const yamlGenerator = new EncoreYAMLGenerator();
let transcodeObjects: EncoreTranscodeType[] = [];
const inlineProfileObject = YAML.parse(inlineProfile);
let profiles: string[] = [];

bitrateResolutions.forEach(bitRateResolution => {

const resolution = bitRateResolution.resolution;

if (resolution.range && resolution.range.min && resolution.range.max) {
const bitRateRange = (resolution.range.max - resolution.range.min);
if (bitRateRange < 1000) {
throw new Error(`Small bitrate range: ${bitRateRange}, use discrete values instead or increase range`);
}
let bitRateStep: number = resolution.range.min;
let bitrateSteps = [bitRateStep];
while (bitRateStep < resolution.range.max) {
bitRateStep += 1000;
bitrateSteps.push(bitRateStep);
}
if (bitrateSteps[bitrateSteps.length - 1] > resolution.range.max) {
bitrateSteps[bitrateSteps.length - 1] = resolution.range.max;
}

bitrateSteps.forEach(bitRate => {

const transcodeObject = yamlGenerator.createEncodeObject('X264Encode', `_${bitRate / 100}`, true, resolution.height, {
'b:v': `${bitRate / 100}k`,
maxrate: `${(bitRate / 100) * 1.5}k`,
bufsize: `${(bitRate / 100) * 1.5 * 1.5}k`,
r: '25',
fps_mode: 'cfr',
pix_fmt: 'yuv420p',
force_key_frames: 'expr:not(mod(n,96))',
preset: 'medium',
});
transcodeObjects.push(transcodeObject);

})
resolutions.forEach(resolution => {
bitRates.forEach(bitRate => {
const encoding = yamlGenerator.modifyEncoreProfileAttributes(YAML.stringify(inlineProfileObject.encodes), resolution, bitRate);
profiles.push(encoding)
})
});

}
else if (!resolution.range) {
const encodeObject = yamlGenerator.createEncodeObject('X264Encode', `_${bitRateResolution.bitrate / 100}`, true, bitRateResolution.resolution.height, {
'b:v': `${bitRateResolution.bitrate / 100}k`,
maxrate: `${(bitRateResolution.bitrate / 100) * 1.5}k`,
bufsize: `${(bitRateResolution.bitrate / 100) * 1.5 * 1.5}k`,
r: '25',
fps_mode: 'cfr',
pix_fmt: 'yuv420p',
force_key_frames: 'expr:not(mod(n,96))',
preset: 'medium',
});
transcodeObjects.push(encodeObject);
}
let i = 0;
profiles.forEach(encodingProfile => {
inlineProfileObject.encodes[i] = encodingProfile;
i++;
})

const programProfile: EncoreProgramProfile = {
name: 'encoreProgram',
description: 'Program profile',
scaling: 'bicubic',
encodes: transcodeObjects,
};
yamlGenerator.saveToFile("test_output_profile.yml", inlineProfileObject);

yamlGenerator.saveToFile(programProfile, "profile.yml"); //TODO: Remove, for dev only
const transcodingProfile = yamlGenerator.generateYAML(programProfile);
return transcodingProfile
return "transcodingProfile"
}

export class EncorePipeline implements Pipeline {
Expand All @@ -111,18 +67,18 @@ export class EncorePipeline implements Pipeline {
this.awsPipe = new AWSPipeline(this.awsConf);
}

async transcode(input: string, targetResolution: Resolution, targetBitrate: number, output: string, variables?: Record<string, string>, bitrateResolutions?: BitrateResolutionPair[]): Promise<string> {
async transcode(input: string, targetResolution: Resolution, targetBitrate: number, output: string, variables?: Record<string, string>, inlineProfile?: string, resolutions?: Resolution[], bitRates?: number[]): Promise<string> {

if (!bitrateResolutions) {
throw new Error('No resolutionsBitrateRange in encore transcode');
if (!(inlineProfile && resolutions && bitRates)) {
throw new Error('No inline profile in encore transcode');
}

const instance: EncoreInstance | undefined = await this.createEncoreInstance(this.configuration.apiAddress, this.configuration.token, this.configuration.instanceId, this.configuration.profilesUrl);
await delay(this.configuration.encoreInstancePostCreationDelay_ms); // Delay required to allow instance to be created before calling it
if (!instance) {
throw new Error('undefined instance');
}
const transcodingProfile = createYAML(bitrateResolutions);
const transcodingProfile = createProfile(inlineProfile, resolutions, bitRates);
// await this.runTranscodePollUntilFinished(instance, transcodingProfile);
await this.deleteEncoreInstance(instance, this.configuration.apiAddress);
return output
Expand Down

0 comments on commit c4fc697

Please sign in to comment.