void PrivateDecoderCrystalHD::FetchFrames(void) { INIT_ST; bool valid = false; m_fetcher_paused = false; while (!m_fetcher_stop) { usleep(1000); if (m_fetcher_pause) { m_fetcher_paused = true; continue; } m_fetcher_paused = false; BC_DTS_STATUS status; st = DtsGetDriverStatus(m_device, &status); CHECK_ST; if (!status.ReadyListCount) continue; BC_DTS_PROC_OUT out; memset(&out, 0, sizeof(BC_DTS_PROC_OUT)); st = DtsProcOutputNoCopy(m_device, valid ? 2000 : 20, &out); if (BC_STS_FMT_CHANGE == st) { LOG(VB_GENERAL, LOG_INFO, LOC + "Decoder reported format change."); CheckProcOutput(&out); valid = true; continue; } CHECK_ST; if (!ok) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to fetch decoded frame"); continue; } if (ok && valid && (out.PoutFlags & BC_POUT_FLAGS_PIB_VALID)) FillFrame(&out); st = DtsReleaseOutputBuffs(m_device, NULL, false); CHECK_ST; } }
static inline CopyRet receive_frame(AVCodecContext *avctx, void *data, int *data_size, uint8_t second_field) { 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; *data_size = 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; return RET_COPY_AGAIN; } else if (ret == BC_STS_SUCCESS) { int copy_ret = -1; if (output.PoutFlags & BC_POUT_FLAGS_PIB_VALID) { if (priv->last_picture == -1) { /* * Init to one less, so that the incrementing code doesn't * need to be special-cased. */ priv->last_picture = output.PicInfo.picture_number - 1; } if (avctx->codec->id == CODEC_ID_MPEG4 && output.PicInfo.timeStamp == 0) { av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Not returning packed frame twice.\n"); priv->last_picture++; DtsReleaseOutputBuffs(dev, NULL, FALSE); return RET_COPY_AGAIN; } print_frame_info(priv, &output); if (priv->last_picture + 1 < output.PicInfo.picture_number) { av_log(avctx, AV_LOG_WARNING, "CrystalHD: Picture Number discontinuity\n"); /* * Have we lost frames? If so, we need to shrink the * pipeline length appropriately. * * XXX: I have no idea what the semantics of this situation * are so I don't even know if we've lost frames or which * ones. * * In any case, only warn the first time. */ priv->last_picture = output.PicInfo.picture_number - 1; } copy_ret = copy_frame(avctx, &output, data, data_size, second_field); if (*data_size > 0) { avctx->has_b_frames--; priv->last_picture++; av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Pipeline length: %u\n", avctx->has_b_frames); } } else { /* * An invalid frame has been consumed. */ av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput succeeded with " "invalid PIB\n"); avctx->has_b_frames--; copy_ret = RET_OK; } 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 decode(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt) { BC_STATUS ret; BC_DTS_STATUS decoder_status; CopyRet rec_ret; CHDContext *priv = avctx->priv_data; HANDLE dev = priv->dev; int len = avpkt->size; av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: decode_frame\n"); if (len) { int32_t tx_free = (int32_t)DtsTxFreeSize(dev); if (len < tx_free - 1024) { /* * 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. */ uint64_t pts = opaque_list_push(priv, avctx->pkt->pts); if (!pts) { return AVERROR(ENOMEM); } av_log(priv->avctx, AV_LOG_VERBOSE, "input \"pts\": %"PRIu64"\n", pts); ret = DtsProcInput(dev, avpkt->data, len, pts, 0); if (ret == BC_STS_BUSY) { av_log(avctx, AV_LOG_WARNING, "CrystalHD: ProcInput returned busy\n"); usleep(BASE_WAIT); return AVERROR(EBUSY); } else if (ret != BC_STS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcInput failed: %u\n", ret); return -1; } avctx->has_b_frames++; } else { av_log(avctx, AV_LOG_WARNING, "CrystalHD: Input buffer full\n"); len = 0; // We didn't consume any bytes. } } else { av_log(avctx, AV_LOG_INFO, "CrystalHD: No more input data\n"); } if (priv->skip_next_output) { av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Skipping next output.\n"); priv->skip_next_output = 0; avctx->has_b_frames--; return len; } ret = DtsGetDriverStatus(dev, &decoder_status); if (ret != BC_STS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "CrystalHD: GetDriverStatus failed\n"); return -1; } /* * No frames ready. Don't try to extract. * * Empirical testing shows that ReadyListCount can be a damn lie, * and ProcOut still fails when count > 0. The same testing showed * that two more iterations were needed before ProcOutput would * succeed. */ if (priv->output_ready < 2) { if (decoder_status.ReadyListCount != 0) priv->output_ready++; usleep(BASE_WAIT); av_log(avctx, AV_LOG_INFO, "CrystalHD: Filling pipeline.\n"); return len; } else if (decoder_status.ReadyListCount == 0) { /* * After the pipeline is established, if we encounter a lack of frames * that probably means we're not giving the hardware enough time to * decode them, so start increasing the wait time at the end of a * decode call. */ usleep(BASE_WAIT); priv->decode_wait += WAIT_UNIT; av_log(avctx, AV_LOG_INFO, "CrystalHD: No frames ready. Returning\n"); return len; } do { 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"); } } else if (rec_ret == RET_SKIP_NEXT_COPY) { /* * Two input packets got turned into a field pair. Gawd. */ av_log(avctx, AV_LOG_VERBOSE, "Don't output on next decode call.\n"); priv->skip_next_output = 1; } /* * If rec_ret == RET_COPY_AGAIN, that means that either we just handled * a FMT_CHANGE event and need to go around again for the actual frame, * we got a busy status and need to try again, or we're dealing with * packed b-frames, where the hardware strangely returns the packed * p-frame twice. We choose to keep the second copy as it carries the * valid pts. */ } while (rec_ret == RET_COPY_AGAIN); usleep(priv->decode_wait); return len; }