1/*
2 * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
3 * Copyright (c) Imagination Technologies Limited, UK
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 *    Elaine Wang <elaine.wang@intel.com>
27 *    Zeng Li <zeng.li@intel.com>
28 *
29 */
30
31#include "psb_def.h"
32#include "psb_drv_debug.h"
33#include "psb_surface.h"
34#include "psb_cmdbuf.h"
35#include "pnw_MPEG4ES.h"
36#include "pnw_hostcode.h"
37#include "pnw_hostheader.h"
38
39#include <stdlib.h>
40#include <stdint.h>
41#include <string.h>
42
43
44#define TOPAZ_MPEG4_MAX_BITRATE 16000000
45
46#define INIT_CONTEXT_MPEG4ES    context_ENC_p ctx = (context_ENC_p) obj_context->format_data
47#define SURFACE(id)    ((object_surface_p) object_heap_lookup( &ctx->obj_context->driver_data->surface_heap, id ))
48#define BUFFER(id)  ((object_buffer_p) object_heap_lookup( &ctx->obj_context->driver_data->buffer_heap, id ))
49
50
51
52static void pnw_MPEG4ES_QueryConfigAttributes(
53    VAProfile __maybe_unused profile,
54    VAEntrypoint __maybe_unused entrypoint,
55    VAConfigAttrib * attrib_list,
56    int num_attribs)
57{
58    int i;
59
60    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_QueryConfigAttributes\n");
61
62    /* RateControl attributes */
63    for (i = 0; i < num_attribs; i++) {
64        switch (attrib_list[i].type) {
65        case VAConfigAttribRTFormat:
66            break;
67
68        case VAConfigAttribRateControl:
69            attrib_list[i].value = VA_RC_NONE | VA_RC_CBR | VA_RC_VBR;
70            break;
71
72        default:
73            attrib_list[i].value = VA_ATTRIB_NOT_SUPPORTED;
74            break;
75        }
76    }
77
78    return;
79}
80
81
82static VAStatus pnw_MPEG4ES_ValidateConfig(
83    object_config_p obj_config)
84{
85    int i;
86    /* Check all attributes */
87    for (i = 0; i < obj_config->attrib_count; i++) {
88        switch (obj_config->attrib_list[i].type) {
89        case VAConfigAttribRTFormat:
90            /* Ignore */
91            break;
92        case VAConfigAttribRateControl:
93            break;
94        default:
95            return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED;
96        }
97    }
98
99    return VA_STATUS_SUCCESS;
100}
101
102
103static VAStatus pnw_MPEG4ES_CreateContext(
104    object_context_p obj_context,
105    object_config_p obj_config)
106{
107    VAStatus vaStatus = VA_STATUS_SUCCESS;
108    context_ENC_p ctx;
109    int i;
110    unsigned int eRCmode;
111
112    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_CreateContext\n");
113
114    vaStatus = pnw_CreateContext(obj_context, obj_config, 0);
115    if (VA_STATUS_SUCCESS != vaStatus)
116        return VA_STATUS_ERROR_ALLOCATION_FAILED;
117
118    ctx = (context_ENC_p) obj_context->format_data;
119
120    for (i = 0; i < obj_config->attrib_count; i++) {
121        if (obj_config->attrib_list[i].type == VAConfigAttribRateControl)
122            break;
123    }
124
125    if (i >= obj_config->attrib_count)
126        eRCmode = VA_RC_NONE;
127    else
128        eRCmode = obj_config->attrib_list[i].value;
129
130
131    if (eRCmode == VA_RC_VBR) {
132        ctx->eCodec = IMG_CODEC_MPEG4_VBR;
133        ctx->sRCParams.RCEnable = IMG_TRUE;
134    } else if (eRCmode == VA_RC_CBR) {
135        ctx->eCodec = IMG_CODEC_MPEG4_CBR;
136        ctx->sRCParams.RCEnable = IMG_TRUE;
137    } else if (eRCmode == VA_RC_NONE) {
138        ctx->eCodec = IMG_CODEC_MPEG4_NO_RC;
139        ctx->sRCParams.RCEnable = IMG_FALSE;
140    } else
141        return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT;
142    ctx->eFormat = IMG_CODEC_PL12;
143
144    ctx->Slices = 1;
145    ctx->ParallelCores = 1;
146
147    ctx->IPEControl = pnw__get_ipe_control(ctx->eCodec);
148
149    switch (obj_config->profile) {
150    case VAProfileMPEG4Simple:
151        ctx->profile_idc = 2;
152        break;
153    case VAProfileMPEG4AdvancedSimple:
154        ctx->profile_idc = 3;
155        break;
156    default:
157        ctx->profile_idc = 2;
158        break;
159    }
160
161    return vaStatus;
162}
163
164
165static void pnw_MPEG4ES_DestroyContext(
166    object_context_p obj_context)
167{
168    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_DestroyPicture\n");
169
170    pnw_DestroyContext(obj_context);
171}
172
173static VAStatus pnw_MPEG4ES_BeginPicture(
174    object_context_p obj_context)
175{
176    INIT_CONTEXT_MPEG4ES;
177    VAStatus vaStatus = VA_STATUS_SUCCESS;
178
179    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_BeginPicture\n");
180
181    vaStatus = pnw_BeginPicture(ctx);
182
183    return vaStatus;
184}
185
186static VAStatus pnw__MPEG4ES_process_sequence_param(context_ENC_p ctx, object_buffer_p obj_buffer)
187{
188    VAStatus vaStatus = VA_STATUS_SUCCESS;
189    VAEncSequenceParameterBufferMPEG4 *seq_params;
190    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
191    MPEG4_PROFILE_TYPE profile;
192    int i, vop_time_increment_resolution;
193    unsigned frame_size;
194
195    ASSERT(obj_buffer->type == VAEncSequenceParameterBufferType);
196    ASSERT(obj_buffer->num_elements == 1);
197    ASSERT(obj_buffer->size == sizeof(VAEncSequenceParameterBufferMPEG4));
198
199    //initialize the frame_rate and qp
200    ctx->sRCParams.InitialQp = 15;
201    ctx->sRCParams.MinQP = 1;
202    ctx->sRCParams.FrameRate = 30;
203
204    if ((obj_buffer->num_elements != 1) ||
205        (obj_buffer->size != sizeof(VAEncSequenceParameterBufferMPEG4))) {
206        return VA_STATUS_ERROR_UNKNOWN;
207    }
208
209    seq_params = (VAEncSequenceParameterBufferMPEG4 *) obj_buffer->buffer_data;
210    obj_buffer->buffer_data = NULL;
211    obj_buffer->size = 0;
212
213    if (seq_params->bits_per_second > TOPAZ_MPEG4_MAX_BITRATE) {
214        ctx->sRCParams.BitsPerSecond = TOPAZ_MPEG4_MAX_BITRATE;
215        drv_debug_msg(VIDEO_DEBUG_GENERAL, " bits_per_second(%d) exceeds \
216		the maximum bitrate, set it with %d\n",
217                                 seq_params->bits_per_second,
218                                 TOPAZ_MPEG4_MAX_BITRATE);
219    } else
220        ctx->sRCParams.BitsPerSecond = seq_params->bits_per_second;
221
222    ctx->sRCParams.FrameRate = (seq_params->frame_rate < 1) ?
223        1 : ((65535 < seq_params->frame_rate) ? 65535 : seq_params->frame_rate);
224    ctx->sRCParams.InitialQp = seq_params->initial_qp;
225    ctx->sRCParams.MinQP = seq_params->min_qp;
226    ctx->sRCParams.BUSize = 0;  /* default 0, and will be set in pnw__setup_busize */
227
228    ctx->sRCParams.Slices = 1;
229    ctx->sRCParams.QCPOffset = 0;/* FIXME */
230
231    if (ctx->sRCParams.IntraFreq != seq_params->intra_period
232            && ctx->raw_frame_count != 0
233            && ctx->sRCParams.IntraFreq != 0
234            && ((ctx->obj_context->frame_count + 1) % ctx->sRCParams.IntraFreq) != 0
235            && (!ctx->sRCParams.bDisableFrameSkipping)) {
236        drv_debug_msg(VIDEO_DEBUG_ERROR,
237                "Changing intra period value in the middle of a GOP is\n"
238                "not allowed if frame skip isn't disabled.\n"
239                "it can cause I frame been skipped\n");
240        free(seq_params);
241        return VA_STATUS_ERROR_INVALID_PARAMETER;
242    }
243    else
244        ctx->sRCParams.IntraFreq = seq_params->intra_period;
245
246    ctx->sRCParams.IntraFreq = seq_params->intra_period;
247
248    frame_size = ctx->sRCParams.BitsPerSecond / ctx->sRCParams.FrameRate;
249
250    ctx->sRCParams.BufferSize = ctx->sRCParams.BitsPerSecond;
251    /* Header buffersize is specified in 16384 units, so ensure conformance
252       of parameters. InitialLevel in units of 64, assured by this */
253
254    ctx->sRCParams.BufferSize /= 16384;
255    ctx->sRCParams.BufferSize *= 16384;
256
257    ctx->sRCParams.InitialLevel = (3 * ctx->sRCParams.BufferSize) >> 4;
258    /* Aligned with target frame size */
259    ctx->sRCParams.InitialLevel += (frame_size / 2);
260    ctx->sRCParams.InitialLevel /= frame_size;
261    ctx->sRCParams.InitialLevel *= frame_size;
262    ctx->sRCParams.InitialDelay = ctx->sRCParams.BufferSize - ctx->sRCParams.InitialLevel;
263    ctx->buffer_size = ctx->sRCParams.BufferSize;
264
265    if (ctx->raw_frame_count == 0) { /* Add Register IO behind begin Picture */
266        for (i = (ctx->ParallelCores - 1); i >= 0; i--)
267            pnw_set_bias(ctx, i);
268    }
269
270    cmdbuf = ctx->obj_context->pnw_cmdbuf;
271
272    switch (ctx->profile_idc) {
273    case 2:
274        profile = SP;
275        break;
276    case 3:
277        profile = ASP;
278        break;
279    default:
280        profile = SP;
281        break;
282    }
283
284    memset(cmdbuf->header_mem_p + ctx->seq_header_ofs,
285           0,
286           HEADER_SIZE);
287
288    vop_time_increment_resolution = (seq_params->vop_time_increment_resolution < 1) ? 1 :
289        ((65535 < seq_params->vop_time_increment_resolution) ? 65535 : seq_params->vop_time_increment_resolution);
290    pnw__MPEG4_prepare_sequence_header(
291        cmdbuf->header_mem_p + ctx->seq_header_ofs,
292        0, /* BFrame? */
293        profile, /* sProfile */
294        seq_params->profile_and_level_indication, /* */
295        seq_params->fixed_vop_time_increment, /*3,*/  /* sFixed_vop_time_increment */
296        seq_params->video_object_layer_width,/* Picture_Width_Pixels */
297        seq_params->video_object_layer_height, /* Picture_Height_Pixels */
298        NULL,
299        vop_time_increment_resolution); /* VopTimeResolution */
300
301    ctx->MPEG4_vop_time_increment_resolution = vop_time_increment_resolution;
302
303    pnw_cmdbuf_insert_command_package(ctx->obj_context,
304                                      ctx->ParallelCores - 1, /* Send to the last core as this will complete first */
305                                      MTX_CMDID_DO_HEADER,
306                                      &cmdbuf->header_mem,
307                                      ctx->seq_header_ofs);
308
309    free(seq_params);
310    return vaStatus;
311}
312
313
314static VAStatus pnw__MPEG4ES_process_picture_param(context_ENC_p ctx, object_buffer_p obj_buffer)
315{
316    VAStatus vaStatus = VA_STATUS_SUCCESS;
317    VAEncPictureParameterBufferMPEG4 *pBuffer;
318    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
319    unsigned int *pPictureHeaderMem;
320    MTX_HEADER_PARAMS *psPicHeader;
321    int i;
322    IMG_BOOL bIsVOPCoded = IMG_TRUE;
323
324    ASSERT(obj_buffer->type == VAEncPictureParameterBufferType);
325
326    if ((obj_buffer->num_elements != 1) ||
327        (obj_buffer->size != sizeof(VAEncPictureParameterBufferMPEG4))) {
328        return VA_STATUS_ERROR_UNKNOWN;
329    }
330
331    /* Transfer ownership of VAEncPictureParameterBufferMPEG4 data */
332    pBuffer = (VAEncPictureParameterBufferMPEG4 *) obj_buffer->buffer_data;
333    obj_buffer->buffer_data = NULL;
334    obj_buffer->size = 0;
335
336    ctx->ref_surface = SURFACE(pBuffer->reference_picture);
337    ctx->dest_surface = SURFACE(pBuffer->reconstructed_picture);
338    ctx->coded_buf = BUFFER(pBuffer->coded_buf);
339
340    ASSERT(ctx->Width == pBuffer->picture_width);
341    ASSERT(ctx->Height == pBuffer->picture_height);
342
343    /*if (ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip)
344        bIsVOPCoded = IMG_FALSE;*/
345
346    ctx->FCode = 4 - 1; /* 4 is default value of "ui8Search_range" */
347
348    pPictureHeaderMem = (unsigned int *)(cmdbuf->header_mem_p + ctx->pic_header_ofs);
349    psPicHeader = (MTX_HEADER_PARAMS *)pPictureHeaderMem;
350
351    memset(pPictureHeaderMem, 0, HEADER_SIZE);
352
353    pnw__MPEG4_prepare_vop_header((unsigned char *)pPictureHeaderMem,
354                                  bIsVOPCoded,
355                                  pBuffer->vop_time_increment, /* In testbench, this should be FrameNum */
356                                  4,/* default value is 4,search range */
357                                  pBuffer->picture_type,
358                                  ctx->MPEG4_vop_time_increment_resolution/* defaule value */);
359
360    /* Mark this header as a complex header */
361    psPicHeader->Elements |= 0x100;
362    pPictureHeaderMem += ((HEADER_SIZE)  >> 3);
363
364    pnw__MPEG4_prepare_vop_header((unsigned char *)pPictureHeaderMem,
365                                  IMG_FALSE,
366                                  pBuffer->vop_time_increment, /* In testbench, this should be FrameNum */
367                                  4,/* default value is 4,search range */
368                                  pBuffer->picture_type,
369                                  ctx->MPEG4_vop_time_increment_resolution/* defaule value */);
370
371    pnw_cmdbuf_insert_command_package(ctx->obj_context,
372                                      ctx->ParallelCores - 1, /* Send to the last core as this will complete first */
373                                      MTX_CMDID_DO_HEADER,
374                                      &cmdbuf->header_mem,
375                                      ctx->pic_header_ofs);
376
377    /* Prepare START_PICTURE params */
378    for (i = (ctx->ParallelCores - 1); i >= 0; i--)
379        vaStatus = pnw_RenderPictureParameter(ctx, i);
380
381    free(pBuffer);
382    return vaStatus;
383}
384
385static VAStatus pnw__MPEG4ES_process_slice_param(context_ENC_p ctx, object_buffer_p obj_buffer)
386{
387    VAStatus vaStatus = VA_STATUS_SUCCESS;
388    VAEncSliceParameterBuffer *pBuffer;
389    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
390    PIC_PARAMS *psPicParams = (PIC_PARAMS *)(cmdbuf->pic_params_p);
391    unsigned int i;
392    int slice_param_idx;
393
394    ASSERT(obj_buffer->type == VAEncSliceParameterBufferType);
395
396    pBuffer = (VAEncSliceParameterBuffer *) obj_buffer->buffer_data;
397
398    /*In case the slice number changes*/
399    if ((ctx->slice_param_cache != NULL) && (obj_buffer->num_elements != ctx->slice_param_num)) {
400        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Slice number changes. Previous value is %d. Now it's %d\n",
401                                 ctx->slice_param_num, obj_buffer->num_elements);
402        free(ctx->slice_param_cache);
403        ctx->slice_param_cache = NULL;
404        ctx->slice_param_num = 0;
405    }
406
407    if (NULL == ctx->slice_param_cache) {
408        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Allocate %d VAEncSliceParameterBuffer cache buffers\n", 2 * ctx->slice_param_num);
409        ctx->slice_param_num = obj_buffer->num_elements;
410        ctx->slice_param_cache = calloc(2 * ctx->slice_param_num, sizeof(VAEncSliceParameterBuffer));
411        if (NULL == ctx->slice_param_cache) {
412            drv_debug_msg(VIDEO_DEBUG_ERROR, "Run out of memory!\n");
413            free(obj_buffer->buffer_data);
414            return VA_STATUS_ERROR_ALLOCATION_FAILED;
415        }
416    }
417
418
419    for (i = 0; i < obj_buffer->num_elements; i++) {
420
421        unsigned char deblock_idc;
422
423        deblock_idc = pBuffer->slice_flags.bits.disable_deblocking_filter_idc;
424
425        if ((pBuffer->start_row_number == 0) && pBuffer->slice_flags.bits.is_intra) {
426            pnw_reset_encoder_params(ctx);
427            ctx->BelowParamsBufIdx = (ctx->BelowParamsBufIdx + 1) & 0x1;
428        }
429
430        /*The corresponding slice buffer cache*/
431        slice_param_idx = (pBuffer->slice_flags.bits.is_intra ? 0 : 1) * ctx->slice_param_num + i;
432
433        if (VAEncSliceParameter_Equal(&ctx->slice_param_cache[slice_param_idx], pBuffer) == 0) {
434            /* cache current param parameters */
435            memcpy(&ctx->slice_param_cache[slice_param_idx],
436                   pBuffer, sizeof(VAEncSliceParameterBuffer));
437
438            /* Setup InParams value*/
439            pnw_setup_slice_params(ctx,
440                                   pBuffer->start_row_number * 16,
441                                   pBuffer->slice_height * 16,
442                                   pBuffer->slice_flags.bits.is_intra,
443                                   ctx->obj_context->frame_count > 0,
444                                   psPicParams->sInParams.SeInitQP);
445        }
446
447        pnw__send_encode_slice_params(ctx,
448                                      pBuffer->slice_flags.bits.is_intra,
449                                      pBuffer->start_row_number * 16,
450                                      deblock_idc,
451                                      ctx->obj_context->frame_count,
452                                      pBuffer->slice_height * 16,
453                                      ctx->obj_context->slice_count);
454
455        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Now frame_count/slice_count is %d/%d\n",
456                                 ctx->obj_context->frame_count, ctx->obj_context->slice_count);
457
458        ctx->obj_context->slice_count++;
459        pBuffer++;
460
461        ASSERT(ctx->obj_context->slice_count < MAX_SLICES_PER_PICTURE);
462    }
463
464    free(obj_buffer->buffer_data);
465    obj_buffer->buffer_data = NULL;
466
467    return vaStatus;
468}
469
470
471static VAStatus pnw_MPEG4ES_RenderPicture(
472    object_context_p obj_context,
473    object_buffer_p *buffers,
474    int num_buffers)
475{
476    INIT_CONTEXT_MPEG4ES;
477    VAStatus vaStatus = VA_STATUS_SUCCESS;
478    int i;
479
480    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture\n");
481
482    for (i = 0; i < num_buffers; i++) {
483        object_buffer_p obj_buffer = buffers[i];
484
485        switch (obj_buffer->type) {
486        case VAEncSequenceParameterBufferType:
487            drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture got VAEncSequenceParameterBufferType\n");
488            vaStatus = pnw__MPEG4ES_process_sequence_param(ctx, obj_buffer);
489            DEBUG_FAILURE;
490            break;
491
492        case VAEncPictureParameterBufferType:
493            drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture got VAEncPictureParameterBufferType\n");
494            vaStatus = pnw__MPEG4ES_process_picture_param(ctx, obj_buffer);
495            DEBUG_FAILURE;
496            break;
497
498        case VAEncSliceParameterBufferType:
499            drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture got VAEncSliceParameterBufferType\n");
500            vaStatus = pnw__MPEG4ES_process_slice_param(ctx, obj_buffer);
501            DEBUG_FAILURE;
502            break;
503        default:
504            vaStatus = VA_STATUS_ERROR_UNKNOWN;
505            DEBUG_FAILURE;
506        }
507    }
508
509    return vaStatus;
510}
511
512static VAStatus pnw_MPEG4ES_EndPicture(
513    object_context_p obj_context)
514{
515    INIT_CONTEXT_MPEG4ES;
516
517    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_EndPicture\n");
518    return pnw_EndPicture(ctx);
519}
520
521
522struct format_vtable_s pnw_MPEG4ES_vtable = {
523queryConfigAttributes:
524    pnw_MPEG4ES_QueryConfigAttributes,
525validateConfig:
526    pnw_MPEG4ES_ValidateConfig,
527createContext:
528    pnw_MPEG4ES_CreateContext,
529destroyContext:
530    pnw_MPEG4ES_DestroyContext,
531beginPicture:
532    pnw_MPEG4ES_BeginPicture,
533renderPicture:
534    pnw_MPEG4ES_RenderPicture,
535endPicture:
536    pnw_MPEG4ES_EndPicture
537};
538