/* * 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 #include #include #include #include "mp4enc_api.h" // Constants. enum { kMaxWidth = 720, kMaxHeight = 480, kMaxFrameRate = 30, kMaxBitrate = 2048, // in kbps. kOutputBufferSize = 250 * 1024, kIDRFrameRefreshIntervalInSec = 1, // in seconds. }; int main(int argc, char *argv[]) { if (argc < 8) { fprintf(stderr, "Usage %s " " \n", argv[0]); fprintf(stderr, "mode : h263 or mpeg4\n"); 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 mode. bool isH263mode; if (strcmp(argv[3], "mpeg4") == 0) { isH263mode = false; } else if (strcmp(argv[3], "h263") == 0) { isH263mode = true; } else { fprintf(stderr, "Unsupported mode %s\n", argv[3]); return EXIT_FAILURE; } // Read height and width. int32_t width; int32_t height; width = atoi(argv[4]); height = atoi(argv[5]); 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[6]); if (frameRate > kMaxFrameRate || frameRate <= 0) { fprintf(stderr, "Unsupported frame rate %d\n", frameRate); return EXIT_FAILURE; } // Read bitrate. int32_t bitrate; bitrate = atoi(argv[7]); if (bitrate > kMaxBitrate || bitrate <= 0) { fprintf(stderr, "Unsupported bitrate %d\n", bitrate); return EXIT_FAILURE; } // Allocate input buffer. uint8_t *inputBuf = (uint8_t *)malloc((width * height * 3) / 2); assert(inputBuf != NULL); // Allocate output buffer. uint8_t *outputBuf = (uint8_t *)malloc(kOutputBufferSize); assert(outputBuf != NULL); // Open the input file. FILE *fpInput = fopen(argv[1], "rb"); if (fpInput == NULL) { fprintf(stderr, "Could not open %s\n", argv[1]); free(inputBuf); free(outputBuf); return EXIT_FAILURE; } // Open the output file. FILE *fpOutput = fopen(argv[2], "wb"); if (fpOutput == NULL) { fprintf(stderr, "Could not open %s\n", argv[2]); free(inputBuf); free(outputBuf); fclose(fpInput); return EXIT_FAILURE; } // Initialize the encoder parameters. tagvideoEncOptions encParams; memset(&encParams, 0, sizeof(tagvideoEncOptions)); if (!PVGetDefaultEncOption(&encParams, 0)) { fprintf(stderr, "Failed to get default encoding parameters\n"); free(inputBuf); free(outputBuf); fclose(fpInput); fclose(fpOutput); return EXIT_FAILURE; } if (isH263mode == false) { encParams.encMode = COMBINE_MODE_WITH_ERR_RES; } else { encParams.encMode = H263_MODE; } encParams.encWidth[0] = width; encParams.encHeight[0] = height; encParams.encFrameRate[0] = frameRate; encParams.rcType = VBR_1; encParams.vbvDelay = 5.0f; encParams.profile_level = CORE_PROFILE_LEVEL2; encParams.packetSize = 32; encParams.rvlcEnable = PV_OFF; encParams.numLayers = 1; encParams.timeIncRes = 1000; encParams.tickPerSrc = encParams.timeIncRes / frameRate; encParams.bitRate[0] = bitrate * 1024; encParams.iQuant[0] = 15; encParams.pQuant[0] = 12; encParams.quantType[0] = 0; encParams.noFrameSkipped = PV_OFF; int32_t IDRFrameRefreshIntervalInSec = kIDRFrameRefreshIntervalInSec; if (IDRFrameRefreshIntervalInSec == 0) { encParams.intraPeriod = 1; // All I frames. } else { encParams.intraPeriod = (IDRFrameRefreshIntervalInSec * frameRate); } encParams.numIntraMB = 0; encParams.sceneDetect = PV_ON; encParams.searchRange = 16; encParams.mv8x8Enable = PV_OFF; encParams.gobHeaderInterval = 0; encParams.useACPred = PV_ON; encParams.intraDCVlcTh = 0; // Initialize the handle. tagvideoEncControls handle; memset(&handle, 0, sizeof(tagvideoEncControls)); // Initialize the encoder. if (!PVInitVideoEncoder(&handle, &encParams)) { fprintf(stderr, "Failed to initialize the encoder\n"); return EXIT_FAILURE; } // Generate the header. int32_t headerLength = kOutputBufferSize; if (!PVGetVolHeader(&handle, outputBuf, &headerLength, 0)) { fprintf(stderr, "Failed to get VOL header\n"); return EXIT_FAILURE; } fwrite(outputBuf, 1, headerLength, fpOutput); // Core loop. int32_t retVal = EXIT_SUCCESS; int32_t frameSize = (width * height * 3) / 2; int32_t numFramesEncoded = 0; while (1) { // Read the input frame. int32_t bytesRead; bytesRead = fread(inputBuf, 1, frameSize, fpInput); if (bytesRead != frameSize) { break; // End of file. } // Encode the input frame. VideoEncFrameIO vin, vout; memset(&vin, 0, sizeof(vin)); memset(&vout, 0, sizeof(vout)); vin.height = height; // height is multiple of 16. vin.pitch = width; // width is multiple of 16. vin.timestamp = (numFramesEncoded * 1000) / frameRate; // in ms. vin.yChan = inputBuf; vin.uChan = vin.yChan + vin.height * vin.pitch; vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2); uint32_t modTimeMs = 0; int32_t nLayer = 0; MP4HintTrack hintTrack; int32_t dataLength = kOutputBufferSize; if (!PVEncodeVideoFrame(&handle, &vin, &vout, &modTimeMs, outputBuf, &dataLength, &nLayer) || !PVGetHintTrack(&handle, &hintTrack)) { fprintf(stderr, "Failed to encode frame or get hink track at " " frame %d\n", numFramesEncoded); retVal = EXIT_FAILURE; break; } PVGetOverrunBuffer(&handle); numFramesEncoded++; // Write the output. fwrite(outputBuf, 1, dataLength, fpOutput); } // Close input and output file. fclose(fpInput); fclose(fpOutput); // Free allocated memory. free(inputBuf); free(outputBuf); // Close encoder instance. PVCleanUpVideoEncoder(&handle); return retVal; }