vod_status_t
adts_encoder_init(
    adts_encoder_state_t* state,
    request_context_t* request_context,
    hls_encryption_params_t* encryption_params,
    const media_filter_t* next_filter,
    void* next_filter_context,
    const u_char* extra_data,
    uint32_t extra_data_size)
{
    mp4a_config_t codec_config;
    vod_status_t rc;

    state->next_filter = next_filter;
    state->next_filter_context = next_filter_context;

    if (request_context->simulation_only)
    {
        return VOD_OK;
    }

    if (encryption_params->type == HLS_ENC_SAMPLE_AES)
    {
        rc = sample_aes_aac_filter_init(
                 &state->sample_aes_context,
                 request_context,
                 next_filter->write,
                 next_filter_context,
                 encryption_params->key,
                 encryption_params->iv);
        if (rc != VOD_OK)
        {
            return rc;
        }

        state->body_write = sample_aes_aac_filter_write_frame_body;
        state->body_write_context = state->sample_aes_context;
    }
    else
    {
        state->sample_aes_context = NULL;

        state->body_write = next_filter->write;
        state->body_write_context = next_filter_context;
    }

    rc = codec_config_mp4a_config_parse(request_context, extra_data, extra_data_size, &codec_config);
    if (rc != VOD_OK)
    {
        return rc;
    }

    // Note: not parsing all the special cases handled in ffmpeg's avpriv_mpeg4audio_get_config
    // Note: not handling pce_data

    vod_log_debug3(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
                   "adts_encoder_init: object_type=%d sample_rate_index=%d channel_config=%d",
                   codec_config.object_type, codec_config.sample_rate_index, codec_config.channel_config);

    vod_memzero(&state->header, sizeof(state->header));

    adts_frame_header_set_syncword(state->header, 0xfff);
    adts_frame_header_set_protection_absent(state->header, 1);
    adts_frame_header_set_profile_object_type(state->header, codec_config.object_type - 1);
    adts_frame_header_set_sample_rate_index(state->header, codec_config.sample_rate_index);
    adts_frame_header_set_channel_configuration(state->header, codec_config.channel_config);
    adts_frame_header_set_adts_buffer_fullness(state->header, 0x7ff);

    return VOD_OK;
}
vod_status_t
audio_filter_alloc_state(
	request_context_t* request_context,
	mpeg_stream_metadata_t* stream_metadata,
	void** result)
{
	char filter_desc[sizeof(ATEMPO_FILTER_DESCRIPTION) + 2 * VOD_INT64_LEN];
	audio_filter_state_t* state;
	uint32_t initial_alloc_size;
	mp4a_config_t codec_config;
	vod_status_t rc;
	int ret;
	
	if (!initialized)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: module failed to initialize successfully");
		return VOD_UNEXPECTED;
	}

	if (stream_metadata->media_info.speed_denom != 10)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: unexpected speed denom, must be 10");
		return VOD_UNEXPECTED;
	}

	// parse the codec config
	rc = codec_config_mp4a_config_parse(
		request_context, 
		stream_metadata->media_info.extra_data,
		stream_metadata->media_info.extra_data_size,
		&codec_config);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// allocate the state
	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	vod_memzero(state, sizeof(*state));
	
	state->request_context = request_context;
	state->speed_nom = stream_metadata->media_info.speed_nom;
	state->speed_denom = stream_metadata->media_info.speed_denom;
	state->output = stream_metadata;

	// init the decoder	
	state->decoder = avcodec_alloc_context3(decoder_codec);
	if (state->decoder == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_alloc_context3 failed (1)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}

	state->decoder->codec_tag = stream_metadata->media_info.format;
	state->decoder->bit_rate = stream_metadata->media_info.bitrate;
	state->decoder->time_base.num = 1;
	// Note: changing the timescale in order to revert the speed change that was applied to the frames while parsing the mp4
	state->decoder->time_base.den = stream_metadata->media_info.timescale / stream_metadata->media_info.speed_nom * stream_metadata->media_info.speed_denom;
	state->decoder->pkt_timebase = state->decoder->time_base;
	state->decoder->extradata = (u_char*)stream_metadata->media_info.extra_data;
	state->decoder->extradata_size = stream_metadata->media_info.extra_data_size;
	state->decoder->channels = stream_metadata->media_info.u.audio.channels;
	state->decoder->bits_per_coded_sample = stream_metadata->media_info.u.audio.bits_per_sample;
	state->decoder->sample_rate = stream_metadata->media_info.u.audio.sample_rate;
	state->decoder->channel_layout = 0;
	if (codec_config.channel_config < ARRAY_ENTRIES(aac_channel_layout))
	{
		state->decoder->channel_layout = aac_channel_layout[codec_config.channel_config];
	}
	
	ret = avcodec_open2(state->decoder, decoder_codec, NULL);
	if (ret < 0) 
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_open2(decoder) failed %d", ret);
		rc = VOD_UNEXPECTED;
		goto error;
	}
	
	// init the encoder
	state->encoder = avcodec_alloc_context3(encoder_codec);
	if (state->encoder == NULL) 
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_alloc_context3 failed (2)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}

	state->encoder->sample_fmt = ENCODER_INPUT_SAMPLE_FORMAT;
	state->encoder->sample_rate = state->decoder->sample_rate;
	state->encoder->channel_layout = state->decoder->channel_layout;
	state->encoder->channels = state->decoder->channels;
	state->encoder->bit_rate = state->decoder->bit_rate;
	state->encoder->flags |= CODEC_FLAG_GLOBAL_HEADER;		// make the codec generate the extra data

	ret = avcodec_open2(state->encoder, encoder_codec, NULL);
	if (ret < 0) 
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_open2(encoder) failed %d", ret);
		rc = VOD_UNEXPECTED;
		goto error;
	}
	
	// allocate frames
	state->decoded_frame = av_frame_alloc();
	if (state->decoded_frame == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: av_frame_alloc failed (1)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}
	state->filtered_frame = av_frame_alloc();
	if (state->filtered_frame == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: av_frame_alloc failed (2)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}

	// initialize the output arrays
	initial_alloc_size = (stream_metadata->frame_count * stream_metadata->media_info.speed_denom) / stream_metadata->media_info.speed_nom + 10;
	
	if (vod_array_init(&state->frames_array, request_context->pool, initial_alloc_size, sizeof(input_frame_t)) != VOD_OK)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_array_init failed (1)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}
	
	if (vod_array_init(&state->frame_offsets_array, request_context->pool, initial_alloc_size, sizeof(uint64_t)) != VOD_OK)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_array_init failed (2)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}
	
	// initialize the filter graph
	vod_sprintf(filter_desc, 
		ATEMPO_FILTER_DESCRIPTION, 
		(int)(stream_metadata->media_info.speed_nom / 10), 
		(int)(stream_metadata->media_info.speed_nom % 10));

	rc = audio_filter_init_filters(state, stream_metadata, filter_desc);
	if (rc != VOD_OK)
	{
		goto error;
	}
	
	*result = state;
	
	return VOD_OK;

error:

	audio_filter_free_state(state);

	return rc;
}