123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814 |
- /*
- * - CrystalHD decoder module -
- *
- * Copyright(C) 2010,2011 Philip Langdale <ffmpeg.philipl@overt.org>
- *
- * This file is part of FFmpeg.
- *
- * FFmpeg is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * FFmpeg is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- /*
- * - Principles of Operation -
- *
- * The CrystalHD decoder operates at the bitstream level - which is an even
- * higher level than the decoding hardware you typically see in modern GPUs.
- * This means it has a very simple interface, in principle. You feed demuxed
- * packets in one end and get decoded picture (fields/frames) out the other.
- *
- * Of course, nothing is ever that simple. Due, at the very least, to b-frame
- * dependencies in the supported formats, the hardware has a delay between
- * when a packet goes in, and when a picture comes out. Furthermore, this delay
- * is not just a function of time, but also one of the dependency on additional
- * frames being fed into the decoder to satisfy the b-frame dependencies.
- *
- * As such, the hardware can only be used effectively with a decode API that
- * doesn't assume a 1:1 relationship between input packets and output frames.
- * The new avcodec decode API is such an API (an m:n API) while the old one is
- * 1:1. Consequently, we no longer support the old API, which allows us to avoid
- * the vicious hacks that are required to approximate 1:1 operation.
- */
- /*****************************************************************************
- * Includes
- ****************************************************************************/
- #define _XOPEN_SOURCE 600
- #include <inttypes.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <libcrystalhd/bc_dts_types.h>
- #include <libcrystalhd/bc_dts_defs.h>
- #include <libcrystalhd/libcrystalhd_if.h>
- #include "avcodec.h"
- #include "decode.h"
- #include "internal.h"
- #include "libavutil/imgutils.h"
- #include "libavutil/intreadwrite.h"
- #include "libavutil/opt.h"
- #if HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- /** Timeout parameter passed to DtsProcOutput() in us */
- #define OUTPUT_PROC_TIMEOUT 50
- /** Step between fake timestamps passed to hardware in units of 100ns */
- #define TIMESTAMP_UNIT 100000
- /*****************************************************************************
- * Module private data
- ****************************************************************************/
- typedef enum {
- RET_ERROR = -1,
- RET_OK = 0,
- RET_COPY_AGAIN = 1,
- } CopyRet;
- typedef struct OpaqueList {
- struct OpaqueList *next;
- uint64_t fake_timestamp;
- uint64_t reordered_opaque;
- } OpaqueList;
- typedef struct {
- AVClass *av_class;
- AVCodecContext *avctx;
- HANDLE dev;
- uint8_t is_70012;
- uint8_t need_second_field;
- uint8_t draining;
- OpaqueList *head;
- OpaqueList *tail;
- /* Options */
- uint32_t sWidth;
- } CHDContext;
- static const AVOption options[] = {
- { "crystalhd_downscale_width",
- "Turn on downscaling to the specified width",
- offsetof(CHDContext, sWidth),
- AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT32_MAX,
- AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM, },
- { NULL, },
- };
- /*****************************************************************************
- * Helper functions
- ****************************************************************************/
- static inline BC_MEDIA_SUBTYPE id2subtype(CHDContext *priv, enum AVCodecID id)
- {
- switch (id) {
- case AV_CODEC_ID_MPEG4:
- return BC_MSUBTYPE_DIVX;
- case AV_CODEC_ID_MSMPEG4V3:
- return BC_MSUBTYPE_DIVX311;
- case AV_CODEC_ID_MPEG2VIDEO:
- return BC_MSUBTYPE_MPEG2VIDEO;
- case AV_CODEC_ID_VC1:
- return BC_MSUBTYPE_VC1;
- case AV_CODEC_ID_WMV3:
- return BC_MSUBTYPE_WMV3;
- case AV_CODEC_ID_H264:
- return BC_MSUBTYPE_H264;
- default:
- return BC_MSUBTYPE_INVALID;
- }
- }
- static inline void print_frame_info(CHDContext *priv, BC_DTS_PROC_OUT *output)
- {
- av_log(priv->avctx, AV_LOG_TRACE, "\tYBuffSz: %u\n", output->YbuffSz);
- av_log(priv->avctx, AV_LOG_TRACE, "\tYBuffDoneSz: %u\n",
- output->YBuffDoneSz);
- av_log(priv->avctx, AV_LOG_TRACE, "\tUVBuffDoneSz: %u\n",
- output->UVBuffDoneSz);
- av_log(priv->avctx, AV_LOG_TRACE, "\tTimestamp: %"PRIu64"\n",
- output->PicInfo.timeStamp);
- av_log(priv->avctx, AV_LOG_TRACE, "\tPicture Number: %u\n",
- output->PicInfo.picture_number);
- av_log(priv->avctx, AV_LOG_TRACE, "\tWidth: %u\n",
- output->PicInfo.width);
- av_log(priv->avctx, AV_LOG_TRACE, "\tHeight: %u\n",
- output->PicInfo.height);
- av_log(priv->avctx, AV_LOG_TRACE, "\tChroma: 0x%03x\n",
- output->PicInfo.chroma_format);
- av_log(priv->avctx, AV_LOG_TRACE, "\tPulldown: %u\n",
- output->PicInfo.pulldown);
- av_log(priv->avctx, AV_LOG_TRACE, "\tFlags: 0x%08x\n",
- output->PicInfo.flags);
- av_log(priv->avctx, AV_LOG_TRACE, "\tFrame Rate/Res: %u\n",
- output->PicInfo.frame_rate);
- av_log(priv->avctx, AV_LOG_TRACE, "\tAspect Ratio: %u\n",
- output->PicInfo.aspect_ratio);
- av_log(priv->avctx, AV_LOG_TRACE, "\tColor Primaries: %u\n",
- output->PicInfo.colour_primaries);
- av_log(priv->avctx, AV_LOG_TRACE, "\tMetaData: %u\n",
- output->PicInfo.picture_meta_payload);
- av_log(priv->avctx, AV_LOG_TRACE, "\tSession Number: %u\n",
- output->PicInfo.sess_num);
- av_log(priv->avctx, AV_LOG_TRACE, "\tycom: %u\n",
- output->PicInfo.ycom);
- av_log(priv->avctx, AV_LOG_TRACE, "\tCustom Aspect: %u\n",
- output->PicInfo.custom_aspect_ratio_width_height);
- av_log(priv->avctx, AV_LOG_TRACE, "\tFrames to Drop: %u\n",
- output->PicInfo.n_drop);
- av_log(priv->avctx, AV_LOG_TRACE, "\tH264 Valid Fields: 0x%08x\n",
- output->PicInfo.other.h264.valid);
- }
- /*****************************************************************************
- * OpaqueList functions
- ****************************************************************************/
- static uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque)
- {
- OpaqueList *newNode = av_mallocz(sizeof (OpaqueList));
- if (!newNode) {
- av_log(priv->avctx, AV_LOG_ERROR,
- "Unable to allocate new node in OpaqueList.\n");
- return 0;
- }
- if (!priv->head) {
- newNode->fake_timestamp = TIMESTAMP_UNIT;
- priv->head = newNode;
- } else {
- newNode->fake_timestamp = priv->tail->fake_timestamp + TIMESTAMP_UNIT;
- priv->tail->next = newNode;
- }
- priv->tail = newNode;
- newNode->reordered_opaque = reordered_opaque;
- return newNode->fake_timestamp;
- }
- /*
- * The OpaqueList is built in decode order, while elements will be removed
- * in presentation order. If frames are reordered, this means we must be
- * able to remove elements that are not the first element.
- *
- * Returned node must be freed by caller.
- */
- static OpaqueList *opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp)
- {
- OpaqueList *node = priv->head;
- if (!priv->head) {
- av_log(priv->avctx, AV_LOG_ERROR,
- "CrystalHD: Attempted to query non-existent timestamps.\n");
- return NULL;
- }
- /*
- * The first element is special-cased because we have to manipulate
- * the head pointer rather than the previous element in the list.
- */
- if (priv->head->fake_timestamp == fake_timestamp) {
- priv->head = node->next;
- if (!priv->head->next)
- priv->tail = priv->head;
- node->next = NULL;
- return node;
- }
- /*
- * The list is processed at arm's length so that we have the
- * previous element available to rewrite its next pointer.
- */
- while (node->next) {
- OpaqueList *current = node->next;
- if (current->fake_timestamp == fake_timestamp) {
- node->next = current->next;
- if (!node->next)
- priv->tail = node;
- current->next = NULL;
- return current;
- } else {
- node = current;
- }
- }
- av_log(priv->avctx, AV_LOG_VERBOSE,
- "CrystalHD: Couldn't match fake_timestamp.\n");
- return NULL;
- }
- /*****************************************************************************
- * Video decoder API function definitions
- ****************************************************************************/
- static void flush(AVCodecContext *avctx)
- {
- CHDContext *priv = avctx->priv_data;
- priv->need_second_field = 0;
- priv->draining = 0;
- /* Flush mode 4 flushes all software and hardware buffers. */
- DtsFlushInput(priv->dev, 4);
- }
- static av_cold int uninit(AVCodecContext *avctx)
- {
- CHDContext *priv = avctx->priv_data;
- HANDLE device;
- device = priv->dev;
- DtsStopDecoder(device);
- DtsCloseDecoder(device);
- DtsDeviceClose(device);
- if (priv->head) {
- OpaqueList *node = priv->head;
- while (node) {
- OpaqueList *next = node->next;
- av_free(node);
- node = next;
- }
- }
- return 0;
- }
- static av_cold int init(AVCodecContext *avctx)
- {
- CHDContext* priv;
- BC_STATUS ret;
- BC_INFO_CRYSTAL version;
- BC_INPUT_FORMAT format = {
- .FGTEnable = FALSE,
- .Progressive = TRUE,
- .OptFlags = 0x80000000 | vdecFrameRate59_94 | 0x40,
- .width = avctx->width,
- .height = avctx->height,
- };
- BC_MEDIA_SUBTYPE subtype;
- uint32_t mode = DTS_PLAYBACK_MODE |
- DTS_LOAD_FILE_PLAY_FW |
- DTS_SKIP_TX_CHK_CPB |
- DTS_PLAYBACK_DROP_RPT_MODE |
- DTS_SINGLE_THREADED_MODE |
- DTS_DFLT_RESOLUTION(vdecRESOLUTION_1080p23_976);
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD Init for %s\n",
- avctx->codec->name);
- avctx->pix_fmt = AV_PIX_FMT_YUYV422;
- /* Initialize the library */
- priv = avctx->priv_data;
- priv->avctx = avctx;
- priv->draining = 0;
- subtype = id2subtype(priv, avctx->codec->id);
- switch (subtype) {
- case BC_MSUBTYPE_H264:
- format.startCodeSz = 4;
- // Fall-through
- case BC_MSUBTYPE_VC1:
- case BC_MSUBTYPE_WVC1:
- case BC_MSUBTYPE_WMV3:
- case BC_MSUBTYPE_WMVA:
- case BC_MSUBTYPE_MPEG2VIDEO:
- case BC_MSUBTYPE_DIVX:
- case BC_MSUBTYPE_DIVX311:
- format.pMetaData = avctx->extradata;
- format.metaDataSz = avctx->extradata_size;
- break;
- default:
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: Unknown codec name\n");
- return AVERROR(EINVAL);
- }
- format.mSubtype = subtype;
- if (priv->sWidth) {
- format.bEnableScaling = 1;
- format.ScalingParams.sWidth = priv->sWidth;
- }
- /* Get a decoder instance */
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: starting up\n");
- // Initialize the Link and Decoder devices
- ret = DtsDeviceOpen(&priv->dev, mode);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: DtsDeviceOpen failed\n");
- goto fail;
- }
- ret = DtsCrystalHDVersion(priv->dev, &version);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_VERBOSE,
- "CrystalHD: DtsCrystalHDVersion failed\n");
- goto fail;
- }
- priv->is_70012 = version.device == 0;
- if (priv->is_70012 &&
- (subtype == BC_MSUBTYPE_DIVX || subtype == BC_MSUBTYPE_DIVX311)) {
- av_log(avctx, AV_LOG_VERBOSE,
- "CrystalHD: BCM70012 doesn't support MPEG4-ASP/DivX/Xvid\n");
- goto fail;
- }
- ret = DtsSetInputFormat(priv->dev, &format);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: SetInputFormat failed\n");
- goto fail;
- }
- ret = DtsOpenDecoder(priv->dev, BC_STREAM_TYPE_ES);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsOpenDecoder failed\n");
- goto fail;
- }
- ret = DtsSetColorSpace(priv->dev, OUTPUT_MODE422_YUY2);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsSetColorSpace failed\n");
- goto fail;
- }
- ret = DtsStartDecoder(priv->dev);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsStartDecoder failed\n");
- goto fail;
- }
- ret = DtsStartCapture(priv->dev);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsStartCapture failed\n");
- goto fail;
- }
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Init complete.\n");
- return 0;
- fail:
- uninit(avctx);
- return -1;
- }
- static inline CopyRet copy_frame(AVCodecContext *avctx,
- BC_DTS_PROC_OUT *output,
- AVFrame *frame, int *got_frame)
- {
- BC_STATUS ret;
- BC_DTS_STATUS decoder_status = { 0, };
- uint8_t interlaced;
- CHDContext *priv = avctx->priv_data;
- int64_t pkt_pts = AV_NOPTS_VALUE;
- uint8_t bottom_field = (output->PicInfo.flags & VDEC_FLAG_BOTTOMFIELD) ==
- VDEC_FLAG_BOTTOMFIELD;
- uint8_t bottom_first = !!(output->PicInfo.flags & VDEC_FLAG_BOTTOM_FIRST);
- int width = output->PicInfo.width;
- int height = output->PicInfo.height;
- int bwidth;
- uint8_t *src = output->Ybuff;
- int sStride;
- uint8_t *dst;
- int dStride;
- if (output->PicInfo.timeStamp != 0) {
- OpaqueList *node = opaque_list_pop(priv, output->PicInfo.timeStamp);
- if (node) {
- pkt_pts = node->reordered_opaque;
- av_free(node);
- } else {
- /*
- * We will encounter a situation where a timestamp cannot be
- * popped if a second field is being returned. In this case,
- * each field has the same timestamp and the first one will
- * cause it to be popped. We'll avoid overwriting the valid
- * timestamp below.
- */
- }
- av_log(avctx, AV_LOG_VERBOSE, "output \"pts\": %"PRIu64"\n",
- output->PicInfo.timeStamp);
- }
- ret = DtsGetDriverStatus(priv->dev, &decoder_status);
- if (ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR,
- "CrystalHD: GetDriverStatus failed: %u\n", ret);
- return RET_ERROR;
- }
- interlaced = output->PicInfo.flags & VDEC_FLAG_INTERLACED_SRC;
- av_log(avctx, AV_LOG_VERBOSE, "Interlaced state: %d\n",
- interlaced);
- priv->need_second_field = interlaced && !priv->need_second_field;
- if (!frame->data[0]) {
- if (ff_get_buffer(avctx, frame, 0) < 0)
- return RET_ERROR;
- }
- bwidth = av_image_get_linesize(avctx->pix_fmt, width, 0);
- if (bwidth < 0)
- return RET_ERROR;
- if (priv->is_70012) {
- int pStride;
- if (width <= 720)
- pStride = 720;
- else if (width <= 1280)
- pStride = 1280;
- else pStride = 1920;
- sStride = av_image_get_linesize(avctx->pix_fmt, pStride, 0);
- if (sStride < 0)
- return RET_ERROR;
- } else {
- sStride = bwidth;
- }
- dStride = frame->linesize[0];
- dst = frame->data[0];
- av_log(priv->avctx, AV_LOG_VERBOSE, "CrystalHD: Copying out frame\n");
- /*
- * The hardware doesn't return the first sample of a picture.
- * Ignoring why it behaves this way, it's better to copy the sample from
- * the second line, rather than the next sample across because the chroma
- * values should be correct (assuming the decoded video was 4:2:0, which
- * it was).
- */
- *((uint32_t *)src) = *((uint32_t *)(src + sStride));
- if (interlaced) {
- int dY = 0;
- int sY = 0;
- height /= 2;
- if (bottom_field) {
- av_log(priv->avctx, AV_LOG_VERBOSE, "Interlaced: bottom field\n");
- dY = 1;
- } else {
- av_log(priv->avctx, AV_LOG_VERBOSE, "Interlaced: top field\n");
- dY = 0;
- }
- for (sY = 0; sY < height; dY++, sY++) {
- memcpy(&(dst[dY * dStride]), &(src[sY * sStride]), bwidth);
- dY++;
- }
- } else {
- av_image_copy_plane(dst, dStride, src, sStride, bwidth, height);
- }
- frame->interlaced_frame = interlaced;
- if (interlaced)
- frame->top_field_first = !bottom_first;
- frame->pts = pkt_pts;
- #if FF_API_PKT_PTS
- FF_DISABLE_DEPRECATION_WARNINGS
- frame->pkt_pts = pkt_pts;
- FF_ENABLE_DEPRECATION_WARNINGS
- #endif
- frame->pkt_pos = -1;
- frame->pkt_duration = 0;
- frame->pkt_size = -1;
- if (!priv->need_second_field) {
- *got_frame = 1;
- } else {
- return RET_COPY_AGAIN;
- }
- return RET_OK;
- }
- static inline CopyRet receive_frame(AVCodecContext *avctx,
- AVFrame *frame, int *got_frame)
- {
- BC_STATUS ret;
- BC_DTS_PROC_OUT output = {
- .PicInfo.width = avctx->width,
- .PicInfo.height = avctx->height,
- };
- CHDContext *priv = avctx->priv_data;
- HANDLE dev = priv->dev;
- *got_frame = 0;
- // Request decoded data from the driver
- ret = DtsProcOutputNoCopy(dev, OUTPUT_PROC_TIMEOUT, &output);
- if (ret == BC_STS_FMT_CHANGE) {
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Initial format change\n");
- avctx->width = output.PicInfo.width;
- avctx->height = output.PicInfo.height;
- switch ( output.PicInfo.aspect_ratio ) {
- case vdecAspectRatioSquare:
- avctx->sample_aspect_ratio = (AVRational) { 1, 1};
- break;
- case vdecAspectRatio12_11:
- avctx->sample_aspect_ratio = (AVRational) { 12, 11};
- break;
- case vdecAspectRatio10_11:
- avctx->sample_aspect_ratio = (AVRational) { 10, 11};
- break;
- case vdecAspectRatio16_11:
- avctx->sample_aspect_ratio = (AVRational) { 16, 11};
- break;
- case vdecAspectRatio40_33:
- avctx->sample_aspect_ratio = (AVRational) { 40, 33};
- break;
- case vdecAspectRatio24_11:
- avctx->sample_aspect_ratio = (AVRational) { 24, 11};
- break;
- case vdecAspectRatio20_11:
- avctx->sample_aspect_ratio = (AVRational) { 20, 11};
- break;
- case vdecAspectRatio32_11:
- avctx->sample_aspect_ratio = (AVRational) { 32, 11};
- break;
- case vdecAspectRatio80_33:
- avctx->sample_aspect_ratio = (AVRational) { 80, 33};
- break;
- case vdecAspectRatio18_11:
- avctx->sample_aspect_ratio = (AVRational) { 18, 11};
- break;
- case vdecAspectRatio15_11:
- avctx->sample_aspect_ratio = (AVRational) { 15, 11};
- break;
- case vdecAspectRatio64_33:
- avctx->sample_aspect_ratio = (AVRational) { 64, 33};
- break;
- case vdecAspectRatio160_99:
- avctx->sample_aspect_ratio = (AVRational) {160, 99};
- break;
- case vdecAspectRatio4_3:
- avctx->sample_aspect_ratio = (AVRational) { 4, 3};
- break;
- case vdecAspectRatio16_9:
- avctx->sample_aspect_ratio = (AVRational) { 16, 9};
- break;
- case vdecAspectRatio221_1:
- avctx->sample_aspect_ratio = (AVRational) {221, 1};
- break;
- }
- return RET_COPY_AGAIN;
- } else if (ret == BC_STS_SUCCESS) {
- int copy_ret = -1;
- if (output.PoutFlags & BC_POUT_FLAGS_PIB_VALID) {
- print_frame_info(priv, &output);
- copy_ret = copy_frame(avctx, &output, frame, got_frame);
- } else {
- /*
- * An invalid frame has been consumed.
- */
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput succeeded with "
- "invalid PIB\n");
- copy_ret = RET_COPY_AGAIN;
- }
- DtsReleaseOutputBuffs(dev, NULL, FALSE);
- return copy_ret;
- } else if (ret == BC_STS_BUSY) {
- return RET_COPY_AGAIN;
- } else {
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput failed %d\n", ret);
- return RET_ERROR;
- }
- }
- static int crystalhd_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt)
- {
- BC_STATUS bc_ret;
- CHDContext *priv = avctx->priv_data;
- HANDLE dev = priv->dev;
- int ret = 0;
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: decode_packet\n");
- if (avpkt && avpkt->size) {
- uint64_t pts;
- /*
- * Despite being notionally opaque, either libcrystalhd or
- * the hardware itself will mangle pts values that are too
- * small or too large. The docs claim it should be in units
- * of 100ns. Given that we're nominally dealing with a black
- * box on both sides, any transform we do has no guarantee of
- * avoiding mangling so we need to build a mapping to values
- * we know will not be mangled.
- */
- pts = opaque_list_push(priv, avpkt->pts);
- if (!pts) {
- ret = AVERROR(ENOMEM);
- goto exit;
- }
- av_log(priv->avctx, AV_LOG_VERBOSE,
- "input \"pts\": %"PRIu64"\n", pts);
- bc_ret = DtsProcInput(dev, avpkt->data, avpkt->size, pts, 0);
- if (bc_ret == BC_STS_BUSY) {
- av_log(avctx, AV_LOG_WARNING,
- "CrystalHD: ProcInput returned busy\n");
- ret = AVERROR(EAGAIN);
- goto exit;
- } else if (bc_ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR,
- "CrystalHD: ProcInput failed: %u\n", ret);
- ret = -1;
- goto exit;
- }
- } else {
- av_log(avctx, AV_LOG_INFO, "CrystalHD: No more input data\n");
- priv->draining = 1;
- ret = AVERROR_EOF;
- goto exit;
- }
- exit:
- return ret;
- }
- static int crystalhd_receive_frame(AVCodecContext *avctx, AVFrame *frame)
- {
- BC_STATUS bc_ret;
- BC_DTS_STATUS decoder_status = { 0, };
- CopyRet rec_ret;
- CHDContext *priv = avctx->priv_data;
- HANDLE dev = priv->dev;
- int got_frame = 0;
- int ret = 0;
- AVPacket pkt = {0};
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: receive_frame\n");
- ret = ff_decode_get_packet(avctx, &pkt);
- if (ret < 0 && ret != AVERROR_EOF) {
- return ret;
- }
- while (pkt.size > DtsTxFreeSize(dev)) {
- /*
- * Block until there is space in the buffer for the next packet.
- * We assume that the hardware will make forward progress at this
- * point, although in pathological cases that may not happen.
- */
- av_log(avctx, AV_LOG_TRACE, "CrystalHD: Waiting for space in input buffer\n");
- }
- ret = crystalhd_decode_packet(avctx, &pkt);
- av_packet_unref(&pkt);
- // crystalhd_is_buffer_full() should avoid this.
- if (ret == AVERROR(EAGAIN)) {
- ret = AVERROR_EXTERNAL;
- }
- if (ret < 0 && ret != AVERROR_EOF) {
- return ret;
- }
- do {
- bc_ret = DtsGetDriverStatus(dev, &decoder_status);
- if (bc_ret != BC_STS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "CrystalHD: GetDriverStatus failed\n");
- return -1;
- }
- if (decoder_status.ReadyListCount == 0) {
- av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Insufficient frames ready. Returning\n");
- got_frame = 0;
- rec_ret = RET_OK;
- break;
- }
- rec_ret = receive_frame(avctx, frame, &got_frame);
- } while (rec_ret == RET_COPY_AGAIN);
- if (rec_ret == RET_ERROR) {
- return -1;
- } else if (got_frame == 0) {
- return priv->draining ? AVERROR_EOF : AVERROR(EAGAIN);
- } else {
- return 0;
- }
- }
- #define DEFINE_CRYSTALHD_DECODER(x, X, bsf_name) \
- static const AVClass x##_crystalhd_class = { \
- .class_name = #x "_crystalhd", \
- .item_name = av_default_item_name, \
- .option = options, \
- .version = LIBAVUTIL_VERSION_INT, \
- }; \
- AVCodec ff_##x##_crystalhd_decoder = { \
- .name = #x "_crystalhd", \
- .long_name = NULL_IF_CONFIG_SMALL("CrystalHD " #X " decoder"), \
- .type = AVMEDIA_TYPE_VIDEO, \
- .id = AV_CODEC_ID_##X, \
- .priv_data_size = sizeof(CHDContext), \
- .priv_class = &x##_crystalhd_class, \
- .init = init, \
- .close = uninit, \
- .receive_frame = crystalhd_receive_frame, \
- .flush = flush, \
- .bsfs = bsf_name, \
- .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \
- .pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_YUYV422, AV_PIX_FMT_NONE}, \
- .wrapper_name = "crystalhd", \
- };
- #if CONFIG_H264_CRYSTALHD_DECODER
- DEFINE_CRYSTALHD_DECODER(h264, H264, "h264_mp4toannexb")
- #endif
- #if CONFIG_MPEG2_CRYSTALHD_DECODER
- DEFINE_CRYSTALHD_DECODER(mpeg2, MPEG2VIDEO, NULL)
- #endif
- #if CONFIG_MPEG4_CRYSTALHD_DECODER
- DEFINE_CRYSTALHD_DECODER(mpeg4, MPEG4, "mpeg4_unpack_bframes")
- #endif
- #if CONFIG_MSMPEG4_CRYSTALHD_DECODER
- DEFINE_CRYSTALHD_DECODER(msmpeg4, MSMPEG4V3, NULL)
- #endif
- #if CONFIG_VC1_CRYSTALHD_DECODER
- DEFINE_CRYSTALHD_DECODER(vc1, VC1, NULL)
- #endif
- #if CONFIG_WMV3_CRYSTALHD_DECODER
- DEFINE_CRYSTALHD_DECODER(wmv3, WMV3, NULL)
- #endif
|