Getting Started with Transcoding
Introduction
Welcome to the initial steps of video transcoding with Qencode! This tutorial will walk you through the process of setting up and launching your first transcoding job. Transcoding is the backbone of delivering video content that maximizes accessibility and user experience across popular devices, software products and bandwidth ranges. With Qencode, you can seamlessly convert your video files into formats suitable for a wide array of applications, ensuring your viewers receive the best possible experience.
Prerequisites
-
A Qencode account to access to the Portal, in order to manage and create Projects.
1Preparing Your Project and API Key
Start by getting your API key by creating a new project in Qencode, or using the API key from an existing transcoding project. This key serves as your access token for all API calls, as well as to segment your usage analytics and billing.
- Log in to your Qencode account. If you do not have a Qencode account, create a free account here.
- In the side menu, navigate to Transcoding > Projects.
- Click + Add New Project.
- Name your project and note the API key.
2Setting up the SDK
Qencode provides a set of SDKs for the following programming languages: Python, Node JS, Java, PHP and C#. You can find instructions on setting up the SDK for each programming language below.
pip install qencode
pip3 install qencode3
npm install qencode-api --save
- Add the library as a dependancy to your project.
- If you're using Maven, add this to your pom.xml file:
<dependency>
<groupId>com.qencode.java.api.client</groupId>
<artifactId>qencode-java-api-client</artifactId>
<version>1.0.14</version>
</dependency>
- Create composer.json under the root of your project with the following instructions:
{
"require": {
"qencode/api-client": "1.09.*"
}
}
- Run composer:
php composer.phar install
- Clone the SDK from GitHub:
git clone https://github.com/Qencode-Corp/qencode-api-dotnet-client
- Adding SDK to Your Solution
Add the Qencode.Api.CSharp.Client
project to your existing .NET solution and create a reference to it from your project.
- Using the SDK in Your Project
Include the following namespaces in your C# file:
using Qencode.Api.CSharp.Client;
using Qencode.Api.CSharp.Client.Classes;
using Qencode.Api.CSharp.Client.Exceptions;
3Authenticating with the API
Establish a secure and authenticated session with the Qencode API using your unique API key. This step confirms your identity and grants you permission to submit transcoding jobs using the Qencode API.
Request
curl --location 'https://api.qencode.com/v1/access_token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'api_key=YOUR_API_KEY'
Response
{
"error": 0,
"token": "5de1d971e45fa0f7a6dc77a05e893123",
"expire": "2023-08-02T13:42:23"
}
The server will respond with the access token, you can re-use it in the subsequent calls until it expires.
import qencode
API_KEY = '123e7c7ed567e'
client = qencode.client(API_KEY)
import qencode3 as qencode
API_KEY = '123e7c7ed567e'
client = qencode.client(API_KEY)
const apiKey = '123e7c7ed567e';
const qencodeApiClient = await new QencodeApiClient(apiKey);
String apiKey = "123e7c7ed567e";
QencodeApiClient client = new QencodeApiClient(apiKey);
$apiKey = '123e7c7ed567e';
$q = new QencodeApiClient($apiKey);
var apiKey = "123e7c7ed567e";
var q = new QencodeApiClient(apiKey);
4Creating a Transcoding Task
Begin configuring your new transcoding task by initializing it within your authenticated Qencode session. This sets up a container for the specific encoding parameters to define how your video will be processed. At this stage, you’re not setting any transcoding parameters yet, you're simply generating a task ID that you will use to define these parameters in the next steps.
Request
Use the /v1/create_task method:
curl --location 'https://api.qencode.com/v1/create_task' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'token=YOUR_ACCESS_TOKEN'
Response
The server response will contain the task_token value which is your transcoding job ID:
{
"error": 0,
"upload_url": "https://prod-us-central1-a-1-storage-gcp.qencode.com/v1/upload_file",
"task_token": "10566ad19601a465801ae56975750123"
}
You should use the task_token value on the next step to start the transcoding task execution.
task = client.create_task()
task = client.create_task()
let task = await qencodeApiClient.CreateTask();
TranscodingTask task = client.CreateTask();
$task = $q->createTask();
var task = q.CreateTask();
5Defining Your Output Settings
Now you can define the formats, codecs, resolution and combination of settings for each of the outputs you want to create using the format object.
{
"query": {
"format": [
{
"output": "mp4",
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
},
{
"output": "advanced_hls",
"optimize_bitrate": 0,
"stream": [
{
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
}
]
}
],
"source": "https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov"
}
}
QUERY = """
{
"query": {
"source": "https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov",
"format": [
{
"output": "mp4",
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
},
{
"output": "advanced_hls",
"optimize_bitrate": 0,
"stream": [
{
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
}
]
}
]
}
}
"""
QUERY = """
{
"query": {
"source": "https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov",
"format": [
{
"output": "mp4",
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
},
{
"output": "advanced_hls",
"optimize_bitrate": 0,
"stream": [
{
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
}
]
}
]
}
}
"""
let transcodingParams = {
"source": "https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov",
"format": [
{
"output": "mp4",
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
},
{
"output": "advanced_hls",
"optimize_bitrate": 0,
"stream": [
{
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
}
]
}
]
};
String QUERY = "{\n" +
" \"query\": {\n" +
" \"source\": \"https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov\",\n" +
" \"format\": [\n" +
" {\n" +
" \"output\": \"mp4\",\n" +
" \"size\": \"1920x1080\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 4800,\n" +
" \"video_codec\": \"libx264\" \n" +
" },\n" +
" {\n" +
" \"output\": \"mp4\",\n" +
" \"size\": \"1280x720\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 3800,\n" +
" \"video_codec\": \"libx264\"\n" +
" }, \n" +
" {\n" +
" \"output\": \"mp4\",\n" +
" \"size\": \"960x540\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 3000,\n" +
" \"video_codec\": \"libx264\"\n" +
" }, \n" +
" {\n" +
" \"output\": \"mp4\",\n" +
" \"size\": \"640x360\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 1800,\n" +
" \"video_codec\": \"libx264\"\n" +
" }, \n" +
" {\n" +
" \"output\": \"mp4\",\n" +
" \"size\": \"426x240\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 1200,\n" +
" \"video_codec\": \"libx264\"\n" +
" },\n" +
" {\n" +
" \"output\": \"advanced_hls\",\n" +
" \"optimize_bitrate\": 0,\n" +
" \"stream\": [\n" +
" {\n" +
" \"size\": \"1920x1080\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 4800,\n" +
" \"video_codec\": \"libx264\"\n" +
" },\n" +
" {\n" +
" \"size\": \"1280x720\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 3800,\n" +
" \"video_codec\": \"libx264\"\n" +
" },\n" +
" {\n" +
" \"size\": \"960x540\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 3000,\n" +
" \"video_codec\": \"libx264\"\n" +
" },\n" +
" {\n" +
" \"size\": \"640x360\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 1800,\n" +
" \"video_codec\": \"libx264\"\n" +
" },\n" +
" {\n" +
" \"size\": \"426x240\",\n" +
" \"profile\": \"main\",\n" +
" \"bitrate\": 1200,\n" +
" \"video_codec\": \"libx264\"\n" +
" }\n" +
" ]\n" +
" } \n" +
" ]\n" +
" }\n" +
"}";
$params = <<<EOD
{
"query": {
"source": "https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov",
"format": [
{
"output": "mp4",
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
},
{
"output": "advanced_hls",
"optimize_bitrate": 0,
"stream": [
{
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
}
]
}
]
}
}
EOD;
string transcodingParams = @"
{
""query"": {
""source"": ""https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov"",
""format"": [
{
""output"": ""mp4"",
""size"": ""1920x1080"",
""profile"": ""main"",
""bitrate"": 4800,
""video_codec"": ""libx264""
},
{
""output"": ""mp4"",
""size"": ""1280x720"",
""profile"": ""main"",
""bitrate"": 3800,
""video_codec"": ""libx264""
},
{
""output"": ""mp4"",
""size"": ""960x540"",
""profile"": ""main"",
""bitrate"": 3000,
""video_codec"": ""libx264""
},
{
""output"": ""mp4"",
""size"": ""640x360"",
""profile"": ""main"",
""bitrate"": 1800,
""video_codec"": ""libx264""
},
{
""output"": ""mp4"",
""size"": ""426x240"",
""profile"": ""main"",
""bitrate"": 1200,
""video_codec"": ""libx264""
},
{
""output"": ""advanced_hls"",
""optimize_bitrate"": 0,
""stream"": [
{
""size"": ""1920x1080"",
""profile"": ""main"",
""bitrate"": 4800,
""video_codec"": ""libx264""
},
{
""size"": ""1280x720"",
""profile"": ""main"",
""bitrate"": 3800,
""video_codec"": ""libx264""
},
{
""size"": ""960x540"",
""profile"": ""main"",
""bitrate"": 3000,
""video_codec"": ""libx264""
},
{
""size"": ""640x360"",
""profile"": ""main"",
""bitrate"": 1800,
""video_codec"": ""libx264""
},
{
""size"": ""426x240"",
""profile"": ""main"",
""bitrate"": 1200,
""video_codec"": ""libx264""
}
]
}
]
}
}
";
6Starting a Transcoding Task
Now that all the settings have been defined, it's time to start your transcoding tasks. Run the code below to kick off the process using the settings you've just defined.
For this tutorial, we will be using the /v1/start_encode2api method, which enables you to create full transcoding workflows using only the API. Encoding with the Custom Tasks also also offers a wider range of settings which can be used to fine tune your transcoding results.
Request Get the status for the job using the /v1/status API method:
curl --location 'https://api.qencode.com/v1/start_encode2' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'task_token=YOUR_TASK_TOKEN' \
--data-urlencode 'query={
"query": {
"format": [
{
"output": "mp4",
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"output": "mp4",
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
},
{
"output": "advanced_hls",
"optimize_bitrate": 0,
"stream": [
{
"size": "1920x1080",
"profile": "main",
"bitrate": 4800,
"video_codec": "libx264"
},
{
"size": "1280x720",
"profile": "main",
"bitrate": 3800,
"video_codec": "libx264"
},
{
"size": "960x540",
"profile": "main",
"bitrate": 3000,
"video_codec": "libx264"
},
{
"size": "640x360",
"profile": "main",
"bitrate": 1800,
"video_codec": "libx264"
},
{
"size": "426x240",
"profile": "main",
"bitrate": 1200,
"video_codec": "libx264"
}
]
}
],
"source": "https://nyc3.s3.qencode.com/qencode/samples/1080-sample.mov"
}
}'
Replace YOUR_TASK_TOKEN with your actual task token.
Response
{
"error": 0,
"status_url": "https://prod-us-central1-a-1-api-gcp.qencode.com/v1/status"
}
task.start_custom(QUERY)
task.custom_start(QUERY)
await task.StartCustom(transcodingParams);
task.startCustomWithJSON(QUERY);
$task->startCustom($params);
var started = task.StartCustom(transcodingParams);
7Checking Status
There are a few approaches to tracking status for transcoding tasks after they have started.
The first is by using Callback URls (Webhooks) to automatically receive changes in status, including updates when the job is complete. You can read a full guide on how to set up Callback URLs here.
The second option is using the /v1/status method and providing a list of tokens to get the status for.
You can even set up a script on your end to get the status updates on a job in real-time. Here's an example of what a script like that could look like:
Request
Get the status for the job using the /v1/status API method (Replace YOUR_TASK_TOKEN with your actual task token):
curl --location 'https://api.qencode.com/v1/status' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'task_tokens=YOUR_TASK_TOKEN'
Response
{
"error": 0,
"statuses": {
"10566ad19601a465801ae56975750123": {
"status": "completed",
"percent": 100,
"error": 0,
...
...
}
}
}
import time
import json
# using while loop
while 1:
status = task.status()
print json.dumps(status, indent=2, sort_keys=True)
if status['error'] or status['status'] == 'completed':
break
time.sleep(3)
# using callback methods
def progress_changed_handler(status):
if status['status'] != 'completed':
print json.dumps(status, indent=2, sort_keys=True)
def task_completed_handler(status, task_token):
print 'Completed task: %s' % task_token
print json.dumps(status, indent=2, sort_keys=True)
task.progress_changed(progress_changed_handler)
task.task_completed(task_completed_handler, task.task_token)
import time
import json
# using while loop
while 1:
status = task.status()
print(json.dumps(status, indent=2, sort_keys=True))
if status['error'] or status['status'] == 'completed':
break
time.sleep(3)
# using callback methods
def progress_changed_handler(status):
if status['status'] != 'completed':
print(json.dumps(status, indent=2, sort_keys=True))
def task_completed_handler(status, task_token):
print(f'Completed task: {task_token}')
print(json.dumps(status, indent=2, sort_keys=True))
task.progress_changed(progress_changed_handler)
task.task_completed(task_completed_handler, task.task_token)
async function CheckTaskStatus(){
let statusObject = await task.GetStatus()
let status = statusObject.status
while (status != "completed") {
statusObject = await task.GetStatus()
status = statusObject.status;
progress = statusObject.percent;
console.log(`task: ${task.taskToken} | status: ${status} | progress: ${progress}`);
await sleep(10000);
}
}
function sleep(ms){
return new Promise(resolve=>{
setTimeout(resolve,ms)
})
}
CheckTaskStatus();
TranscodingTaskStatus response;
String status;
do {
System.out.print("Checking job status... ");
response = task.getStatus();
status = response.getStatus();
System.out.print(status);
if (status.equals("encoding")) {
final Float percent = response.getPercent();
System.out.println(" " + percent.toString() + "%");
}
else {
System.out.println();
}
TimeUnit.SECONDS.sleep(5);
} while (!status.equals("completed") && response.getError() != 1);
if (response.getError() == 1) {
System.out.println(response.getErrorDescription());
}
do {
sleep(5);
$response = $task->getStatus();
if (is_array($response) and array_key_exists('percent', $response)) {
log_message("Completed: {$response['percent']}%");
}
} while ($response['status'] != 'completed');
task.TaskCompleted = new RunWorkerCompletedEventHandler(
delegate (object o, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
Console.WriteLine("Error: ", e.Error.Message);
}
var response = e.Result as TranscodingTaskStatus;
if (response.error == 1)
{
Console.WriteLine("Error: " + response.error_description);
}
else if (response.status == "completed")
{
Console.WriteLine("Video urls: ");
foreach (var video in response.videos)
{
Console.WriteLine(video.url);
}
}
else
{
Console.WriteLine(response.status);
}
Console.WriteLine("Done!");
});
var started = task.StartCustom(transcodingParams);
POSSIBLE STATUS VALUES
downloading | Video is being downloaded to Qencode server. |
queued | Task is waiting for available encoders. |
encoding | Video is being transcoded. |
saving | Video is being saved to destination location. |
completed | The transcoding job has completed successfully and the videos were saved to the destination. |