1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <stdio.h>
30#include <stdint.h>
31#include <assert.h>
32#include <stdlib.h>
33
34#include "mp4enc_api.h"
35
36// Constants.
37enum {
38    kMaxWidth         = 720,
39    kMaxHeight        = 480,
40    kMaxFrameRate     = 30,
41    kMaxBitrate       = 2048, // in kbps.
42    kOutputBufferSize = 250 * 1024,
43    kIDRFrameRefreshIntervalInSec = 1, // in seconds.
44};
45
46int main(int argc, char *argv[]) {
47
48    if (argc < 8) {
49        fprintf(stderr, "Usage %s <input yuv> <output file> <mode> <width> "
50                        "<height> <frame rate> <bitrate in kbps>\n", argv[0]);
51        fprintf(stderr, "mode : h263 or mpeg4\n");
52        fprintf(stderr, "Max width %d\n", kMaxWidth);
53        fprintf(stderr, "Max height %d\n", kMaxHeight);
54        fprintf(stderr, "Max framerate %d\n", kMaxFrameRate);
55        fprintf(stderr, "Max bitrate %d kbps\n", kMaxBitrate);
56        return EXIT_FAILURE;
57    }
58
59    // Read mode.
60    bool isH263mode;
61    if (strcmp(argv[3], "mpeg4") == 0) {
62        isH263mode = false;
63    } else if (strcmp(argv[3], "h263") == 0) {
64        isH263mode = true;
65    } else {
66        fprintf(stderr, "Unsupported mode %s\n", argv[3]);
67        return EXIT_FAILURE;
68    }
69
70    // Read height and width.
71    int32_t width;
72    int32_t height;
73    width = atoi(argv[4]);
74    height = atoi(argv[5]);
75    if (width > kMaxWidth || height > kMaxHeight || width <= 0 || height <= 0) {
76        fprintf(stderr, "Unsupported dimensions %dx%d\n", width, height);
77        return EXIT_FAILURE;
78    }
79
80    if (width % 16 != 0 || height % 16 != 0) {
81        fprintf(stderr, "Video frame size %dx%d must be a multiple of 16\n",
82            width, height);
83        return EXIT_FAILURE;
84    }
85
86    // Read frame rate.
87    int32_t frameRate;
88    frameRate = atoi(argv[6]);
89    if (frameRate > kMaxFrameRate || frameRate <= 0) {
90        fprintf(stderr, "Unsupported frame rate %d\n", frameRate);
91        return EXIT_FAILURE;
92    }
93
94    // Read bitrate.
95    int32_t bitrate;
96    bitrate = atoi(argv[7]);
97    if (bitrate > kMaxBitrate || bitrate <= 0) {
98        fprintf(stderr, "Unsupported bitrate %d\n", bitrate);
99        return EXIT_FAILURE;
100    }
101
102    // Allocate input buffer.
103    uint8_t *inputBuf = (uint8_t *)malloc((width * height * 3) / 2);
104    assert(inputBuf != NULL);
105
106    // Allocate output buffer.
107    uint8_t *outputBuf = (uint8_t *)malloc(kOutputBufferSize);
108    assert(outputBuf != NULL);
109
110    // Open the input file.
111    FILE *fpInput = fopen(argv[1], "rb");
112    if (fpInput == NULL) {
113        fprintf(stderr, "Could not open %s\n", argv[1]);
114        free(inputBuf);
115        free(outputBuf);
116        return EXIT_FAILURE;
117    }
118
119    // Open the output file.
120    FILE *fpOutput = fopen(argv[2], "wb");
121    if (fpOutput == NULL) {
122        fprintf(stderr, "Could not open %s\n", argv[2]);
123        free(inputBuf);
124        free(outputBuf);
125        fclose(fpInput);
126        return EXIT_FAILURE;
127    }
128
129    // Initialize the encoder parameters.
130    tagvideoEncOptions encParams;
131    memset(&encParams, 0, sizeof(tagvideoEncOptions));
132    if (!PVGetDefaultEncOption(&encParams, 0)) {
133        fprintf(stderr, "Failed to get default encoding parameters\n");
134        free(inputBuf);
135        free(outputBuf);
136        fclose(fpInput);
137        fclose(fpOutput);
138        return EXIT_FAILURE;
139    }
140
141    if (isH263mode == false) {
142        encParams.encMode = COMBINE_MODE_WITH_ERR_RES;
143    } else {
144        encParams.encMode = H263_MODE;
145    }
146    encParams.encWidth[0] = width;
147    encParams.encHeight[0] = height;
148    encParams.encFrameRate[0] = frameRate;
149    encParams.rcType = VBR_1;
150    encParams.vbvDelay = 5.0f;
151    encParams.profile_level = CORE_PROFILE_LEVEL2;
152    encParams.packetSize = 32;
153    encParams.rvlcEnable = PV_OFF;
154    encParams.numLayers = 1;
155    encParams.timeIncRes = 1000;
156    encParams.tickPerSrc = encParams.timeIncRes / frameRate;
157
158    encParams.bitRate[0] = bitrate * 1024;
159    encParams.iQuant[0] = 15;
160    encParams.pQuant[0] = 12;
161    encParams.quantType[0] = 0;
162    encParams.noFrameSkipped = PV_OFF;
163
164    int32_t  IDRFrameRefreshIntervalInSec = kIDRFrameRefreshIntervalInSec;
165    if (IDRFrameRefreshIntervalInSec == 0) {
166        encParams.intraPeriod = 1;  // All I frames.
167    } else {
168        encParams.intraPeriod = (IDRFrameRefreshIntervalInSec * frameRate);
169    }
170
171    encParams.numIntraMB = 0;
172    encParams.sceneDetect = PV_ON;
173    encParams.searchRange = 16;
174    encParams.mv8x8Enable = PV_OFF;
175    encParams.gobHeaderInterval = 0;
176    encParams.useACPred = PV_ON;
177    encParams.intraDCVlcTh = 0;
178
179    // Initialize the handle.
180    tagvideoEncControls handle;
181    memset(&handle, 0, sizeof(tagvideoEncControls));
182
183    // Initialize the encoder.
184    if (!PVInitVideoEncoder(&handle, &encParams)) {
185        fprintf(stderr, "Failed to initialize the encoder\n");
186        return EXIT_FAILURE;
187    }
188
189    // Generate the header.
190    int32_t headerLength = kOutputBufferSize;
191    if (!PVGetVolHeader(&handle, outputBuf, &headerLength, 0)) {
192        fprintf(stderr, "Failed to get VOL header\n");
193        return EXIT_FAILURE;
194    }
195    fwrite(outputBuf, 1, headerLength, fpOutput);
196
197    // Core loop.
198    int32_t retVal = EXIT_SUCCESS;
199    int32_t frameSize = (width * height * 3) / 2;
200    int32_t numFramesEncoded = 0;
201
202    while (1) {
203        // Read the input frame.
204        int32_t bytesRead;
205        bytesRead = fread(inputBuf, 1, frameSize, fpInput);
206        if (bytesRead != frameSize) {
207            break; // End of file.
208        }
209
210        // Encode the input frame.
211        VideoEncFrameIO vin, vout;
212        memset(&vin, 0, sizeof(vin));
213        memset(&vout, 0, sizeof(vout));
214        vin.height = height; // height is multiple of 16.
215        vin.pitch = width; // width is multiple of 16.
216        vin.timestamp = (numFramesEncoded * 1000) / frameRate;  // in ms.
217        vin.yChan = inputBuf;
218        vin.uChan = vin.yChan + vin.height * vin.pitch;
219        vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2);
220
221        uint32_t modTimeMs = 0;
222        int32_t nLayer = 0;
223        MP4HintTrack hintTrack;
224        int32_t dataLength = kOutputBufferSize;
225        if (!PVEncodeVideoFrame(&handle, &vin, &vout,
226                &modTimeMs, outputBuf, &dataLength, &nLayer) ||
227            !PVGetHintTrack(&handle, &hintTrack)) {
228            fprintf(stderr, "Failed to encode frame or get hink track at "
229                    " frame %d\n", numFramesEncoded);
230            retVal = EXIT_FAILURE;
231            break;
232        }
233        PVGetOverrunBuffer(&handle);
234        numFramesEncoded++;
235
236        // Write the output.
237        fwrite(outputBuf, 1, dataLength, fpOutput);
238    }
239
240    // Close input and output file.
241    fclose(fpInput);
242    fclose(fpOutput);
243
244    // Free allocated memory.
245    free(inputBuf);
246    free(outputBuf);
247
248    // Close encoder instance.
249    PVCleanUpVideoEncoder(&handle);
250    return retVal;
251}
252