static vod_status_t 
audio_filter_update_track(audio_filter_state_t* state)
{
	media_track_t* output = state->output;
	input_frame_t* cur_frame;
	input_frame_t* last_frame;
	uint32_t old_timescale;
	vod_status_t rc;
	u_char* new_extra_data;
	bool_t has_frames;

	if (state->sink.encoder->time_base.num != 1)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_update_track: unexpected encoder time base %d/%d", 
			state->sink.encoder->time_base.num, state->sink.encoder->time_base.den);
		return VOD_UNEXPECTED;
	}

	// decrement the old frame count and size
	state->sequence->total_frame_count -= output->frame_count;
	state->sequence->total_frame_size -= output->total_frames_size;
	output->total_frames_size = 0;
	output->total_frames_duration = 0;

	// update frames
	output->frame_count = state->frames_array.nelts;

	output->frames.first_frame = state->frames_array.elts;
	output->frames.last_frame = output->frames.first_frame + output->frame_count;
	output->frames.next = NULL;

	// check whether there are any frames with duration
	has_frames = FALSE;
	
	// Note: always a single part here
	last_frame = output->frames.last_frame;
	for (cur_frame = output->frames.first_frame; cur_frame < last_frame; cur_frame++)
	{
		if (cur_frame->duration != 0)
		{
			has_frames = TRUE;
			break;
		}
	}

	if (!has_frames)
	{
		output->frames.first_frame = NULL;
		output->frames.last_frame = NULL;
		output->frame_count = 0;
		return VOD_OK;
	}
	
	// update the frames source to memory
	rc = frames_source_memory_init(state->request_context, &output->frames.frames_source_context);
	if (rc != VOD_OK)
	{
		return rc;
	}

	output->frames.frames_source = &frames_source_memory;

	// calculate the total frames size and duration
	output->media_info.min_frame_duration = 0;
	
	for (cur_frame = output->frames.first_frame; cur_frame < last_frame; cur_frame++)
	{
		output->total_frames_size += cur_frame->size;
		output->total_frames_duration += cur_frame->duration;
		
		if (cur_frame->duration != 0 && 
			(output->media_info.min_frame_duration == 0 || cur_frame->duration < output->media_info.min_frame_duration))
		{
			output->media_info.min_frame_duration = cur_frame->duration;
		}
	}
	
	// update media info
	old_timescale = output->media_info.timescale;
	output->media_info.timescale = state->sink.encoder->time_base.den;
	output->media_info.duration = rescale_time(output->media_info.duration, old_timescale, output->media_info.timescale);
	output->media_info.bitrate = state->sink.encoder->bit_rate;
	
	output->media_info.u.audio.object_type_id = 0x40;		// ffmpeg always writes 0x40 (ff_mp4_obj_type)
	output->media_info.u.audio.channels = state->sink.encoder->channels;
	output->media_info.u.audio.bits_per_sample = ENCODER_BITS_PER_SAMPLE;
	output->media_info.u.audio.packet_size = 0;				// ffmpeg always writes 0 (mov_write_audio_tag)
	output->media_info.u.audio.sample_rate = state->sink.encoder->sample_rate;
	
	output->key_frame_count = 0;
	output->first_frame_time_offset = rescale_time(output->first_frame_time_offset, old_timescale, output->media_info.timescale);
	
	new_extra_data = vod_alloc(state->request_context->pool, state->sink.encoder->extradata_size);
	if (new_extra_data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
			"audio_filter_update_track: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	vod_memcpy(new_extra_data, state->sink.encoder->extradata, state->sink.encoder->extradata_size);
	
	output->media_info.extra_data.data = new_extra_data;
	output->media_info.extra_data.len = state->sink.encoder->extradata_size;

	if (output->media_info.codec_name.data != NULL)
	{
		rc = codec_config_get_audio_codec_name(state->request_context, &output->media_info);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}
		
	// add the new frame count and size
	state->sequence->total_frame_count += output->frame_count;
	state->sequence->total_frame_size += output->total_frames_size;

	// TODO: update raw_atoms
	
	return VOD_OK;
}
vod_status_t 
audio_filter_update_stream_metadata(audio_filter_state_t* state)
{
	mpeg_stream_metadata_t* output = state->output;
	input_frame_t* cur_frame;
	input_frame_t* last_frame;
	uint32_t old_timescale;
	vod_status_t rc;
	u_char* new_extra_data;

	if (state->encoder->time_base.num != 1)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_update_stream_metadata: unexpected encoder time base num %d", state->encoder->time_base.num);
		return VOD_UNEXPECTED;
	}

	output->frames = state->frames_array.elts;
	output->frame_count = state->frames_array.nelts;
	output->frame_offsets = state->frame_offsets_array.elts;
	
	output->total_frames_size = 0;
	output->total_frames_duration = 0;
	output->media_info.min_frame_duration = 0;
	output->media_info.max_frame_duration = 0;
	
	last_frame = output->frames + output->frame_count;
	for (cur_frame = output->frames; cur_frame < last_frame; cur_frame++)
	{
		output->total_frames_size += cur_frame->size;
		output->total_frames_duration += cur_frame->duration;
		
		if (cur_frame->duration != 0 && 
			(output->media_info.min_frame_duration == 0 || cur_frame->duration < output->media_info.min_frame_duration))
		{
			output->media_info.min_frame_duration = cur_frame->duration;
		}

		if (cur_frame->duration > output->media_info.max_frame_duration)
		{
			output->media_info.max_frame_duration = cur_frame->duration;
		}
	}
	
	if (output->media_info.min_frame_duration == 0)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_update_stream_metadata: min frame duration is zero");
		return VOD_UNEXPECTED;
	}

	old_timescale = output->media_info.timescale;
	output->media_info.timescale = state->encoder->time_base.den;
	output->media_info.duration = rescale_time(output->media_info.duration, old_timescale, output->media_info.timescale);
	output->media_info.bitrate = state->encoder->bit_rate;

	output->media_info.speed_nom = 1;
	output->media_info.speed_denom = 1;
	
	output->media_info.u.audio.object_type_id = 0x40;		// ffmpeg always writes 0x40 (ff_mp4_obj_type)
	output->media_info.u.audio.channels = state->encoder->channels;
	output->media_info.u.audio.bits_per_sample = ENCODER_BITS_PER_SAMPLE;
	output->media_info.u.audio.packet_size = 0;				// ffmpeg always writes 0 (mov_write_audio_tag)
	output->media_info.u.audio.sample_rate = state->encoder->sample_rate;
	
	output->key_frame_count = 0;
	output->first_frame_time_offset = rescale_time(output->first_frame_time_offset, old_timescale, output->media_info.timescale);
	
	new_extra_data = vod_alloc(state->request_context->pool, state->encoder->extradata_size);
	if (new_extra_data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
			"audio_filter_update_stream_metadata: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	vod_memcpy(new_extra_data, state->encoder->extradata, state->encoder->extradata_size);
	
	output->media_info.extra_data = new_extra_data;
	output->media_info.extra_data_size = state->encoder->extradata_size;

	if (output->media_info.codec_name.data != NULL)
	{
		rc = codec_config_get_audio_codec_name(state->request_context, &output->media_info);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}
	
	output->frames_file_index = INVALID_FILE_INDEX;		// the frames are now in memory
	
	// TODO: update raw_atoms
	
	return VOD_OK;
}