1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * - CrystalHD decoder module -
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * Copyright(C) 2010,2011 Philip Langdale <ffmpeg.philipl@overt.org>
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * This file is part of FFmpeg.
7cabdff1aSopenharmony_ci *
8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
12cabdff1aSopenharmony_ci *
13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16cabdff1aSopenharmony_ci * Lesser General Public License for more details.
17cabdff1aSopenharmony_ci *
18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21cabdff1aSopenharmony_ci */
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci/*
24cabdff1aSopenharmony_ci * - Principles of Operation -
25cabdff1aSopenharmony_ci *
26cabdff1aSopenharmony_ci * The CrystalHD decoder operates at the bitstream level - which is an even
27cabdff1aSopenharmony_ci * higher level than the decoding hardware you typically see in modern GPUs.
28cabdff1aSopenharmony_ci * This means it has a very simple interface, in principle. You feed demuxed
29cabdff1aSopenharmony_ci * packets in one end and get decoded picture (fields/frames) out the other.
30cabdff1aSopenharmony_ci *
31cabdff1aSopenharmony_ci * Of course, nothing is ever that simple. Due, at the very least, to b-frame
32cabdff1aSopenharmony_ci * dependencies in the supported formats, the hardware has a delay between
33cabdff1aSopenharmony_ci * when a packet goes in, and when a picture comes out. Furthermore, this delay
34cabdff1aSopenharmony_ci * is not just a function of time, but also one of the dependency on additional
35cabdff1aSopenharmony_ci * frames being fed into the decoder to satisfy the b-frame dependencies.
36cabdff1aSopenharmony_ci *
37cabdff1aSopenharmony_ci * As such, the hardware can only be used effectively with a decode API that
38cabdff1aSopenharmony_ci * doesn't assume a 1:1 relationship between input packets and output frames.
39cabdff1aSopenharmony_ci * The new avcodec decode API is such an API (an m:n API) while the old one is
40cabdff1aSopenharmony_ci * 1:1. Consequently, we no longer support the old API, which allows us to avoid
41cabdff1aSopenharmony_ci * the vicious hacks that are required to approximate 1:1 operation.
42cabdff1aSopenharmony_ci */
43cabdff1aSopenharmony_ci
44cabdff1aSopenharmony_ci/*****************************************************************************
45cabdff1aSopenharmony_ci * Includes
46cabdff1aSopenharmony_ci ****************************************************************************/
47cabdff1aSopenharmony_ci
48cabdff1aSopenharmony_ci#include "config_components.h"
49cabdff1aSopenharmony_ci
50cabdff1aSopenharmony_ci#define _XOPEN_SOURCE 600
51cabdff1aSopenharmony_ci#include <inttypes.h>
52cabdff1aSopenharmony_ci#include <stdio.h>
53cabdff1aSopenharmony_ci#include <stdlib.h>
54cabdff1aSopenharmony_ci
55cabdff1aSopenharmony_ci#include <libcrystalhd/bc_dts_types.h>
56cabdff1aSopenharmony_ci#include <libcrystalhd/bc_dts_defs.h>
57cabdff1aSopenharmony_ci#include <libcrystalhd/libcrystalhd_if.h>
58cabdff1aSopenharmony_ci
59cabdff1aSopenharmony_ci#include "avcodec.h"
60cabdff1aSopenharmony_ci#include "codec_internal.h"
61cabdff1aSopenharmony_ci#include "decode.h"
62cabdff1aSopenharmony_ci#include "internal.h"
63cabdff1aSopenharmony_ci#include "libavutil/imgutils.h"
64cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
65cabdff1aSopenharmony_ci#include "libavutil/opt.h"
66cabdff1aSopenharmony_ci
67cabdff1aSopenharmony_ci#if HAVE_UNISTD_H
68cabdff1aSopenharmony_ci#include <unistd.h>
69cabdff1aSopenharmony_ci#endif
70cabdff1aSopenharmony_ci
71cabdff1aSopenharmony_ci/** Timeout parameter passed to DtsProcOutput() in us */
72cabdff1aSopenharmony_ci#define OUTPUT_PROC_TIMEOUT 50
73cabdff1aSopenharmony_ci/** Step between fake timestamps passed to hardware in units of 100ns */
74cabdff1aSopenharmony_ci#define TIMESTAMP_UNIT 100000
75cabdff1aSopenharmony_ci
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci/*****************************************************************************
78cabdff1aSopenharmony_ci * Module private data
79cabdff1aSopenharmony_ci ****************************************************************************/
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_citypedef enum {
82cabdff1aSopenharmony_ci    RET_ERROR           = -1,
83cabdff1aSopenharmony_ci    RET_OK              = 0,
84cabdff1aSopenharmony_ci    RET_COPY_AGAIN      = 1,
85cabdff1aSopenharmony_ci} CopyRet;
86cabdff1aSopenharmony_ci
87cabdff1aSopenharmony_citypedef struct OpaqueList {
88cabdff1aSopenharmony_ci    struct OpaqueList *next;
89cabdff1aSopenharmony_ci    uint64_t fake_timestamp;
90cabdff1aSopenharmony_ci    uint64_t reordered_opaque;
91cabdff1aSopenharmony_ci} OpaqueList;
92cabdff1aSopenharmony_ci
93cabdff1aSopenharmony_citypedef struct {
94cabdff1aSopenharmony_ci    AVClass *av_class;
95cabdff1aSopenharmony_ci    AVCodecContext *avctx;
96cabdff1aSopenharmony_ci    /* This packet coincides with AVCodecInternal.in_pkt
97cabdff1aSopenharmony_ci     * and is not owned by us. */
98cabdff1aSopenharmony_ci    AVPacket *pkt;
99cabdff1aSopenharmony_ci    HANDLE dev;
100cabdff1aSopenharmony_ci
101cabdff1aSopenharmony_ci    uint8_t is_70012;
102cabdff1aSopenharmony_ci    uint8_t need_second_field;
103cabdff1aSopenharmony_ci    uint8_t draining;
104cabdff1aSopenharmony_ci
105cabdff1aSopenharmony_ci    OpaqueList *head;
106cabdff1aSopenharmony_ci    OpaqueList *tail;
107cabdff1aSopenharmony_ci
108cabdff1aSopenharmony_ci    /* Options */
109cabdff1aSopenharmony_ci    uint32_t sWidth;
110cabdff1aSopenharmony_ci} CHDContext;
111cabdff1aSopenharmony_ci
112cabdff1aSopenharmony_cistatic const AVOption options[] = {
113cabdff1aSopenharmony_ci    { "crystalhd_downscale_width",
114cabdff1aSopenharmony_ci      "Turn on downscaling to the specified width",
115cabdff1aSopenharmony_ci      offsetof(CHDContext, sWidth),
116cabdff1aSopenharmony_ci      AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT32_MAX,
117cabdff1aSopenharmony_ci      AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM, },
118cabdff1aSopenharmony_ci    { NULL, },
119cabdff1aSopenharmony_ci};
120cabdff1aSopenharmony_ci
121cabdff1aSopenharmony_ci
122cabdff1aSopenharmony_ci/*****************************************************************************
123cabdff1aSopenharmony_ci * Helper functions
124cabdff1aSopenharmony_ci ****************************************************************************/
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_cistatic inline BC_MEDIA_SUBTYPE id2subtype(CHDContext *priv, enum AVCodecID id)
127cabdff1aSopenharmony_ci{
128cabdff1aSopenharmony_ci    switch (id) {
129cabdff1aSopenharmony_ci    case AV_CODEC_ID_MPEG4:
130cabdff1aSopenharmony_ci        return BC_MSUBTYPE_DIVX;
131cabdff1aSopenharmony_ci    case AV_CODEC_ID_MSMPEG4V3:
132cabdff1aSopenharmony_ci        return BC_MSUBTYPE_DIVX311;
133cabdff1aSopenharmony_ci    case AV_CODEC_ID_MPEG2VIDEO:
134cabdff1aSopenharmony_ci        return BC_MSUBTYPE_MPEG2VIDEO;
135cabdff1aSopenharmony_ci    case AV_CODEC_ID_VC1:
136cabdff1aSopenharmony_ci        return BC_MSUBTYPE_VC1;
137cabdff1aSopenharmony_ci    case AV_CODEC_ID_WMV3:
138cabdff1aSopenharmony_ci        return BC_MSUBTYPE_WMV3;
139cabdff1aSopenharmony_ci    case AV_CODEC_ID_H264:
140cabdff1aSopenharmony_ci        return BC_MSUBTYPE_H264;
141cabdff1aSopenharmony_ci    default:
142cabdff1aSopenharmony_ci        return BC_MSUBTYPE_INVALID;
143cabdff1aSopenharmony_ci    }
144cabdff1aSopenharmony_ci}
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_cistatic inline void print_frame_info(CHDContext *priv, BC_DTS_PROC_OUT *output)
147cabdff1aSopenharmony_ci{
148cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tYBuffSz: %u\n", output->YbuffSz);
149cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tYBuffDoneSz: %u\n",
150cabdff1aSopenharmony_ci           output->YBuffDoneSz);
151cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tUVBuffDoneSz: %u\n",
152cabdff1aSopenharmony_ci           output->UVBuffDoneSz);
153cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tTimestamp: %"PRIu64"\n",
154cabdff1aSopenharmony_ci           output->PicInfo.timeStamp);
155cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tPicture Number: %u\n",
156cabdff1aSopenharmony_ci           output->PicInfo.picture_number);
157cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tWidth: %u\n",
158cabdff1aSopenharmony_ci           output->PicInfo.width);
159cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tHeight: %u\n",
160cabdff1aSopenharmony_ci           output->PicInfo.height);
161cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tChroma: 0x%03x\n",
162cabdff1aSopenharmony_ci           output->PicInfo.chroma_format);
163cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tPulldown: %u\n",
164cabdff1aSopenharmony_ci           output->PicInfo.pulldown);
165cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tFlags: 0x%08x\n",
166cabdff1aSopenharmony_ci           output->PicInfo.flags);
167cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tFrame Rate/Res: %u\n",
168cabdff1aSopenharmony_ci           output->PicInfo.frame_rate);
169cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tAspect Ratio: %u\n",
170cabdff1aSopenharmony_ci           output->PicInfo.aspect_ratio);
171cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tColor Primaries: %u\n",
172cabdff1aSopenharmony_ci           output->PicInfo.colour_primaries);
173cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tMetaData: %u\n",
174cabdff1aSopenharmony_ci           output->PicInfo.picture_meta_payload);
175cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tSession Number: %u\n",
176cabdff1aSopenharmony_ci           output->PicInfo.sess_num);
177cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tycom: %u\n",
178cabdff1aSopenharmony_ci           output->PicInfo.ycom);
179cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tCustom Aspect: %u\n",
180cabdff1aSopenharmony_ci           output->PicInfo.custom_aspect_ratio_width_height);
181cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tFrames to Drop: %u\n",
182cabdff1aSopenharmony_ci           output->PicInfo.n_drop);
183cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_TRACE, "\tH264 Valid Fields: 0x%08x\n",
184cabdff1aSopenharmony_ci           output->PicInfo.other.h264.valid);
185cabdff1aSopenharmony_ci}
186cabdff1aSopenharmony_ci
187cabdff1aSopenharmony_ci
188cabdff1aSopenharmony_ci/*****************************************************************************
189cabdff1aSopenharmony_ci * OpaqueList functions
190cabdff1aSopenharmony_ci ****************************************************************************/
191cabdff1aSopenharmony_ci
192cabdff1aSopenharmony_cistatic uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque)
193cabdff1aSopenharmony_ci{
194cabdff1aSopenharmony_ci    OpaqueList *newNode = av_mallocz(sizeof (OpaqueList));
195cabdff1aSopenharmony_ci    if (!newNode) {
196cabdff1aSopenharmony_ci        av_log(priv->avctx, AV_LOG_ERROR,
197cabdff1aSopenharmony_ci               "Unable to allocate new node in OpaqueList.\n");
198cabdff1aSopenharmony_ci        return 0;
199cabdff1aSopenharmony_ci    }
200cabdff1aSopenharmony_ci    if (!priv->head) {
201cabdff1aSopenharmony_ci        newNode->fake_timestamp = TIMESTAMP_UNIT;
202cabdff1aSopenharmony_ci        priv->head              = newNode;
203cabdff1aSopenharmony_ci    } else {
204cabdff1aSopenharmony_ci        newNode->fake_timestamp = priv->tail->fake_timestamp + TIMESTAMP_UNIT;
205cabdff1aSopenharmony_ci        priv->tail->next        = newNode;
206cabdff1aSopenharmony_ci    }
207cabdff1aSopenharmony_ci    priv->tail = newNode;
208cabdff1aSopenharmony_ci    newNode->reordered_opaque = reordered_opaque;
209cabdff1aSopenharmony_ci
210cabdff1aSopenharmony_ci    return newNode->fake_timestamp;
211cabdff1aSopenharmony_ci}
212cabdff1aSopenharmony_ci
213cabdff1aSopenharmony_ci/*
214cabdff1aSopenharmony_ci * The OpaqueList is built in decode order, while elements will be removed
215cabdff1aSopenharmony_ci * in presentation order. If frames are reordered, this means we must be
216cabdff1aSopenharmony_ci * able to remove elements that are not the first element.
217cabdff1aSopenharmony_ci *
218cabdff1aSopenharmony_ci * Returned node must be freed by caller.
219cabdff1aSopenharmony_ci */
220cabdff1aSopenharmony_cistatic OpaqueList *opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp)
221cabdff1aSopenharmony_ci{
222cabdff1aSopenharmony_ci    OpaqueList *node = priv->head;
223cabdff1aSopenharmony_ci
224cabdff1aSopenharmony_ci    if (!priv->head) {
225cabdff1aSopenharmony_ci        av_log(priv->avctx, AV_LOG_ERROR,
226cabdff1aSopenharmony_ci               "CrystalHD: Attempted to query non-existent timestamps.\n");
227cabdff1aSopenharmony_ci        return NULL;
228cabdff1aSopenharmony_ci    }
229cabdff1aSopenharmony_ci
230cabdff1aSopenharmony_ci    /*
231cabdff1aSopenharmony_ci     * The first element is special-cased because we have to manipulate
232cabdff1aSopenharmony_ci     * the head pointer rather than the previous element in the list.
233cabdff1aSopenharmony_ci     */
234cabdff1aSopenharmony_ci    if (priv->head->fake_timestamp == fake_timestamp) {
235cabdff1aSopenharmony_ci        priv->head = node->next;
236cabdff1aSopenharmony_ci
237cabdff1aSopenharmony_ci        if (!priv->head->next)
238cabdff1aSopenharmony_ci            priv->tail = priv->head;
239cabdff1aSopenharmony_ci
240cabdff1aSopenharmony_ci        node->next = NULL;
241cabdff1aSopenharmony_ci        return node;
242cabdff1aSopenharmony_ci    }
243cabdff1aSopenharmony_ci
244cabdff1aSopenharmony_ci    /*
245cabdff1aSopenharmony_ci     * The list is processed at arm's length so that we have the
246cabdff1aSopenharmony_ci     * previous element available to rewrite its next pointer.
247cabdff1aSopenharmony_ci     */
248cabdff1aSopenharmony_ci    while (node->next) {
249cabdff1aSopenharmony_ci        OpaqueList *current = node->next;
250cabdff1aSopenharmony_ci        if (current->fake_timestamp == fake_timestamp) {
251cabdff1aSopenharmony_ci            node->next = current->next;
252cabdff1aSopenharmony_ci
253cabdff1aSopenharmony_ci            if (!node->next)
254cabdff1aSopenharmony_ci               priv->tail = node;
255cabdff1aSopenharmony_ci
256cabdff1aSopenharmony_ci            current->next = NULL;
257cabdff1aSopenharmony_ci            return current;
258cabdff1aSopenharmony_ci        } else {
259cabdff1aSopenharmony_ci            node = current;
260cabdff1aSopenharmony_ci        }
261cabdff1aSopenharmony_ci    }
262cabdff1aSopenharmony_ci
263cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_VERBOSE,
264cabdff1aSopenharmony_ci           "CrystalHD: Couldn't match fake_timestamp.\n");
265cabdff1aSopenharmony_ci    return NULL;
266cabdff1aSopenharmony_ci}
267cabdff1aSopenharmony_ci
268cabdff1aSopenharmony_ci
269cabdff1aSopenharmony_ci/*****************************************************************************
270cabdff1aSopenharmony_ci * Video decoder API function definitions
271cabdff1aSopenharmony_ci ****************************************************************************/
272cabdff1aSopenharmony_ci
273cabdff1aSopenharmony_cistatic void flush(AVCodecContext *avctx)
274cabdff1aSopenharmony_ci{
275cabdff1aSopenharmony_ci    CHDContext *priv = avctx->priv_data;
276cabdff1aSopenharmony_ci
277cabdff1aSopenharmony_ci    priv->need_second_field = 0;
278cabdff1aSopenharmony_ci    priv->draining          = 0;
279cabdff1aSopenharmony_ci
280cabdff1aSopenharmony_ci    /* Flush mode 4 flushes all software and hardware buffers. */
281cabdff1aSopenharmony_ci    DtsFlushInput(priv->dev, 4);
282cabdff1aSopenharmony_ci}
283cabdff1aSopenharmony_ci
284cabdff1aSopenharmony_ci
285cabdff1aSopenharmony_cistatic av_cold int uninit(AVCodecContext *avctx)
286cabdff1aSopenharmony_ci{
287cabdff1aSopenharmony_ci    CHDContext *priv = avctx->priv_data;
288cabdff1aSopenharmony_ci    HANDLE device;
289cabdff1aSopenharmony_ci
290cabdff1aSopenharmony_ci    device = priv->dev;
291cabdff1aSopenharmony_ci    DtsStopDecoder(device);
292cabdff1aSopenharmony_ci    DtsCloseDecoder(device);
293cabdff1aSopenharmony_ci    DtsDeviceClose(device);
294cabdff1aSopenharmony_ci
295cabdff1aSopenharmony_ci    if (priv->head) {
296cabdff1aSopenharmony_ci       OpaqueList *node = priv->head;
297cabdff1aSopenharmony_ci       while (node) {
298cabdff1aSopenharmony_ci          OpaqueList *next = node->next;
299cabdff1aSopenharmony_ci          av_free(node);
300cabdff1aSopenharmony_ci          node = next;
301cabdff1aSopenharmony_ci       }
302cabdff1aSopenharmony_ci    }
303cabdff1aSopenharmony_ci
304cabdff1aSopenharmony_ci    return 0;
305cabdff1aSopenharmony_ci}
306cabdff1aSopenharmony_ci
307cabdff1aSopenharmony_cistatic av_cold int init(AVCodecContext *avctx)
308cabdff1aSopenharmony_ci{
309cabdff1aSopenharmony_ci    CHDContext* priv;
310cabdff1aSopenharmony_ci    BC_STATUS ret;
311cabdff1aSopenharmony_ci    BC_INFO_CRYSTAL version;
312cabdff1aSopenharmony_ci    BC_INPUT_FORMAT format = {
313cabdff1aSopenharmony_ci        .FGTEnable   = FALSE,
314cabdff1aSopenharmony_ci        .Progressive = TRUE,
315cabdff1aSopenharmony_ci        .OptFlags    = 0x80000000 | vdecFrameRate59_94 | 0x40,
316cabdff1aSopenharmony_ci        .width       = avctx->width,
317cabdff1aSopenharmony_ci        .height      = avctx->height,
318cabdff1aSopenharmony_ci    };
319cabdff1aSopenharmony_ci
320cabdff1aSopenharmony_ci    BC_MEDIA_SUBTYPE subtype;
321cabdff1aSopenharmony_ci
322cabdff1aSopenharmony_ci    uint32_t mode = DTS_PLAYBACK_MODE |
323cabdff1aSopenharmony_ci                    DTS_LOAD_FILE_PLAY_FW |
324cabdff1aSopenharmony_ci                    DTS_SKIP_TX_CHK_CPB |
325cabdff1aSopenharmony_ci                    DTS_PLAYBACK_DROP_RPT_MODE |
326cabdff1aSopenharmony_ci                    DTS_SINGLE_THREADED_MODE |
327cabdff1aSopenharmony_ci                    DTS_DFLT_RESOLUTION(vdecRESOLUTION_1080p23_976);
328cabdff1aSopenharmony_ci
329cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_VERBOSE, "CrystalHD Init for %s\n",
330cabdff1aSopenharmony_ci           avctx->codec->name);
331cabdff1aSopenharmony_ci
332cabdff1aSopenharmony_ci    avctx->pix_fmt = AV_PIX_FMT_YUYV422;
333cabdff1aSopenharmony_ci
334cabdff1aSopenharmony_ci    /* Initialize the library */
335cabdff1aSopenharmony_ci    priv               = avctx->priv_data;
336cabdff1aSopenharmony_ci    priv->avctx        = avctx;
337cabdff1aSopenharmony_ci    priv->pkt          = avctx->internal->in_pkt;
338cabdff1aSopenharmony_ci    priv->draining     = 0;
339cabdff1aSopenharmony_ci
340cabdff1aSopenharmony_ci    subtype = id2subtype(priv, avctx->codec->id);
341cabdff1aSopenharmony_ci    switch (subtype) {
342cabdff1aSopenharmony_ci    case BC_MSUBTYPE_H264:
343cabdff1aSopenharmony_ci        format.startCodeSz = 4;
344cabdff1aSopenharmony_ci        // Fall-through
345cabdff1aSopenharmony_ci    case BC_MSUBTYPE_VC1:
346cabdff1aSopenharmony_ci    case BC_MSUBTYPE_WVC1:
347cabdff1aSopenharmony_ci    case BC_MSUBTYPE_WMV3:
348cabdff1aSopenharmony_ci    case BC_MSUBTYPE_WMVA:
349cabdff1aSopenharmony_ci    case BC_MSUBTYPE_MPEG2VIDEO:
350cabdff1aSopenharmony_ci    case BC_MSUBTYPE_DIVX:
351cabdff1aSopenharmony_ci    case BC_MSUBTYPE_DIVX311:
352cabdff1aSopenharmony_ci        format.pMetaData  = avctx->extradata;
353cabdff1aSopenharmony_ci        format.metaDataSz = avctx->extradata_size;
354cabdff1aSopenharmony_ci        break;
355cabdff1aSopenharmony_ci    default:
356cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "CrystalHD: Unknown codec name\n");
357cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
358cabdff1aSopenharmony_ci    }
359cabdff1aSopenharmony_ci    format.mSubtype = subtype;
360cabdff1aSopenharmony_ci
361cabdff1aSopenharmony_ci    if (priv->sWidth) {
362cabdff1aSopenharmony_ci        format.bEnableScaling = 1;
363cabdff1aSopenharmony_ci        format.ScalingParams.sWidth = priv->sWidth;
364cabdff1aSopenharmony_ci    }
365cabdff1aSopenharmony_ci
366cabdff1aSopenharmony_ci    /* Get a decoder instance */
367cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: starting up\n");
368cabdff1aSopenharmony_ci    // Initialize the Link and Decoder devices
369cabdff1aSopenharmony_ci    ret = DtsDeviceOpen(&priv->dev, mode);
370cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
371cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: DtsDeviceOpen failed\n");
372cabdff1aSopenharmony_ci        goto fail;
373cabdff1aSopenharmony_ci    }
374cabdff1aSopenharmony_ci
375cabdff1aSopenharmony_ci    ret = DtsCrystalHDVersion(priv->dev, &version);
376cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
377cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_VERBOSE,
378cabdff1aSopenharmony_ci               "CrystalHD: DtsCrystalHDVersion failed\n");
379cabdff1aSopenharmony_ci        goto fail;
380cabdff1aSopenharmony_ci    }
381cabdff1aSopenharmony_ci    priv->is_70012 = version.device == 0;
382cabdff1aSopenharmony_ci
383cabdff1aSopenharmony_ci    if (priv->is_70012 &&
384cabdff1aSopenharmony_ci        (subtype == BC_MSUBTYPE_DIVX || subtype == BC_MSUBTYPE_DIVX311)) {
385cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_VERBOSE,
386cabdff1aSopenharmony_ci               "CrystalHD: BCM70012 doesn't support MPEG4-ASP/DivX/Xvid\n");
387cabdff1aSopenharmony_ci        goto fail;
388cabdff1aSopenharmony_ci    }
389cabdff1aSopenharmony_ci
390cabdff1aSopenharmony_ci    ret = DtsSetInputFormat(priv->dev, &format);
391cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
392cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "CrystalHD: SetInputFormat failed\n");
393cabdff1aSopenharmony_ci        goto fail;
394cabdff1aSopenharmony_ci    }
395cabdff1aSopenharmony_ci
396cabdff1aSopenharmony_ci    ret = DtsOpenDecoder(priv->dev, BC_STREAM_TYPE_ES);
397cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
398cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsOpenDecoder failed\n");
399cabdff1aSopenharmony_ci        goto fail;
400cabdff1aSopenharmony_ci    }
401cabdff1aSopenharmony_ci
402cabdff1aSopenharmony_ci    ret = DtsSetColorSpace(priv->dev, OUTPUT_MODE422_YUY2);
403cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
404cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsSetColorSpace failed\n");
405cabdff1aSopenharmony_ci        goto fail;
406cabdff1aSopenharmony_ci    }
407cabdff1aSopenharmony_ci    ret = DtsStartDecoder(priv->dev);
408cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
409cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsStartDecoder failed\n");
410cabdff1aSopenharmony_ci        goto fail;
411cabdff1aSopenharmony_ci    }
412cabdff1aSopenharmony_ci    ret = DtsStartCapture(priv->dev);
413cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
414cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsStartCapture failed\n");
415cabdff1aSopenharmony_ci        goto fail;
416cabdff1aSopenharmony_ci    }
417cabdff1aSopenharmony_ci
418cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Init complete.\n");
419cabdff1aSopenharmony_ci
420cabdff1aSopenharmony_ci    return 0;
421cabdff1aSopenharmony_ci
422cabdff1aSopenharmony_ci fail:
423cabdff1aSopenharmony_ci    uninit(avctx);
424cabdff1aSopenharmony_ci    return -1;
425cabdff1aSopenharmony_ci}
426cabdff1aSopenharmony_ci
427cabdff1aSopenharmony_ci
428cabdff1aSopenharmony_cistatic inline CopyRet copy_frame(AVCodecContext *avctx,
429cabdff1aSopenharmony_ci                                 BC_DTS_PROC_OUT *output,
430cabdff1aSopenharmony_ci                                 AVFrame *frame, int *got_frame)
431cabdff1aSopenharmony_ci{
432cabdff1aSopenharmony_ci    BC_STATUS ret;
433cabdff1aSopenharmony_ci    BC_DTS_STATUS decoder_status = { 0, };
434cabdff1aSopenharmony_ci    uint8_t interlaced;
435cabdff1aSopenharmony_ci
436cabdff1aSopenharmony_ci    CHDContext *priv = avctx->priv_data;
437cabdff1aSopenharmony_ci    int64_t pkt_pts  = AV_NOPTS_VALUE;
438cabdff1aSopenharmony_ci
439cabdff1aSopenharmony_ci    uint8_t bottom_field = (output->PicInfo.flags & VDEC_FLAG_BOTTOMFIELD) ==
440cabdff1aSopenharmony_ci                           VDEC_FLAG_BOTTOMFIELD;
441cabdff1aSopenharmony_ci    uint8_t bottom_first = !!(output->PicInfo.flags & VDEC_FLAG_BOTTOM_FIRST);
442cabdff1aSopenharmony_ci
443cabdff1aSopenharmony_ci    int width    = output->PicInfo.width;
444cabdff1aSopenharmony_ci    int height   = output->PicInfo.height;
445cabdff1aSopenharmony_ci    int bwidth;
446cabdff1aSopenharmony_ci    uint8_t *src = output->Ybuff;
447cabdff1aSopenharmony_ci    int sStride;
448cabdff1aSopenharmony_ci    uint8_t *dst;
449cabdff1aSopenharmony_ci    int dStride;
450cabdff1aSopenharmony_ci
451cabdff1aSopenharmony_ci    if (output->PicInfo.timeStamp != 0) {
452cabdff1aSopenharmony_ci        OpaqueList *node = opaque_list_pop(priv, output->PicInfo.timeStamp);
453cabdff1aSopenharmony_ci        if (node) {
454cabdff1aSopenharmony_ci            pkt_pts = node->reordered_opaque;
455cabdff1aSopenharmony_ci            av_free(node);
456cabdff1aSopenharmony_ci        } else {
457cabdff1aSopenharmony_ci            /*
458cabdff1aSopenharmony_ci             * We will encounter a situation where a timestamp cannot be
459cabdff1aSopenharmony_ci             * popped if a second field is being returned. In this case,
460cabdff1aSopenharmony_ci             * each field has the same timestamp and the first one will
461cabdff1aSopenharmony_ci             * cause it to be popped. We'll avoid overwriting the valid
462cabdff1aSopenharmony_ci             * timestamp below.
463cabdff1aSopenharmony_ci             */
464cabdff1aSopenharmony_ci        }
465cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_VERBOSE, "output \"pts\": %"PRIu64"\n",
466cabdff1aSopenharmony_ci               output->PicInfo.timeStamp);
467cabdff1aSopenharmony_ci    }
468cabdff1aSopenharmony_ci
469cabdff1aSopenharmony_ci    ret = DtsGetDriverStatus(priv->dev, &decoder_status);
470cabdff1aSopenharmony_ci    if (ret != BC_STS_SUCCESS) {
471cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR,
472cabdff1aSopenharmony_ci               "CrystalHD: GetDriverStatus failed: %u\n", ret);
473cabdff1aSopenharmony_ci       return RET_ERROR;
474cabdff1aSopenharmony_ci    }
475cabdff1aSopenharmony_ci
476cabdff1aSopenharmony_ci    interlaced = output->PicInfo.flags & VDEC_FLAG_INTERLACED_SRC;
477cabdff1aSopenharmony_ci
478cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_VERBOSE, "Interlaced state: %d\n",
479cabdff1aSopenharmony_ci           interlaced);
480cabdff1aSopenharmony_ci
481cabdff1aSopenharmony_ci    priv->need_second_field = interlaced && !priv->need_second_field;
482cabdff1aSopenharmony_ci
483cabdff1aSopenharmony_ci    if (!frame->data[0]) {
484cabdff1aSopenharmony_ci        if (ff_get_buffer(avctx, frame, 0) < 0)
485cabdff1aSopenharmony_ci            return RET_ERROR;
486cabdff1aSopenharmony_ci    }
487cabdff1aSopenharmony_ci
488cabdff1aSopenharmony_ci    bwidth = av_image_get_linesize(avctx->pix_fmt, width, 0);
489cabdff1aSopenharmony_ci    if (bwidth < 0)
490cabdff1aSopenharmony_ci       return RET_ERROR;
491cabdff1aSopenharmony_ci
492cabdff1aSopenharmony_ci    if (priv->is_70012) {
493cabdff1aSopenharmony_ci        int pStride;
494cabdff1aSopenharmony_ci
495cabdff1aSopenharmony_ci        if (width <= 720)
496cabdff1aSopenharmony_ci            pStride = 720;
497cabdff1aSopenharmony_ci        else if (width <= 1280)
498cabdff1aSopenharmony_ci            pStride = 1280;
499cabdff1aSopenharmony_ci        else pStride = 1920;
500cabdff1aSopenharmony_ci        sStride = av_image_get_linesize(avctx->pix_fmt, pStride, 0);
501cabdff1aSopenharmony_ci        if (sStride < 0)
502cabdff1aSopenharmony_ci            return RET_ERROR;
503cabdff1aSopenharmony_ci    } else {
504cabdff1aSopenharmony_ci        sStride = bwidth;
505cabdff1aSopenharmony_ci    }
506cabdff1aSopenharmony_ci
507cabdff1aSopenharmony_ci    dStride = frame->linesize[0];
508cabdff1aSopenharmony_ci    dst     = frame->data[0];
509cabdff1aSopenharmony_ci
510cabdff1aSopenharmony_ci    av_log(priv->avctx, AV_LOG_VERBOSE, "CrystalHD: Copying out frame\n");
511cabdff1aSopenharmony_ci
512cabdff1aSopenharmony_ci    /*
513cabdff1aSopenharmony_ci     * The hardware doesn't return the first sample of a picture.
514cabdff1aSopenharmony_ci     * Ignoring why it behaves this way, it's better to copy the sample from
515cabdff1aSopenharmony_ci     * the second line, rather than the next sample across because the chroma
516cabdff1aSopenharmony_ci     * values should be correct (assuming the decoded video was 4:2:0, which
517cabdff1aSopenharmony_ci     * it was).
518cabdff1aSopenharmony_ci     */
519cabdff1aSopenharmony_ci    *((uint32_t *)src) = *((uint32_t *)(src + sStride));
520cabdff1aSopenharmony_ci
521cabdff1aSopenharmony_ci    if (interlaced) {
522cabdff1aSopenharmony_ci        int dY = 0;
523cabdff1aSopenharmony_ci        int sY = 0;
524cabdff1aSopenharmony_ci
525cabdff1aSopenharmony_ci        height /= 2;
526cabdff1aSopenharmony_ci        if (bottom_field) {
527cabdff1aSopenharmony_ci            av_log(priv->avctx, AV_LOG_VERBOSE, "Interlaced: bottom field\n");
528cabdff1aSopenharmony_ci            dY = 1;
529cabdff1aSopenharmony_ci        } else {
530cabdff1aSopenharmony_ci            av_log(priv->avctx, AV_LOG_VERBOSE, "Interlaced: top field\n");
531cabdff1aSopenharmony_ci            dY = 0;
532cabdff1aSopenharmony_ci        }
533cabdff1aSopenharmony_ci
534cabdff1aSopenharmony_ci        for (sY = 0; sY < height; dY++, sY++) {
535cabdff1aSopenharmony_ci            memcpy(&(dst[dY * dStride]), &(src[sY * sStride]), bwidth);
536cabdff1aSopenharmony_ci            dY++;
537cabdff1aSopenharmony_ci        }
538cabdff1aSopenharmony_ci    } else {
539cabdff1aSopenharmony_ci        av_image_copy_plane(dst, dStride, src, sStride, bwidth, height);
540cabdff1aSopenharmony_ci    }
541cabdff1aSopenharmony_ci
542cabdff1aSopenharmony_ci    frame->interlaced_frame = interlaced;
543cabdff1aSopenharmony_ci    if (interlaced)
544cabdff1aSopenharmony_ci        frame->top_field_first = !bottom_first;
545cabdff1aSopenharmony_ci
546cabdff1aSopenharmony_ci    frame->pts = pkt_pts;
547cabdff1aSopenharmony_ci
548cabdff1aSopenharmony_ci    frame->pkt_pos = -1;
549cabdff1aSopenharmony_ci    frame->pkt_duration = 0;
550cabdff1aSopenharmony_ci    frame->pkt_size = -1;
551cabdff1aSopenharmony_ci
552cabdff1aSopenharmony_ci    if (!priv->need_second_field) {
553cabdff1aSopenharmony_ci        *got_frame       = 1;
554cabdff1aSopenharmony_ci    } else {
555cabdff1aSopenharmony_ci        return RET_COPY_AGAIN;
556cabdff1aSopenharmony_ci    }
557cabdff1aSopenharmony_ci
558cabdff1aSopenharmony_ci    return RET_OK;
559cabdff1aSopenharmony_ci}
560cabdff1aSopenharmony_ci
561cabdff1aSopenharmony_ci
562cabdff1aSopenharmony_cistatic inline CopyRet receive_frame(AVCodecContext *avctx,
563cabdff1aSopenharmony_ci                                    AVFrame *frame, int *got_frame)
564cabdff1aSopenharmony_ci{
565cabdff1aSopenharmony_ci    BC_STATUS ret;
566cabdff1aSopenharmony_ci    BC_DTS_PROC_OUT output = {
567cabdff1aSopenharmony_ci        .PicInfo.width  = avctx->width,
568cabdff1aSopenharmony_ci        .PicInfo.height = avctx->height,
569cabdff1aSopenharmony_ci    };
570cabdff1aSopenharmony_ci    CHDContext *priv = avctx->priv_data;
571cabdff1aSopenharmony_ci    HANDLE dev       = priv->dev;
572cabdff1aSopenharmony_ci
573cabdff1aSopenharmony_ci    *got_frame = 0;
574cabdff1aSopenharmony_ci
575cabdff1aSopenharmony_ci    // Request decoded data from the driver
576cabdff1aSopenharmony_ci    ret = DtsProcOutputNoCopy(dev, OUTPUT_PROC_TIMEOUT, &output);
577cabdff1aSopenharmony_ci    if (ret == BC_STS_FMT_CHANGE) {
578cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Initial format change\n");
579cabdff1aSopenharmony_ci        avctx->width  = output.PicInfo.width;
580cabdff1aSopenharmony_ci        avctx->height = output.PicInfo.height;
581cabdff1aSopenharmony_ci        switch ( output.PicInfo.aspect_ratio ) {
582cabdff1aSopenharmony_ci        case vdecAspectRatioSquare:
583cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) {  1,  1};
584cabdff1aSopenharmony_ci            break;
585cabdff1aSopenharmony_ci        case vdecAspectRatio12_11:
586cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 12, 11};
587cabdff1aSopenharmony_ci            break;
588cabdff1aSopenharmony_ci        case vdecAspectRatio10_11:
589cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 10, 11};
590cabdff1aSopenharmony_ci            break;
591cabdff1aSopenharmony_ci        case vdecAspectRatio16_11:
592cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 16, 11};
593cabdff1aSopenharmony_ci            break;
594cabdff1aSopenharmony_ci        case vdecAspectRatio40_33:
595cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 40, 33};
596cabdff1aSopenharmony_ci            break;
597cabdff1aSopenharmony_ci        case vdecAspectRatio24_11:
598cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 24, 11};
599cabdff1aSopenharmony_ci            break;
600cabdff1aSopenharmony_ci        case vdecAspectRatio20_11:
601cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 20, 11};
602cabdff1aSopenharmony_ci            break;
603cabdff1aSopenharmony_ci        case vdecAspectRatio32_11:
604cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 32, 11};
605cabdff1aSopenharmony_ci            break;
606cabdff1aSopenharmony_ci        case vdecAspectRatio80_33:
607cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 80, 33};
608cabdff1aSopenharmony_ci            break;
609cabdff1aSopenharmony_ci        case vdecAspectRatio18_11:
610cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 18, 11};
611cabdff1aSopenharmony_ci            break;
612cabdff1aSopenharmony_ci        case vdecAspectRatio15_11:
613cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 15, 11};
614cabdff1aSopenharmony_ci            break;
615cabdff1aSopenharmony_ci        case vdecAspectRatio64_33:
616cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 64, 33};
617cabdff1aSopenharmony_ci            break;
618cabdff1aSopenharmony_ci        case vdecAspectRatio160_99:
619cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) {160, 99};
620cabdff1aSopenharmony_ci            break;
621cabdff1aSopenharmony_ci        case vdecAspectRatio4_3:
622cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) {  4,  3};
623cabdff1aSopenharmony_ci            break;
624cabdff1aSopenharmony_ci        case vdecAspectRatio16_9:
625cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) { 16,  9};
626cabdff1aSopenharmony_ci            break;
627cabdff1aSopenharmony_ci        case vdecAspectRatio221_1:
628cabdff1aSopenharmony_ci            avctx->sample_aspect_ratio = (AVRational) {221,  1};
629cabdff1aSopenharmony_ci            break;
630cabdff1aSopenharmony_ci        }
631cabdff1aSopenharmony_ci        return RET_COPY_AGAIN;
632cabdff1aSopenharmony_ci    } else if (ret == BC_STS_SUCCESS) {
633cabdff1aSopenharmony_ci        int copy_ret = -1;
634cabdff1aSopenharmony_ci        if (output.PoutFlags & BC_POUT_FLAGS_PIB_VALID) {
635cabdff1aSopenharmony_ci            print_frame_info(priv, &output);
636cabdff1aSopenharmony_ci
637cabdff1aSopenharmony_ci            copy_ret = copy_frame(avctx, &output, frame, got_frame);
638cabdff1aSopenharmony_ci        } else {
639cabdff1aSopenharmony_ci            /*
640cabdff1aSopenharmony_ci             * An invalid frame has been consumed.
641cabdff1aSopenharmony_ci             */
642cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput succeeded with "
643cabdff1aSopenharmony_ci                                        "invalid PIB\n");
644cabdff1aSopenharmony_ci            copy_ret = RET_COPY_AGAIN;
645cabdff1aSopenharmony_ci        }
646cabdff1aSopenharmony_ci        DtsReleaseOutputBuffs(dev, NULL, FALSE);
647cabdff1aSopenharmony_ci
648cabdff1aSopenharmony_ci        return copy_ret;
649cabdff1aSopenharmony_ci    } else if (ret == BC_STS_BUSY) {
650cabdff1aSopenharmony_ci        return RET_COPY_AGAIN;
651cabdff1aSopenharmony_ci    } else {
652cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput failed %d\n", ret);
653cabdff1aSopenharmony_ci        return RET_ERROR;
654cabdff1aSopenharmony_ci    }
655cabdff1aSopenharmony_ci}
656cabdff1aSopenharmony_ci
657cabdff1aSopenharmony_cistatic int crystalhd_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt)
658cabdff1aSopenharmony_ci{
659cabdff1aSopenharmony_ci    BC_STATUS bc_ret;
660cabdff1aSopenharmony_ci    CHDContext *priv   = avctx->priv_data;
661cabdff1aSopenharmony_ci    HANDLE dev         = priv->dev;
662cabdff1aSopenharmony_ci    int ret = 0;
663cabdff1aSopenharmony_ci
664cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: decode_packet\n");
665cabdff1aSopenharmony_ci
666cabdff1aSopenharmony_ci    if (avpkt && avpkt->size) {
667cabdff1aSopenharmony_ci        uint64_t pts;
668cabdff1aSopenharmony_ci
669cabdff1aSopenharmony_ci        /*
670cabdff1aSopenharmony_ci         * Despite being notionally opaque, either libcrystalhd or
671cabdff1aSopenharmony_ci         * the hardware itself will mangle pts values that are too
672cabdff1aSopenharmony_ci         * small or too large. The docs claim it should be in units
673cabdff1aSopenharmony_ci         * of 100ns. Given that we're nominally dealing with a black
674cabdff1aSopenharmony_ci         * box on both sides, any transform we do has no guarantee of
675cabdff1aSopenharmony_ci         * avoiding mangling so we need to build a mapping to values
676cabdff1aSopenharmony_ci         * we know will not be mangled.
677cabdff1aSopenharmony_ci         */
678cabdff1aSopenharmony_ci        pts = opaque_list_push(priv, avpkt->pts);
679cabdff1aSopenharmony_ci        if (!pts) {
680cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
681cabdff1aSopenharmony_ci            goto exit;
682cabdff1aSopenharmony_ci        }
683cabdff1aSopenharmony_ci        av_log(priv->avctx, AV_LOG_VERBOSE,
684cabdff1aSopenharmony_ci               "input \"pts\": %"PRIu64"\n", pts);
685cabdff1aSopenharmony_ci        bc_ret = DtsProcInput(dev, avpkt->data, avpkt->size, pts, 0);
686cabdff1aSopenharmony_ci        if (bc_ret == BC_STS_BUSY) {
687cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_WARNING,
688cabdff1aSopenharmony_ci                   "CrystalHD: ProcInput returned busy\n");
689cabdff1aSopenharmony_ci            ret = AVERROR(EAGAIN);
690cabdff1aSopenharmony_ci            goto exit;
691cabdff1aSopenharmony_ci        } else if (bc_ret != BC_STS_SUCCESS) {
692cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_ERROR,
693cabdff1aSopenharmony_ci                   "CrystalHD: ProcInput failed: %u\n", ret);
694cabdff1aSopenharmony_ci            ret = -1;
695cabdff1aSopenharmony_ci            goto exit;
696cabdff1aSopenharmony_ci        }
697cabdff1aSopenharmony_ci    } else {
698cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_INFO, "CrystalHD: No more input data\n");
699cabdff1aSopenharmony_ci        priv->draining = 1;
700cabdff1aSopenharmony_ci        ret = AVERROR_EOF;
701cabdff1aSopenharmony_ci        goto exit;
702cabdff1aSopenharmony_ci    }
703cabdff1aSopenharmony_ci exit:
704cabdff1aSopenharmony_ci    return ret;
705cabdff1aSopenharmony_ci}
706cabdff1aSopenharmony_ci
707cabdff1aSopenharmony_cistatic int crystalhd_receive_frame(AVCodecContext *avctx, AVFrame *frame)
708cabdff1aSopenharmony_ci{
709cabdff1aSopenharmony_ci    BC_STATUS bc_ret;
710cabdff1aSopenharmony_ci    BC_DTS_STATUS decoder_status = { 0, };
711cabdff1aSopenharmony_ci    CopyRet rec_ret;
712cabdff1aSopenharmony_ci    CHDContext *priv   = avctx->priv_data;
713cabdff1aSopenharmony_ci    AVPacket *const pkt = priv->pkt;
714cabdff1aSopenharmony_ci    HANDLE dev         = priv->dev;
715cabdff1aSopenharmony_ci    int got_frame = 0;
716cabdff1aSopenharmony_ci    int ret = 0;
717cabdff1aSopenharmony_ci
718cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: receive_frame\n");
719cabdff1aSopenharmony_ci
720cabdff1aSopenharmony_ci    ret = ff_decode_get_packet(avctx, pkt);
721cabdff1aSopenharmony_ci    if (ret < 0 && ret != AVERROR_EOF) {
722cabdff1aSopenharmony_ci        return ret;
723cabdff1aSopenharmony_ci    }
724cabdff1aSopenharmony_ci
725cabdff1aSopenharmony_ci    while (pkt->size > DtsTxFreeSize(dev)) {
726cabdff1aSopenharmony_ci        /*
727cabdff1aSopenharmony_ci         * Block until there is space in the buffer for the next packet.
728cabdff1aSopenharmony_ci         * We assume that the hardware will make forward progress at this
729cabdff1aSopenharmony_ci         * point, although in pathological cases that may not happen.
730cabdff1aSopenharmony_ci         */
731cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_TRACE, "CrystalHD: Waiting for space in input buffer\n");
732cabdff1aSopenharmony_ci    }
733cabdff1aSopenharmony_ci
734cabdff1aSopenharmony_ci    ret = crystalhd_decode_packet(avctx, pkt);
735cabdff1aSopenharmony_ci    av_packet_unref(pkt);
736cabdff1aSopenharmony_ci    // crystalhd_is_buffer_full() should avoid this.
737cabdff1aSopenharmony_ci    if (ret == AVERROR(EAGAIN)) {
738cabdff1aSopenharmony_ci        ret = AVERROR_EXTERNAL;
739cabdff1aSopenharmony_ci    }
740cabdff1aSopenharmony_ci    if (ret < 0 && ret != AVERROR_EOF) {
741cabdff1aSopenharmony_ci        return ret;
742cabdff1aSopenharmony_ci    }
743cabdff1aSopenharmony_ci
744cabdff1aSopenharmony_ci    do {
745cabdff1aSopenharmony_ci        bc_ret = DtsGetDriverStatus(dev, &decoder_status);
746cabdff1aSopenharmony_ci        if (bc_ret != BC_STS_SUCCESS) {
747cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_ERROR, "CrystalHD: GetDriverStatus failed\n");
748cabdff1aSopenharmony_ci            return -1;
749cabdff1aSopenharmony_ci        }
750cabdff1aSopenharmony_ci
751cabdff1aSopenharmony_ci        if (decoder_status.ReadyListCount == 0) {
752cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Insufficient frames ready. Returning\n");
753cabdff1aSopenharmony_ci            got_frame = 0;
754cabdff1aSopenharmony_ci            rec_ret = RET_OK;
755cabdff1aSopenharmony_ci            break;
756cabdff1aSopenharmony_ci        }
757cabdff1aSopenharmony_ci
758cabdff1aSopenharmony_ci        rec_ret = receive_frame(avctx, frame, &got_frame);
759cabdff1aSopenharmony_ci    } while (rec_ret == RET_COPY_AGAIN);
760cabdff1aSopenharmony_ci
761cabdff1aSopenharmony_ci    if (rec_ret == RET_ERROR) {
762cabdff1aSopenharmony_ci        return -1;
763cabdff1aSopenharmony_ci    } else if (got_frame == 0) {
764cabdff1aSopenharmony_ci        return priv->draining ? AVERROR_EOF : AVERROR(EAGAIN);
765cabdff1aSopenharmony_ci    } else {
766cabdff1aSopenharmony_ci        return 0;
767cabdff1aSopenharmony_ci    }
768cabdff1aSopenharmony_ci}
769cabdff1aSopenharmony_ci
770cabdff1aSopenharmony_ci#define DEFINE_CRYSTALHD_DECODER(x, X, bsf_name) \
771cabdff1aSopenharmony_ci    static const AVClass x##_crystalhd_class = { \
772cabdff1aSopenharmony_ci        .class_name = #x "_crystalhd", \
773cabdff1aSopenharmony_ci        .item_name = av_default_item_name, \
774cabdff1aSopenharmony_ci        .option = options, \
775cabdff1aSopenharmony_ci        .version = LIBAVUTIL_VERSION_INT, \
776cabdff1aSopenharmony_ci    }; \
777cabdff1aSopenharmony_ci    const FFCodec ff_##x##_crystalhd_decoder = { \
778cabdff1aSopenharmony_ci        .p.name         = #x "_crystalhd", \
779cabdff1aSopenharmony_ci        .p.long_name    = NULL_IF_CONFIG_SMALL("CrystalHD " #X " decoder"), \
780cabdff1aSopenharmony_ci        .p.type         = AVMEDIA_TYPE_VIDEO, \
781cabdff1aSopenharmony_ci        .p.id           = AV_CODEC_ID_##X, \
782cabdff1aSopenharmony_ci        .priv_data_size = sizeof(CHDContext), \
783cabdff1aSopenharmony_ci        .p.priv_class   = &x##_crystalhd_class, \
784cabdff1aSopenharmony_ci        .init           = init, \
785cabdff1aSopenharmony_ci        .close          = uninit, \
786cabdff1aSopenharmony_ci        FF_CODEC_RECEIVE_FRAME_CB(crystalhd_receive_frame), \
787cabdff1aSopenharmony_ci        .flush          = flush, \
788cabdff1aSopenharmony_ci        .bsfs           = bsf_name, \
789cabdff1aSopenharmony_ci        .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \
790cabdff1aSopenharmony_ci        .caps_internal  = FF_CODEC_CAP_SETS_FRAME_PROPS, \
791cabdff1aSopenharmony_ci        .p.pix_fmts     = (const enum AVPixelFormat[]){AV_PIX_FMT_YUYV422, AV_PIX_FMT_NONE}, \
792cabdff1aSopenharmony_ci        .p.wrapper_name = "crystalhd", \
793cabdff1aSopenharmony_ci    };
794cabdff1aSopenharmony_ci
795cabdff1aSopenharmony_ci#if CONFIG_H264_CRYSTALHD_DECODER
796cabdff1aSopenharmony_ciDEFINE_CRYSTALHD_DECODER(h264, H264, "h264_mp4toannexb")
797cabdff1aSopenharmony_ci#endif
798cabdff1aSopenharmony_ci
799cabdff1aSopenharmony_ci#if CONFIG_MPEG2_CRYSTALHD_DECODER
800cabdff1aSopenharmony_ciDEFINE_CRYSTALHD_DECODER(mpeg2, MPEG2VIDEO, NULL)
801cabdff1aSopenharmony_ci#endif
802cabdff1aSopenharmony_ci
803cabdff1aSopenharmony_ci#if CONFIG_MPEG4_CRYSTALHD_DECODER
804cabdff1aSopenharmony_ciDEFINE_CRYSTALHD_DECODER(mpeg4, MPEG4, "mpeg4_unpack_bframes")
805cabdff1aSopenharmony_ci#endif
806cabdff1aSopenharmony_ci
807cabdff1aSopenharmony_ci#if CONFIG_MSMPEG4_CRYSTALHD_DECODER
808cabdff1aSopenharmony_ciDEFINE_CRYSTALHD_DECODER(msmpeg4, MSMPEG4V3, NULL)
809cabdff1aSopenharmony_ci#endif
810cabdff1aSopenharmony_ci
811cabdff1aSopenharmony_ci#if CONFIG_VC1_CRYSTALHD_DECODER
812cabdff1aSopenharmony_ciDEFINE_CRYSTALHD_DECODER(vc1, VC1, NULL)
813cabdff1aSopenharmony_ci#endif
814cabdff1aSopenharmony_ci
815cabdff1aSopenharmony_ci#if CONFIG_WMV3_CRYSTALHD_DECODER
816cabdff1aSopenharmony_ciDEFINE_CRYSTALHD_DECODER(wmv3, WMV3, NULL)
817cabdff1aSopenharmony_ci#endif
818