Esempio n. 1
0
static int ndi_write_header(AVFormatContext *avctx)
{
    int ret = 0;
    unsigned int n;
    struct NDIContext *ctx = avctx->priv_data;
    const NDIlib_send_create_t ndi_send_desc = { .p_ndi_name = avctx->filename,
        .p_groups = NULL, .clock_video = ctx->clock_video, .clock_audio = ctx->clock_audio };

    if (!NDIlib_initialize()) {
        av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n");
        return AVERROR_EXTERNAL;
    }

    /* check if streams compatible */
    for (n = 0; n < avctx->nb_streams; n++) {
        AVStream *st = avctx->streams[n];
        AVCodecParameters *c = st->codecpar;
        if        (c->codec_type == AVMEDIA_TYPE_AUDIO) {
            if ((ret = ndi_setup_audio(avctx, st)))
                goto error;
        } else if (c->codec_type == AVMEDIA_TYPE_VIDEO) {
            if ((ret = ndi_setup_video(avctx, st)))
                goto error;
        } else {
            av_log(avctx, AV_LOG_ERROR, "Unsupported stream type.\n");
            ret = AVERROR(EINVAL);
            goto error;
        }
    }

    ctx->ndi_send = NDIlib_send_create(&ndi_send_desc);
    if (!ctx->ndi_send) {
        av_log(avctx, AV_LOG_ERROR, "Failed to create NDI output %s\n", avctx->filename);
        ret = AVERROR_EXTERNAL;
    }

error:
    return ret;
}

#define OFFSET(x) offsetof(struct NDIContext, x)
static const AVOption options[] = {
    { "reference_level", "The audio reference level in dB"  , OFFSET(reference_level), AV_OPT_TYPE_INT, { .i64 = 0 }, -20, 20, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM},
int _tmain(int argc, _TCHAR* argv[])
{	// Not required, but "correct" (see the SDK documentation.
	if (!NDIlib_initialize())
	{	// Cannot run NDI. Most likely because the CPU is not sufficient (see SDK documentation).
		// you can check this directly with a call to NDIlib_is_supported_CPU()
		printf("Cannot run NDI.");
		return 0;
	}
	
	// We are going to create an NDI finder that locates sources on the network.
	// including ones that are available on this machine itself. It will use the default
	// groups assigned for the current machine.
	const NDIlib_find_create_t NDI_find_create_desc = { TRUE, NULL };

	// We create the NDI finder
	NDIlib_find_instance_t pNDI_find = NDIlib_find_create(&NDI_find_create_desc);
	if (!pNDI_find) return FALSE;

	// Run for one minute
	for (DWORD start = ::GetTickCount(); ::GetTickCount() - start < 60000;)
	{	// Wait up till 10 seconds to check for new sources having been added or removed from the network.
		// Because we have specified a non zero timeout here, this will return NULL if there have been no changes.
		DWORD no_sources = 0;
		const NDIlib_source_t* p_sources = NDIlib_find_get_sources(pNDI_find, &no_sources, 10000);

		// If it returned NULL then where where no changes on the network. If we have specified a timeout of 0,
		// then it always returns all sources on the network.
		if (!p_sources) continue;

		// Display all the sources.
		printf("Network sources (%u found).\n", no_sources);
		for (DWORD i = 0; i < no_sources; i++)
			printf("%d. %s\n", i + 1, p_sources[i].p_ndi_name);
	}

	// Destroy the NDI finder
	NDIlib_find_destroy(pNDI_find);

	// Success. We are done
	return 0;
}
static int ndi_find_sources(AVFormatContext *avctx, const char *name, NDIlib_source_t *source_to_connect_to)
{
    int j = AVERROR(ENODEV);
    unsigned int n, i;
    struct NDIContext *ctx = avctx->priv_data;
    const NDIlib_source_t *ndi_srcs = NULL;
    const NDIlib_find_create_t find_create_desc = { .show_local_sources = true,
        .p_groups = NULL, .p_extra_ips = NULL };

    if (!ctx->ndi_find)
        ctx->ndi_find = NDIlib_find_create2(&find_create_desc);
    if (!ctx->ndi_find) {
        av_log(avctx, AV_LOG_ERROR, "NDIlib_find_create failed.\n");
        return AVERROR(EIO);
    }

    while (1)
    {
        int f, t = ctx->wait_sources / 1000;
        av_log(avctx, AV_LOG_DEBUG, "Waiting for sources %d miliseconds\n", t);
        f = NDIlib_find_wait_for_sources(ctx->ndi_find, t);
        av_log(avctx, AV_LOG_DEBUG, "NDIlib_find_wait_for_sources returns %d\n", f);
        if (!f)
            break;
    };

    ndi_srcs = NDIlib_find_get_current_sources(ctx->ndi_find, &n);

    if (ctx->find_sources)
        av_log(avctx, AV_LOG_INFO, "Found %d NDI sources:\n", n);

    for (i = 0; i < n; i++) {
        if (ctx->find_sources)
            av_log(avctx, AV_LOG_INFO, "\t'%s'\t'%s'\n", ndi_srcs[i].p_ndi_name, ndi_srcs[i].p_ip_address);

        if (!strcmp(name, ndi_srcs[i].p_ndi_name)) {
            *source_to_connect_to = ndi_srcs[i];
            j = i;
        }
    }

    return j;
}

static int ndi_read_header(AVFormatContext *avctx)
{
    int ret;
    NDIlib_recv_create_t recv_create_desc;
    const NDIlib_tally_t tally_state = { .on_program = true, .on_preview = false };
    struct NDIContext *ctx = avctx->priv_data;

    if (!NDIlib_initialize()) {
        av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n");
        return AVERROR_EXTERNAL;
    }

    /* Find available sources. */
    ret = ndi_find_sources(avctx, avctx->url, &recv_create_desc.source_to_connect_to);
    if (ctx->find_sources) {
        return AVERROR_EXIT;
    }
    if (ret < 0)
        return ret;

    /* Create receiver description */
    recv_create_desc.color_format = NDIlib_recv_color_format_e_UYVY_RGBA;
    recv_create_desc.bandwidth = NDIlib_recv_bandwidth_highest;
    recv_create_desc.allow_video_fields = ctx->allow_video_fields;

    /* Create the receiver */
    ctx->recv = NDIlib_recv_create(&recv_create_desc);
    if (!ctx->recv) {
        av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_create2 failed.\n");
        return AVERROR(EIO);
    }

    /* Set tally */
    NDIlib_recv_set_tally(ctx->recv, &tally_state);

    avctx->ctx_flags |= AVFMTCTX_NOHEADER;

    return 0;
}

static int ndi_create_video_stream(AVFormatContext *avctx, NDIlib_video_frame_t *v)
{
    AVStream *st;
    AVRational tmp;
    struct NDIContext *ctx = avctx->priv_data;

    st = avformat_new_stream(avctx, NULL);
    if (!st) {
        av_log(avctx, AV_LOG_ERROR, "Cannot add video stream\n");
        return AVERROR(ENOMEM);
    }

    st->time_base                   = NDI_TIME_BASE_Q;
    st->r_frame_rate                = av_make_q(v->frame_rate_N, v->frame_rate_D);

    tmp = av_mul_q(av_d2q(v->picture_aspect_ratio, INT_MAX), (AVRational){v->yres, v->xres});
    av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, tmp.num, tmp.den, 1000);
    st->codecpar->sample_aspect_ratio = st->sample_aspect_ratio;

    st->codecpar->codec_type        = AVMEDIA_TYPE_VIDEO;
    st->codecpar->width             = v->xres;
    st->codecpar->height            = v->yres;
    st->codecpar->codec_id          = AV_CODEC_ID_RAWVIDEO;
    st->codecpar->bit_rate          = av_rescale(v->xres * v->yres * 16, v->frame_rate_N, v->frame_rate_D);
    st->codecpar->field_order       = v->frame_format_type == NDIlib_frame_format_type_progressive
        ? AV_FIELD_PROGRESSIVE : AV_FIELD_TT;

    if (NDIlib_FourCC_type_UYVY == v->FourCC || NDIlib_FourCC_type_UYVA == v->FourCC) {
        st->codecpar->format        = AV_PIX_FMT_UYVY422;
        st->codecpar->codec_tag     = MKTAG('U', 'Y', 'V', 'Y');
        if (NDIlib_FourCC_type_UYVA == v->FourCC)
            av_log(avctx, AV_LOG_WARNING, "Alpha channel ignored\n");
    } else if (NDIlib_FourCC_type_BGRA == v->FourCC) {
        st->codecpar->format        = AV_PIX_FMT_BGRA;
        st->codecpar->codec_tag     = MKTAG('B', 'G', 'R', 'A');
    } else if (NDIlib_FourCC_type_BGRX == v->FourCC) {
        st->codecpar->format        = AV_PIX_FMT_BGR0;
        st->codecpar->codec_tag     = MKTAG('B', 'G', 'R', '0');
    } else if (NDIlib_FourCC_type_RGBA == v->FourCC) {
        st->codecpar->format        = AV_PIX_FMT_RGBA;
        st->codecpar->codec_tag     = MKTAG('R', 'G', 'B', 'A');
    } else if (NDIlib_FourCC_type_RGBX == v->FourCC) {
        st->codecpar->format        = AV_PIX_FMT_RGB0;
        st->codecpar->codec_tag     = MKTAG('R', 'G', 'B', '0');
    } else {
        av_log(avctx, AV_LOG_ERROR, "Unsupported video stream format, v->FourCC=%d\n", v->FourCC);
        return AVERROR(EINVAL);
    }

    avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE);

    ctx->video_st = st;

    return 0;
}