blob: 7a782a809338519e851ffed79d38c921a40c6056 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include "avcenc_api.h"
#include "avcenc_int.h"
// Constants.
enum {
kMaxWidth = 720,
kMaxHeight = 480,
kMaxFrameRate = 30,
kMaxBitrate = 2048, // in kbps.
kInputBufferSize = (kMaxWidth * kMaxHeight * 3) / 2, // For YUV 420 format.
kOutputBufferSize = kInputBufferSize,
kMaxDpbBuffers = 17,
kIDRFrameRefreshIntervalInSec = 1,
};
static void *MallocCb(void * /*userData*/, int32_t size, int32_t /*attrs*/) {
void *ptr = calloc(size, 1);
return ptr;
}
static void FreeCb(void * /*userData*/, void *ptr) {
free(ptr);
}
static int32_t DpbAllocCb(void * /*userData*/,
unsigned int sizeInMbs, unsigned int numBuffers) {
size_t frameSize = (sizeInMbs << 7) * 3;
if(numBuffers < kMaxDpbBuffers && frameSize <= kInputBufferSize) {
return 1;
} else {
return 0;
}
}
static int32_t BindFrameCb(void *userData, int32_t index, uint8_t **yuv) {
assert(index < kMaxDpbBuffers);
uint8_t** dpbBuffer = static_cast<uint8_t**>(userData);
*yuv = dpbBuffer[index];
return 1;
}
static void UnbindFrameCb(void * /*userData*/, int32_t /*index*/) {
}
int main(int argc, char *argv[]) {
if (argc < 7) {
fprintf(stderr, "Usage %s <input yuv> <output file> <width> <height>"
" <frame rate> <bitrate in kbps>\n", argv[0]);
fprintf(stderr, "Max width %d\n", kMaxWidth);
fprintf(stderr, "Max height %d\n", kMaxHeight);
fprintf(stderr, "Max framerate %d\n", kMaxFrameRate);
fprintf(stderr, "Max bitrate %d kbps\n", kMaxBitrate);
return EXIT_FAILURE;
}
// Read height and width.
int32_t width;
int32_t height;
width = atoi(argv[3]);
height = atoi(argv[4]);
if (width > kMaxWidth || height > kMaxHeight || width <= 0 || height <= 0) {
fprintf(stderr, "Unsupported dimensions %dx%d\n", width, height);
return EXIT_FAILURE;
}
if (width % 16 != 0 || height % 16 != 0) {
fprintf(stderr, "Video frame size %dx%d must be a multiple of 16\n",
width, height);
return EXIT_FAILURE;
}
// Read frame rate.
int32_t frameRate;
frameRate = atoi(argv[5]);
if (frameRate > kMaxFrameRate || frameRate <= 0) {
fprintf(stderr, "Unsupported frame rate %d\n", frameRate);
return EXIT_FAILURE;
}
// Read bit rate.
int32_t bitrate;
bitrate = atoi(argv[6]);
if (bitrate > kMaxBitrate || bitrate <= 0) {
fprintf(stderr, "Unsupported bitrate %d\n", bitrate);
return EXIT_FAILURE;
}
bitrate *= 1024; // kbps to bps.
// Open the input file.
FILE *fpInput = fopen(argv[1], "rb");
if (!fpInput) {
fprintf(stderr, "Could not open %s\n", argv[1]);
return EXIT_FAILURE;
}
// Open the output file.
FILE *fpOutput = fopen(argv[2], "wb");
if (!fpOutput) {
fprintf(stderr, "Could not open %s\n", argv[2]);
fclose(fpInput);
return EXIT_FAILURE;
}
// Allocate input buffer.
uint8_t *inputBuf = (uint8_t *)malloc(kInputBufferSize);
assert(inputBuf != NULL);
// Allocate output buffer.
uint8_t *outputBuf = (uint8_t *)malloc(kOutputBufferSize);
assert(outputBuf != NULL);
// Allocate dpb buffers.
uint8_t * dpbBuffers[kMaxDpbBuffers];
for (int i = 0; i < kMaxDpbBuffers; ++i) {
dpbBuffers[i] = (uint8_t *)malloc(kInputBufferSize);
assert(dpbBuffers[i] != NULL);
}
// Initialize the encoder parameters.
tagAVCEncParam encParams;
memset(&encParams, 0, sizeof(tagAVCEncParam));
encParams.rate_control = AVC_ON;
encParams.initQP = 0;
encParams.init_CBP_removal_delay = 1600;
encParams.intramb_refresh = 0;
encParams.auto_scd = AVC_ON;
encParams.out_of_band_param_set = AVC_ON;
encParams.poc_type = 2;
encParams.log2_max_poc_lsb_minus_4 = 12;
encParams.delta_poc_zero_flag = 0;
encParams.offset_poc_non_ref = 0;
encParams.offset_top_bottom = 0;
encParams.num_ref_in_cycle = 0;
encParams.offset_poc_ref = NULL;
encParams.num_ref_frame = 1;
encParams.num_slice_group = 1;
encParams.fmo_type = 0;
encParams.db_filter = AVC_ON;
encParams.disable_db_idc = 0;
encParams.alpha_offset = 0;
encParams.beta_offset = 0;
encParams.constrained_intra_pred = AVC_OFF;
encParams.data_par = AVC_OFF;
encParams.fullsearch = AVC_OFF;
encParams.search_range = 16;
encParams.sub_pel = AVC_OFF;
encParams.submb_pred = AVC_OFF;
encParams.rdopt_mode = AVC_OFF;
encParams.bidir_pred = AVC_OFF;
encParams.use_overrun_buffer = AVC_OFF;
encParams.width = width;
encParams.height = height;
encParams.bitrate = bitrate;
encParams.frame_rate = 1000 * frameRate; // In frames/ms.
encParams.CPB_size = (uint32_t) (bitrate >> 1);
int32_t IDRFrameRefreshIntervalInSec = kIDRFrameRefreshIntervalInSec;
if (IDRFrameRefreshIntervalInSec == 0) {
encParams.idr_period = 1; // All I frames.
} else {
encParams.idr_period = (IDRFrameRefreshIntervalInSec * frameRate);
}
int32_t nMacroBlocks = ((((width + 15) >> 4) << 4) *
(((height + 15) >> 4) << 4)) >> 8;
uint32_t *sliceGroup = (uint32_t *) malloc(sizeof(uint32_t) * nMacroBlocks);
assert(sliceGroup != NULL);
for (int i = 0, idx = 0; i < nMacroBlocks; ++i) {
sliceGroup[i] = idx++;
if (idx >= encParams.num_slice_group) {
idx = 0;
}
}
encParams.slice_group = sliceGroup;
encParams.profile = AVC_BASELINE;
encParams.level = AVC_LEVEL2;
// Initialize the handle.
tagAVCHandle handle;
memset(&handle, 0, sizeof(tagAVCHandle));
handle.AVCObject = NULL;
handle.userData = dpbBuffers;
handle.CBAVC_DPBAlloc = DpbAllocCb;
handle.CBAVC_FrameBind = BindFrameCb;
handle.CBAVC_FrameUnbind = UnbindFrameCb;
handle.CBAVC_Malloc = MallocCb;
handle.CBAVC_Free = FreeCb;
// Initialize the encoder.
AVCEnc_Status status;
status = PVAVCEncInitialize(&handle, &encParams, NULL, NULL);
if (status != AVCENC_SUCCESS) {
fprintf(stderr, "Failed to initialize the encoder\n");
// Release resources.
fclose(fpInput);
fclose(fpOutput);
free(sliceGroup);
free(inputBuf);
free(outputBuf);
for (int i = 0; i < kMaxDpbBuffers; ++i) {
free(dpbBuffers[i]);
}
return EXIT_FAILURE;
}
// Encode Sequence Parameter Set.
uint32_t dataLength = kOutputBufferSize;
int32_t type;
status = PVAVCEncodeNAL(&handle, outputBuf, &dataLength, &type);
assert(type == AVC_NALTYPE_SPS);
fwrite("\x00\x00\x00\x01", 1, 4, fpOutput); // Start Code.
fwrite(outputBuf, 1, dataLength, fpOutput); // SPS.
// Encode Picture Paramater Set.
dataLength = kOutputBufferSize;
status = PVAVCEncodeNAL(&handle, outputBuf, &dataLength, &type);
assert(type == AVC_NALTYPE_PPS);
fwrite("\x00\x00\x00\x01", 1, 4, fpOutput); // Start Code.
fwrite(outputBuf, 1, dataLength, fpOutput); // PPS.
// Core loop.
int32_t retVal = EXIT_SUCCESS;
int32_t frameSize = (width * height * 3) / 2;
int32_t numInputFrames = 0;
int32_t numNalEncoded = 0;
bool readyForNextFrame = true;
while (1) {
if (readyForNextFrame == true) {
// Read the input frame.
int32_t bytesRead;
bytesRead = fread(inputBuf, 1, frameSize, fpInput);
if (bytesRead != frameSize) {
break; // End of file.
}
// Set the input frame.
AVCFrameIO vin;
memset(&vin, 0, sizeof(vin));
vin.height = ((height + 15) >> 4) << 4;
vin.pitch = ((width + 15) >> 4) << 4;
vin.coding_timestamp = (numInputFrames * 1000) / frameRate; // in ms
vin.YCbCr[0] = inputBuf;
vin.YCbCr[1] = vin.YCbCr[0] + vin.height * vin.pitch;
vin.YCbCr[2] = vin.YCbCr[1] + ((vin.height * vin.pitch) >> 2);
vin.disp_order = numInputFrames;
status = PVAVCEncSetInput(&handle, &vin);
if (status == AVCENC_SUCCESS || status == AVCENC_NEW_IDR) {
readyForNextFrame = false;
++numInputFrames;
} else if (status < AVCENC_SUCCESS) {
fprintf(stderr, "Error %d while setting input frame\n", status);
retVal = EXIT_FAILURE;
break;
} else {
fprintf(stderr, "Frame drop\n");
readyForNextFrame = true;
++numInputFrames;
continue;
}
}
// Encode the input frame.
dataLength = kOutputBufferSize;
status = PVAVCEncodeNAL(&handle, outputBuf, &dataLength, &type);
if (status == AVCENC_SUCCESS) {
PVAVCEncGetOverrunBuffer(&handle);
} else if (status == AVCENC_PICTURE_READY) {
PVAVCEncGetOverrunBuffer(&handle);
readyForNextFrame = true;
AVCFrameIO recon;
if (PVAVCEncGetRecon(&handle, &recon) == AVCENC_SUCCESS) {
PVAVCEncReleaseRecon(&handle, &recon);
}
} else {
dataLength = 0;
readyForNextFrame = true;
}
if (status < AVCENC_SUCCESS) {
fprintf(stderr, "Error %d while encoding frame\n", status);
retVal = EXIT_FAILURE;
break;
}
numNalEncoded++;
// Write the output.
if (dataLength > 0) {
fwrite("\x00\x00\x00\x01", 1, 4, fpOutput); // Start Code.
fwrite(outputBuf, 1, dataLength, fpOutput); // NAL.
printf("NAL %d of size %d written\n", numNalEncoded, dataLength + 4);
}
}
// Close input and output file.
fclose(fpInput);
fclose(fpOutput);
// Free allocated memory.
free(sliceGroup);
free(inputBuf);
free(outputBuf);
for (int i = 0; i < kMaxDpbBuffers; ++i) {
free(dpbBuffers[i]);
}
// Close encoder instance.
PVAVCCleanUpEncoder(&handle);
return retVal;
}