diff --git a/configure b/configure index a07adae8e8..54973e959b 100755 --- a/configure +++ b/configure @@ -1276,7 +1276,7 @@ h263_vaapi_hwaccel_select="vaapi h263_decoder" h263i_decoder_select="h263_decoder" h263p_encoder_select="h263_encoder" h264_decoder_select="golomb h264dsp h264pred" -h264_crystalhd_decoder_select="crystalhd h264_mp4toannexb_bsf h264_parser" +h264_crystalhd_decoder_select="crystalhd h264_mp4toannexb_bsf" h264_dxva2_hwaccel_deps="dxva2api_h" h264_dxva2_hwaccel_select="dxva2 h264_decoder" h264_vaapi_hwaccel_select="vaapi" diff --git a/libavcodec/crystalhd.c b/libavcodec/crystalhd.c index 452edeff43..a773b848df 100644 --- a/libavcodec/crystalhd.c +++ b/libavcodec/crystalhd.c @@ -1,7 +1,7 @@ /* * - CrystalHD decoder module - * - * Copyright(C) 2010,2011 Philip Langdale + * Copyright(C) 2010 Philip Langdale * * This file is part of FFmpeg. * @@ -84,7 +84,6 @@ #include #include "avcodec.h" -#include "h264.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" @@ -103,18 +102,16 @@ ****************************************************************************/ typedef enum { - RET_ERROR = -1, - RET_OK = 0, - RET_COPY_AGAIN = 1, - RET_SKIP_NEXT_COPY = 2, - RET_COPY_NEXT_FIELD = 3, + RET_ERROR = -1, + RET_OK = 0, + RET_COPY_AGAIN = 1, + RET_SKIP_NEXT_COPY = 2, } CopyRet; typedef struct OpaqueList { struct OpaqueList *next; uint64_t fake_timestamp; uint64_t reordered_opaque; - uint8_t pic_type; } OpaqueList; typedef struct { @@ -122,8 +119,6 @@ typedef struct { AVFrame pic; HANDLE dev; - AVCodecParserContext *parser; - uint8_t is_70012; uint8_t *sps_pps_buf; uint32_t sps_pps_size; @@ -210,8 +205,7 @@ static inline void print_frame_info(CHDContext *priv, BC_DTS_PROC_OUT *output) * OpaqueList functions ****************************************************************************/ -static uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque, - uint8_t pic_type) +static uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque) { OpaqueList *newNode = av_mallocz(sizeof (OpaqueList)); if (!newNode) { @@ -228,7 +222,6 @@ static uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque, } priv->tail = newNode; newNode->reordered_opaque = reordered_opaque; - newNode->pic_type = pic_type; return newNode->fake_timestamp; } @@ -238,15 +231,14 @@ static uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque, * in presentation order. If frames are reordered, this means we must be * able to remove elements that are not the first element. */ -static uint8_t opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp, - uint64_t *reordered_opaque, uint8_t *pic_type) +static uint64_t 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 FALSE; + return AV_NOPTS_VALUE; } /* @@ -254,15 +246,14 @@ static uint8_t opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp, * the head pointer rather than the previous element in the list. */ if (priv->head->fake_timestamp == fake_timestamp) { - *reordered_opaque = node->reordered_opaque; - *pic_type = node->pic_type; + uint64_t reordered_opaque = node->reordered_opaque; priv->head = node->next; av_free(node); if (!priv->head->next) priv->tail = priv->head; - return TRUE; + return reordered_opaque; } /* @@ -272,15 +263,14 @@ static uint8_t opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp, while (node->next) { OpaqueList *next = node->next; if (next->fake_timestamp == fake_timestamp) { - *reordered_opaque = node->reordered_opaque; - *pic_type = node->pic_type; + uint64_t reordered_opaque = next->reordered_opaque; node->next = next->next; av_free(next); if (!node->next) priv->tail = node; - return TRUE; + return reordered_opaque; } else { node = next; } @@ -288,7 +278,7 @@ static uint8_t opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp, av_log(priv->avctx, AV_LOG_VERBOSE, "CrystalHD: Couldn't match fake_timestamp.\n"); - return FALSE; + return AV_NOPTS_VALUE; } @@ -325,8 +315,6 @@ static av_cold int uninit(AVCodecContext *avctx) DtsCloseDecoder(device); DtsDeviceClose(device); - av_parser_close(priv->parser); - av_free(priv->sps_pps_buf); if (priv->pic.data[0]) @@ -489,13 +477,6 @@ static av_cold int init(AVCodecContext *avctx) goto fail; } - if (avctx->codec->id == CODEC_ID_H264) { - priv->parser = av_parser_init(avctx->codec->id); - if (!priv->parser) - av_log(avctx, AV_LOG_WARNING, - "Cannot open the h.264 parser! Interlaced h.264 content " - "will not be detected reliably.\n"); - } av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Init complete.\n"); return 0; @@ -511,24 +492,30 @@ static av_cold int init(AVCodecContext *avctx) * us to distinguish between specific cases that require different handling. * So, for now, we have to hard-code the behaviour we want. * - * Specifically, there are PAFF samples where input is always separate fields - * but the hardware returns separate fields on one occasion and a field-pair - * on another. The code assumes the first case and define - * ASSUME_TWO_INPUTS_ONE_OUTPUT to assume the second case. + * The default behaviour is to assume MBAFF with input and output fieldpairs. + * + * Define ASSUME_PAFF_OVER_MBAFF to treat input as PAFF with separate input + * and output fields. + * + * Define ASSUME_TWO_INPUTS_ONE_OUTPUT to treat input as separate fields but + * output as a single fieldpair. + * + * Define both to mess up your playback. */ +#define ASSUME_PAFF_OVER_MBAFF 0 #define ASSUME_TWO_INPUTS_ONE_OUTPUT 0 static inline CopyRet copy_frame(AVCodecContext *avctx, BC_DTS_PROC_OUT *output, - void *data, int *data_size) + void *data, int *data_size, + uint8_t second_field) { BC_STATUS ret; BC_DTS_STATUS decoder_status; - uint8_t ignore_interlaced; + uint8_t is_paff; + uint8_t next_frame_same; uint8_t interlaced; CHDContext *priv = avctx->priv_data; - int64_t pkt_pts = AV_NOPTS_VALUE; - uint8_t pic_type = 0; uint8_t bottom_field = (output->PicInfo.flags & VDEC_FLAG_BOTTOMFIELD) == VDEC_FLAG_BOTTOMFIELD; @@ -542,27 +529,6 @@ static inline CopyRet copy_frame(AVCodecContext *avctx, uint8_t *dst; int dStride; - if (output->PicInfo.timeStamp != 0) { - uint8_t pop_ret; - pop_ret = opaque_list_pop(priv, output->PicInfo.timeStamp, - &pkt_pts, &pic_type); - if (!pop_ret) { - /* - * 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. To keep subsequent calculations - * simple, pic_type should be set a FIELD value - doesn't - * matter which, but I chose BOTTOM. - */ - pic_type = PICT_BOTTOM_FIELD; - } - av_log(avctx, AV_LOG_VERBOSE, "output \"pts\": %"PRIu64"\n", - output->PicInfo.timeStamp); - av_log(avctx, AV_LOG_VERBOSE, "output picture type %d\n", - pic_type); - } - ret = DtsGetDriverStatus(priv->dev, &decoder_status); if (ret != BC_STS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, @@ -570,19 +536,17 @@ static inline CopyRet copy_frame(AVCodecContext *avctx, return RET_ERROR; } - /* - * Testing has, so far, shown that we can't trust the interlaced flag for - * H.264 content when VDEC_FLAG_UNKNOWN_SRC is set. - */ - ignore_interlaced = avctx->codec->id == CODEC_ID_H264 && - (output->PicInfo.flags & VDEC_FLAG_UNKNOWN_SRC) && - (pic_type == 0 || pic_type == PICT_FRAME || - ASSUME_TWO_INPUTS_ONE_OUTPUT); - interlaced = (output->PicInfo.flags & VDEC_FLAG_INTERLACED_SRC) && - !ignore_interlaced; + is_paff = ASSUME_PAFF_OVER_MBAFF || + !(output->PicInfo.flags & VDEC_FLAG_UNKNOWN_SRC); + next_frame_same = output->PicInfo.picture_number == + (decoder_status.picNumFlags & ~0x40000000); + interlaced = ((output->PicInfo.flags & + VDEC_FLAG_INTERLACED_SRC) && is_paff) || + next_frame_same || bottom_field || second_field; - av_log(avctx, AV_LOG_VERBOSE, "Interlaced state: %d | ignore_interlaced %d\n", - interlaced, ignore_interlaced); + av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: next_frame_same: %u | %u | %u\n", + next_frame_same, output->PicInfo.picture_number, + decoder_status.picNumFlags & ~0x40000000); if (priv->pic.data[0] && !priv->need_second_field) avctx->release_buffer(avctx, &priv->pic); @@ -633,7 +597,8 @@ static inline CopyRet copy_frame(AVCodecContext *avctx, for (sY = 0; sY < height; dY++, sY++) { memcpy(&(dst[dY * dStride]), &(src[sY * sStride]), bwidth); - dY++; + if (interlaced) + dY++; } } else { av_image_copy_plane(dst, dStride, src, sStride, bwidth, height); @@ -643,7 +608,11 @@ static inline CopyRet copy_frame(AVCodecContext *avctx, if (interlaced) priv->pic.top_field_first = !bottom_first; - priv->pic.pkt_pts = pkt_pts; + if (output->PicInfo.timeStamp != 0) { + priv->pic.pkt_pts = opaque_list_pop(priv, output->PicInfo.timeStamp); + av_log(avctx, AV_LOG_VERBOSE, "output \"pts\": %"PRIu64"\n", + priv->pic.pkt_pts); + } if (!priv->need_second_field) { *data_size = sizeof(AVFrame); @@ -656,18 +625,13 @@ static inline CopyRet copy_frame(AVCodecContext *avctx, return RET_SKIP_NEXT_COPY; } - /* - * Testing has shown that in all cases where we don't want to return the - * full frame immediately, VDEC_FLAG_UNKNOWN_SRC is set. - */ - return priv->need_second_field && - !(output->PicInfo.flags & VDEC_FLAG_UNKNOWN_SRC) ? - RET_COPY_NEXT_FIELD : RET_OK; + return RET_OK; } static inline CopyRet receive_frame(AVCodecContext *avctx, - void *data, int *data_size) + void *data, int *data_size, + uint8_t second_field) { BC_STATUS ret; BC_DTS_PROC_OUT output = { @@ -724,7 +688,7 @@ static inline CopyRet receive_frame(AVCodecContext *avctx, priv->last_picture = output.PicInfo.picture_number - 1; } - copy_ret = copy_frame(avctx, &output, data, data_size); + copy_ret = copy_frame(avctx, &output, data, data_size, second_field); if (*data_size > 0) { avctx->has_b_frames--; priv->last_picture++; @@ -760,29 +724,11 @@ static int decode(AVCodecContext *avctx, void *data, int *data_size, AVPacket *a CHDContext *priv = avctx->priv_data; HANDLE dev = priv->dev; int len = avpkt->size; - uint8_t pic_type = 0; av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: decode_frame\n"); if (len) { int32_t tx_free = (int32_t)DtsTxFreeSize(dev); - - if (priv->parser) { - uint8_t *pout = NULL; - int psize = len; - H264Context *h = priv->parser->priv_data; - - while (psize) { - ret = av_parser_parse2(priv->parser, avctx, &pout, &psize, - avpkt->data, len, avctx->pkt->pts, - avctx->pkt->dts, len - psize); - } - av_log(avctx, AV_LOG_VERBOSE, - "CrystalHD: parser picture type %d\n", - h->s.picture_structure); - pic_type = h->s.picture_structure; - } - if (len < tx_free - 1024) { /* * Despite being notionally opaque, either libcrystalhd or @@ -793,7 +739,7 @@ static int decode(AVCodecContext *avctx, void *data, int *data_size, AVPacket *a * avoiding mangling so we need to build a mapping to values * we know will not be mangled. */ - uint64_t pts = opaque_list_push(priv, avctx->pkt->pts, pic_type); + uint64_t pts = opaque_list_push(priv, avctx->pkt->pts); if (!pts) { return AVERROR(ENOMEM); } @@ -860,39 +806,41 @@ static int decode(AVCodecContext *avctx, void *data, int *data_size, AVPacket *a } do { - rec_ret = receive_frame(avctx, data, data_size); - if (rec_ret == RET_OK && *data_size == 0) { - /* - * This case is for when the encoded fields are stored - * separately and we get a separate avpkt for each one. To keep - * the pipeline stable, we should return nothing and wait for - * the next time round to grab the second field. - * H.264 PAFF is an example of this. - */ - av_log(avctx, AV_LOG_VERBOSE, "Returning after first field.\n"); - avctx->has_b_frames--; - } else if (rec_ret == RET_COPY_NEXT_FIELD) { - /* - * This case is for when the encoded fields are stored in a - * single avpkt but the hardware returns then separately. Unless - * we grab the second field before returning, we'll slip another - * frame in the pipeline and if that happens a lot, we're sunk. - * So we have to get that second field now. - * Interlaced mpeg2 and vc1 are examples of this. - */ - av_log(avctx, AV_LOG_VERBOSE, "Trying to get second field.\n"); - while (1) { - usleep(priv->decode_wait); - ret = DtsGetDriverStatus(dev, &decoder_status); - if (ret == BC_STS_SUCCESS && - decoder_status.ReadyListCount > 0) { - rec_ret = receive_frame(avctx, data, data_size); - if ((rec_ret == RET_OK && *data_size > 0) || - rec_ret == RET_ERROR) - break; + rec_ret = receive_frame(avctx, data, data_size, 0); + if (rec_ret == 0 && *data_size == 0) { + if (avctx->codec->id == CODEC_ID_H264) { + /* + * This case is for when the encoded fields are stored + * separately and we get a separate avpkt for each one. To keep + * the pipeline stable, we should return nothing and wait for + * the next time round to grab the second field. + * H.264 PAFF is an example of this. + */ + av_log(avctx, AV_LOG_VERBOSE, "Returning after first field.\n"); + avctx->has_b_frames--; + } else { + /* + * This case is for when the encoded fields are stored in a + * single avpkt but the hardware returns then separately. Unless + * we grab the second field before returning, we'll slip another + * frame in the pipeline and if that happens a lot, we're sunk. + * So we have to get that second field now. + * Interlaced mpeg2 and vc1 are examples of this. + */ + av_log(avctx, AV_LOG_VERBOSE, "Trying to get second field.\n"); + while (1) { + usleep(priv->decode_wait); + ret = DtsGetDriverStatus(dev, &decoder_status); + if (ret == BC_STS_SUCCESS && + decoder_status.ReadyListCount > 0) { + rec_ret = receive_frame(avctx, data, data_size, 1); + if ((rec_ret == 0 && *data_size > 0) || + rec_ret == RET_ERROR) + break; + } } + av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Got second field.\n"); } - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Got second field.\n"); } else if (rec_ret == RET_SKIP_NEXT_COPY) { /* * Two input packets got turned into a field pair. Gawd.