Ejemplo n.º 1
0
static u_char*
mss_write_manifest_chunks_live(u_char* p, segment_durations_t* segment_durations)
{
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count;
	uint32_t repeat_count;
	bool_t first_time = TRUE;

	for (cur_item = segment_durations->items; cur_item < last_item; cur_item++)
	{
		repeat_count = cur_item->repeat_count;

		// output the timestamp in the first chunk
		if (first_time)
		{
			p = vod_sprintf(p, MSS_CHUNK_TAG_LIVE_FIRST, 
				mss_rescale_millis(segment_durations->start_time), 
				rescale_time(cur_item->duration, segment_durations->timescale, MSS_TIMESCALE));
			repeat_count--;
			first_time = FALSE;
		}
		
		// output only the duration in subsequent chunks
		for (; repeat_count > 0; repeat_count--)
		{
			p = vod_sprintf(p, MSS_CHUNK_TAG_LIVE, rescale_time(cur_item->duration, segment_durations->timescale, MSS_TIMESCALE));
		}
	}

	return p;
}
Ejemplo n.º 2
0
static u_char* 
hds_calculate_output_offsets_and_write_afra_entries(
	hds_muxer_state_t* state, 
	uint32_t initial_value, 
	uint32_t afra_entries_base, 
	u_char* p)
{
	hds_muxer_stream_state_t* selected_stream;
	hds_muxer_stream_state_t* cur_stream;
	uint32_t cur_offset = initial_value;

	for (;;)
	{
		// choose a stream
		selected_stream = hds_muxer_choose_stream(state);
		if (selected_stream == NULL)
		{
			break;
		}

		// video key frames start with the codec info
		if (selected_stream->media_type == MEDIA_TYPE_VIDEO && selected_stream->cur_frame->key_frame)
		{
			p = hds_write_afra_atom_entry(p, 
				rescale_time(selected_stream->next_frame_dts, selected_stream->timescale, HDS_TIMESCALE),
				cur_offset + afra_entries_base);
			cur_offset += state->codec_config_size;
		}

		// skip the tag size
		cur_offset += tag_size_by_media_type[selected_stream->media_type];

		// set the offset (points to the beginning of the actual data)
		*selected_stream->cur_frame_output_offset = cur_offset;
		selected_stream->cur_frame_output_offset++;

		// move to the end of the frame
		cur_offset += selected_stream->cur_frame->size;
		cur_offset += sizeof(uint32_t);

		// move to the next frame
		selected_stream->next_frame_time_offset += selected_stream->cur_frame->duration;
		selected_stream->next_frame_dts = rescale_time(selected_stream->next_frame_time_offset, selected_stream->timescale, HDS_TIMESCALE);
		selected_stream->cur_frame++;
	}

	// reset the state
	for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++)
	{
		cur_stream->cur_frame = cur_stream->first_frame;
		cur_stream->cur_frame_output_offset = cur_stream->first_frame_output_offset;
		cur_stream->next_frame_time_offset = cur_stream->first_frame_time_offset;
		cur_stream->next_frame_dts = rescale_time(cur_stream->next_frame_time_offset, cur_stream->timescale, HDS_TIMESCALE);
	}

	return p;
}
Ejemplo n.º 3
0
static u_char*
mss_write_uuid_tfxd_atom(u_char* p, mpeg_stream_metadata_t* stream_metadata)
{
	size_t atom_size = ATOM_HEADER_SIZE + sizeof(uuid_tfxd_atom_t);
	uint64_t timestamp = rescale_time(stream_metadata->first_frame_time_offset, stream_metadata->media_info.timescale, MSS_TIMESCALE);
	uint64_t duration = rescale_time(stream_metadata->total_frames_duration, stream_metadata->media_info.timescale, MSS_TIMESCALE);

	write_atom_header(p, atom_size, 'u', 'u', 'i', 'd');
	p = vod_copy(p, tfxd_uuid, sizeof(tfxd_uuid));
	write_dword(p, 0x01000000);		// version / flags
	write_qword(p, timestamp);
	write_qword(p, duration);
	return p;
}
Ejemplo n.º 4
0
static void 
mss_get_segment_timing_info(media_sequence_t* sequence, segment_timing_info_t* result)
{
	media_clip_filtered_t* cur_clip;
	media_track_t* track;

	cur_clip = sequence->filtered_clips;
	track = cur_clip->first_track;
	result->timestamp = rescale_time(track->first_frame_time_offset, track->media_info.timescale, MSS_TIMESCALE) + mss_rescale_millis(track->clip_start_time);
	result->duration = rescale_time(track->total_frames_duration, track->media_info.timescale, MSS_TIMESCALE);
	cur_clip++;

	for (; cur_clip < sequence->filtered_clips_end; cur_clip++)
	{
		track = cur_clip->first_track;
		result->duration += rescale_time(track->total_frames_duration, track->media_info.timescale, MSS_TIMESCALE);
	}
}
Ejemplo n.º 5
0
static void
dash_packager_init_sidx_params(media_set_t* media_set, media_sequence_t* sequence, sidx_params_t* result)
{
	media_clip_filtered_t* cur_clip;
	media_track_t* track;
	uint64_t earliest_pres_time;
	uint64_t total_frames_duration;
	uint32_t timescale;

	// initialize according to the first clip
	cur_clip = sequence->filtered_clips;
	track = cur_clip->first_track;
	total_frames_duration = track->total_frames_duration;
	earliest_pres_time = dash_packager_get_earliest_pres_time(media_set, track);
	timescale = track->media_info.timescale;
	cur_clip++;

	for (; cur_clip < sequence->filtered_clips_end; cur_clip++)
	{
		track = cur_clip->first_track;

		if (timescale > track->media_info.timescale)
		{
			// scale the track duration to timescale
			total_frames_duration += rescale_time(track->total_frames_duration, track->media_info.timescale, timescale);
			continue;
		}

		if (timescale < track->media_info.timescale)
		{
			// rescale to track->media_info.timescale
			total_frames_duration = rescale_time(total_frames_duration, timescale, track->media_info.timescale);
			earliest_pres_time = rescale_time(earliest_pres_time, timescale, track->media_info.timescale);
			timescale = track->media_info.timescale;
		}

		// same timescale, just add
		total_frames_duration += track->total_frames_duration;
	}

	result->total_frames_duration = total_frames_duration;
	result->earliest_pres_time = earliest_pres_time;
	result->timescale = timescale;
}
Ejemplo n.º 6
0
static void
hds_scale_segment_durations(hds_segment_durations_t* segments)
{
	segment_durations_t* segment_durations = &segments->durations;
	segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count;
	segment_duration_item_t* cur_item;

	segments->zero_segments = 0;

	segment_durations->end_time = hds_rescale_millis(segment_durations->end_time);
	for (cur_item = segment_durations->items; cur_item < last_item; cur_item++)
	{
		cur_item->time = segment_durations->timescale == 1000 ? hds_rescale_millis(cur_item->time) :				// special case for 1000 to prevent overflow in time
			rescale_time(cur_item->time, segment_durations->timescale, HDS_TIMESCALE);
		cur_item->duration = rescale_time(cur_item->duration, segment_durations->timescale, HDS_TIMESCALE);

		if (cur_item->duration == 0)
		{
			segments->zero_segments++;
		}
	}
}
Ejemplo n.º 7
0
void
rate_filter_scale_track_timestamps(
	media_track_t* track,
	uint32_t speed_nom,
	uint32_t speed_denom)
{
	input_frame_t* last_frame;
	input_frame_t* cur_frame;
	frame_list_part_t* part;

	// TODO: remove this (added temporarily in order to avoid changing existing responses)
	if (speed_nom % 10 == 0 && speed_denom % 10 == 0)
	{
		speed_nom /= 10;
		speed_denom /= 10;
	}

	track->media_info.timescale *= speed_nom;
	track->media_info.duration *= speed_denom;
	track->media_info.full_duration *= speed_denom;
	track->media_info.duration_millis = rescale_time(track->media_info.duration, track->media_info.timescale, 1000);

	track->first_frame_time_offset *= speed_denom;
	track->total_frames_duration *= speed_denom;

	track->media_info.min_frame_duration *= speed_denom;

	if (track->media_info.media_type == MEDIA_TYPE_AUDIO)
	{
		return;		// should not change the frame durations for audio, since they will be filtered by libavcodec
	}

	track->media_info.bitrate = (uint32_t)((track->total_frames_size * track->media_info.timescale * 8) / track->media_info.full_duration);

	part = &track->frames;
	last_frame = part->last_frame;
	for (cur_frame = part->first_frame;; cur_frame++)
	{
		if (cur_frame >= last_frame)
		{
			if (part->next == NULL)
				break;
			part = part->next;
			cur_frame = part->first_frame;
			last_frame = part->last_frame;
		}

		cur_frame->duration *= speed_denom;
		cur_frame->pts_delay *= speed_denom;
	}
}
Ejemplo n.º 8
0
static u_char* 
dash_packager_write_segment_timeline(
	u_char* p, 
	dash_manifest_config_t* conf,
	segment_durations_t* segment_durations,
	segment_duration_item_t** cur_item_ptr,
	vod_str_t* base_url)
{
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count;
	uint32_t duration;
	bool_t first_time = TRUE;

	p = vod_sprintf(p,
		VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_HEADER,
		base_url,
		&conf->fragment_file_name_prefix,
		base_url,
		&conf->init_file_name_prefix);

	for (cur_item = *cur_item_ptr; cur_item < last_item; cur_item++)
	{
		// stop on discontinuity, will get called again for the next period
		if (cur_item->discontinuity && !first_time)
		{
			break;
		}

		first_time = FALSE;

		duration = (uint32_t)rescale_time(cur_item->duration, segment_durations->timescale, 1000);

		if (cur_item->repeat_count == 1)
		{
			p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT, duration);
		}
		else if (cur_item->repeat_count > 1)
		{
			p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT_REPEAT, duration, cur_item->repeat_count - 1);
		}
	}

	*cur_item_ptr = cur_item;

	p = vod_copy(p, VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FOOTER, sizeof(VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FOOTER) - 1);

	return p;
}
Ejemplo n.º 9
0
static u_char* 
dash_packager_write_segment_template(
	u_char* p, 
	dash_manifest_config_t* conf,
	segmenter_conf_t* segmenter_conf,
	vod_str_t* base_url,
	segment_durations_t* segment_durations)
{
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count;
	uint32_t duration;

	if (!conf->segment_timeline)
	{
		return vod_sprintf(p,
			VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FIXED,
			base_url,
			&conf->fragment_file_name_prefix,
			base_url,
			&conf->init_file_name_prefix,
			segmenter_conf->segment_duration);
	}

	p = vod_sprintf(p,
		VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_HEADER,
		base_url,
		&conf->fragment_file_name_prefix,
		base_url,
		&conf->init_file_name_prefix);

	for (cur_item = segment_durations->items; cur_item < last_item; cur_item++)
	{
		duration = (uint32_t)rescale_time(cur_item->duration, segment_durations->timescale, 1000);

		if (cur_item->repeat_count == 1)
		{
			p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT, duration);
		}
		else if (cur_item->repeat_count > 1)
		{
			p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT_REPEAT, duration, cur_item->repeat_count - 1);
		}
	}

	p = vod_copy(p, VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FOOTER, sizeof(VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FOOTER) - 1);

	return p;
}
Ejemplo n.º 10
0
static u_char*
mss_write_manifest_chunks(u_char* p, segment_durations_t* segment_durations)
{
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count;
	uint32_t last_segment_index;
	uint32_t segment_index;

	for (cur_item = segment_durations->items; cur_item < last_item; cur_item++)
	{
		segment_index = cur_item->segment_index;
		last_segment_index = segment_index + cur_item->repeat_count;
		for (; segment_index < last_segment_index; segment_index++)
		{
			p = vod_sprintf(p, MSS_CHUNK_TAG, segment_index, rescale_time(cur_item->duration, segment_durations->timescale, MSS_TIMESCALE));
		}
	}

	return p;
}
Ejemplo n.º 11
0
static vod_status_t
hds_muxer_init_track(
	hds_muxer_state_t* state,
	hds_muxer_stream_state_t* cur_stream,
	media_track_t* cur_track)
{
	vod_status_t rc;

	cur_stream->track = cur_track;
	cur_stream->media_type = cur_track->media_info.media_type;
	cur_stream->timescale = cur_track->media_info.timescale;
	cur_stream->frames_source = cur_track->frames_source;
	cur_stream->frames_source_context = cur_track->frames_source_context;
	cur_stream->first_frame = cur_track->first_frame;
	cur_stream->last_frame = cur_track->last_frame;

	cur_stream->clip_start_time = hds_rescale_millis(cur_track->clip_start_time);
	cur_stream->first_frame_time_offset = cur_track->first_frame_time_offset;
	cur_stream->next_frame_time_offset = cur_stream->first_frame_time_offset;
	cur_stream->next_frame_dts = rescale_time(cur_stream->next_frame_time_offset, cur_stream->timescale, HDS_TIMESCALE);

	cur_stream->cur_frame = cur_stream->first_frame;

	cur_stream->first_frame_input_offset = cur_track->frame_offsets;
	cur_stream->cur_frame_input_offset = cur_stream->first_frame_input_offset;

	if (cur_track->media_info.media_type == MEDIA_TYPE_AUDIO)
	{
		rc = hds_get_sound_info(state->request_context, &cur_track->media_info, &cur_stream->sound_info);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
				"hds_muxer_init_track: hds_get_sound_info failed %i", rc);
			return rc;
		}
	}

	return VOD_OK;
}
Ejemplo n.º 12
0
static u_char*
hds_write_abst_atom(
	u_char* p, 
	segment_durations_t* segment_durations)
{
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count;
	uint64_t start_offset = 0;
	uint64_t timestamp;
	uint32_t duration;
	size_t afrt_atom_size = AFRT_BASE_ATOM_SIZE;
	size_t asrt_atom_size = ASRT_ATOM_SIZE;
	size_t abst_atom_size = ABST_BASE_ATOM_SIZE;

	afrt_atom_size += (segment_durations->item_count + 1) * sizeof(afrt_entry_t);
	abst_atom_size += (segment_durations->item_count + 1) * sizeof(afrt_entry_t);

	// abst
	write_atom_header(p, abst_atom_size, 'a', 'b', 's', 't');
	write_dword(p, 0);					// version + flags
	write_dword(p, 1);					// bootstrap info version
	*p++ = 0;							// profile, live, update
	write_dword(p, HDS_TIMESCALE);		// timescale
	write_dword(p, 0);					// current media time - high
	write_dword(p, segment_durations->duration_millis);	// current media time - low
	write_qword(p, 0LL);				// smpte offset
	*p++ = 0;							// movie identifier
	*p++ = 0;							// server entries
	*p++ = 0;							// quality entries
	*p++ = 0;							// drm data
	*p++ = 0;							// metadata
	*p++ = 1;							// segment run table count

	// abst.asrt
	write_atom_header(p, asrt_atom_size, 'a', 's', 'r', 't');
	write_dword(p, 0);					// version + flags
	*p++ = 0;							// quality entries
	write_dword(p, 1);					// segment run entries
	// entry #1
	write_dword(p, 1);					// first segment
	write_dword(p, segment_durations->segment_count);		// fragments per segment

	// abst
	*p++ = 1;							// fragment run table count

	// abst.afrt
	write_atom_header(p, afrt_atom_size, 'a', 'f', 'r', 't');
	write_dword(p, 0);					// version + flags
	write_dword(p, HDS_TIMESCALE);		// timescale
	*p++ = 0;							// quality entries
	write_dword(p, segment_durations->item_count + 1);	// fragment run entries

	// write the afrt entries
	for (cur_item = segment_durations->items; cur_item < last_item; cur_item++)
	{
		timestamp = rescale_time(start_offset, segment_durations->timescale, HDS_TIMESCALE);
		duration = rescale_time(cur_item->duration, segment_durations->timescale, HDS_TIMESCALE);

		write_dword(p, cur_item->segment_index + 1);		// first fragment
		write_qword(p, timestamp);	// first fragment timestamp
		write_dword(p, duration);			// fragment duration
		start_offset += cur_item->duration * cur_item->repeat_count;
	}

	// last entry
	write_dword(p, 0);					// first fragment
	write_qword(p, 0LL);				// first fragment timestamp
	write_dword(p, 0);					// fragment duration
	*p++ = 0;							// discontinuity indicator (0 = end of presentation)

	return p;
}
Ejemplo n.º 13
0
static u_char* 
dash_packager_write_segment_timeline(
	u_char* p, 
	dash_manifest_config_t* conf,
	uint32_t start_number,
	uint64_t clip_start_time,
	u_char* clip_spec,
	media_track_t* reference_track,
	segment_durations_t* segment_durations,
	segment_duration_item_t** cur_item_ptr,
	vod_str_t* base_url)
{
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count;
	uint64_t start_time;
	uint32_t duration;
	bool_t first_time = TRUE;

	if (segment_durations->start_time > clip_start_time)
	{
		start_time = segment_durations->start_time - clip_start_time;
	}
	else
	{
		start_time = 0;
	}

	// Note: SegmentTemplate is currently printed in the adaptation set level, so it is not possible
	//		to mix mp4 and webm representations for the same media type
	p = vod_sprintf(p,
		VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_HEADER,
		base_url,
		&conf->fragment_file_name_prefix,
		&dash_codecs[reference_track->media_info.codec_id].frag_file_ext,
		base_url,
		&conf->init_file_name_prefix,
		clip_spec,
		&dash_codecs[reference_track->media_info.codec_id].init_file_ext,
		start_number + 1);

	for (cur_item = *cur_item_ptr; cur_item < last_item; cur_item++)
	{
		// stop on discontinuity, will get called again for the next period
		if (cur_item->discontinuity && !first_time)
		{
			break;
		}

		duration = (uint32_t)rescale_time(cur_item->duration, segment_durations->timescale, 1000);

		if (first_time && start_time != 0)
		{
			// output the time
			if (cur_item->repeat_count == 1)
			{
				p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT_TIME, start_time, duration);
			}
			else if (cur_item->repeat_count > 1)
			{
				p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT_REPEAT_TIME, start_time, duration, cur_item->repeat_count - 1);
			}
		}
		else
		{
			// don't output the time
			if (cur_item->repeat_count == 1)
			{
				p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT, duration);
			}
			else if (cur_item->repeat_count > 1)
			{
				p = vod_sprintf(p, VOD_DASH_MANIFEST_SEGMENT_REPEAT, duration, cur_item->repeat_count - 1);
			}
		}

		first_time = FALSE;
	}

	*cur_item_ptr = cur_item;

	p = vod_copy(p, VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FOOTER, sizeof(VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FOOTER) - 1);

	return p;
}
Ejemplo n.º 14
0
vod_status_t
segmenter_get_segment_durations_accurate(
	request_context_t* request_context,
	segmenter_conf_t* conf,
	media_set_t* media_set,
	media_sequence_t* sequence,
	uint32_t media_type,
	segment_durations_t* result)
{
	segmenter_boundary_iterator_context_t boundary_iterator;
	media_track_t* cur_track;
	media_track_t* last_track;
	media_track_t* main_track = NULL;
	media_track_t* longest_track = NULL;
	segment_duration_item_t* cur_item;
	media_sequence_t* sequences_end;
	media_sequence_t* cur_sequence;
	input_frame_t* last_frame;
	input_frame_t* cur_frame;
	uint64_t total_duration;
	uint32_t segment_index = 0;
	uint64_t accum_duration = 0;
	uint64_t segment_start = 0;
	uint64_t segment_limit_millis;
	uint64_t segment_limit;
	uint64_t cur_duration;
	uint32_t duration_millis;
	bool_t align_to_key_frames;

	SEGMENT_CHOOSE_HEADER(conf);

	if (media_set->durations != NULL)
	{
		// in case of a playlist fall back to estimate
		return segmenter_get_segment_durations_estimate(
			request_context,
			conf,
			media_set,
			sequence,
			media_type,
			result);
	}

	// get the maximum duration and main track (=first video track if exists, or first audio track otherwise)
	if (sequence != NULL)
	{
		cur_sequence = sequence;
		sequences_end = sequence + 1;
	}
	else
	{
		cur_sequence = media_set->sequences;
		sequences_end = media_set->sequences_end;
	}

	duration_millis = 0;
	for (; cur_sequence < sequences_end; cur_sequence++)
	{
		last_track = cur_sequence->filtered_clips[0].last_track;
		for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < last_track; cur_track++)
		{
			if (media_type != MEDIA_TYPE_NONE && cur_track->media_info.media_type != media_type)
			{
				continue;
			}

			if (main_track == NULL ||
				(cur_track->media_info.media_type < main_track->media_info.media_type))
			{
				main_track = cur_track;
			}

			if (cur_track->media_info.duration_millis > duration_millis)
			{
				longest_track = cur_track;
				duration_millis = cur_track->media_info.duration_millis;
			}
		}
	}

	if (main_track == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_segment_durations_accurate: didnt get any tracks");
		return VOD_UNEXPECTED;
	}

	// if the main track is not audio/video, or main track is audio and requires filtering, fall back to estimate
	switch (main_track->media_info.media_type)
	{
	case MEDIA_TYPE_VIDEO:
		break;

	case MEDIA_TYPE_AUDIO:
		if (!media_set->audio_filtering_needed)
		{
			break;
		}

	default:
		return segmenter_get_segment_durations_estimate(
			request_context,
			conf,
			media_set,
			sequence,
			media_type,
			result);
	}

	// get the segment count
	result->segment_count = conf->get_segment_count(conf, duration_millis);
	if (result->segment_count > MAX_SEGMENT_COUNT)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_segment_durations_accurate: segment count %uD is invalid", result->segment_count);
		return VOD_BAD_DATA;
	}

	// allocate the result buffer
	result->items = vod_alloc(request_context->pool, sizeof(*result->items) * result->segment_count);
	if (result->items == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"segmenter_get_segment_durations_accurate: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	result->timescale = main_track->media_info.timescale;
	result->discontinuities = 0;

	// Note: assuming a single frame list part
	cur_item = result->items - 1;
	last_frame = main_track->frames.last_frame;
	cur_frame = main_track->frames.first_frame;

	align_to_key_frames = conf->align_to_key_frames && main_track->media_info.media_type == MEDIA_TYPE_VIDEO;

	// bootstrap segments
	if (conf->bootstrap_segments_count > 0)
	{
		segment_limit = rescale_time(conf->bootstrap_segments_end[0], 1000, result->timescale);

		for (; cur_frame < last_frame; cur_frame++)
		{
			while (accum_duration >= segment_limit && segment_index + 1 < result->segment_count &&
				(!align_to_key_frames || cur_frame->key_frame))
			{
				// get the current duration and update to array
				cur_duration = accum_duration - segment_start;
				if (cur_item < result->items || cur_duration != cur_item->duration)
				{
					cur_item++;
					cur_item->repeat_count = 0;
					cur_item->segment_index = segment_index;
					cur_item->duration = cur_duration;
					cur_item->discontinuity = FALSE;
				}
				cur_item->repeat_count++;

				// move to the next segment
				segment_start = accum_duration;
				segment_index++;
				if (segment_index >= conf->bootstrap_segments_count)
				{
					goto post_bootstrap;
				}
				segment_limit = rescale_time(conf->bootstrap_segments_end[segment_index], 1000, result->timescale);
			}
			accum_duration += cur_frame->duration;
		}
	}

post_bootstrap:

	// remaining segments
	segment_limit_millis = conf->bootstrap_segments_total_duration + conf->segment_duration;
	segment_limit = rescale_time(segment_limit_millis, 1000, result->timescale);

	for (; cur_frame < last_frame; cur_frame++)
	{
		while (accum_duration >= segment_limit && segment_index + 1 < result->segment_count &&
			(!align_to_key_frames || cur_frame->key_frame))
		{
			// get the current duration and update to array
			cur_duration = accum_duration - segment_start;
			if (cur_item < result->items || cur_duration != cur_item->duration)
			{
				cur_item++;
				cur_item->repeat_count = 0;
				cur_item->segment_index = segment_index;
				cur_item->duration = cur_duration;
				cur_item->discontinuity = FALSE;
			}
			cur_item->repeat_count++;

			// move to the next segment
			segment_index++;
			segment_start = accum_duration;
			segment_limit_millis += conf->segment_duration;
			segment_limit = rescale_time(segment_limit_millis, 1000, result->timescale);
		}
		accum_duration += cur_frame->duration;
	}

	// in case the main video track is shorter than the audio track, add the estimated durations of the remaining audio-only segments
	if (main_track->media_info.duration_millis < duration_millis &&
		!align_to_key_frames)
	{
		segmenter_boundary_iterator_init(&boundary_iterator, conf, result->segment_count);
		segmenter_boundary_iterator_skip(&boundary_iterator, segment_index);

		total_duration = rescale_time(longest_track->media_info.duration, longest_track->media_info.timescale, result->timescale);

		while (accum_duration < total_duration &&
			segment_index + 1 < result->segment_count)
		{
			segment_limit_millis = segmenter_boundary_iterator_next(&boundary_iterator);
			segment_limit = rescale_time(segment_limit_millis, 1000, result->timescale);
			segment_limit = vod_min(segment_limit, total_duration);

			accum_duration = segment_limit;

			cur_duration = accum_duration - segment_start;
			if (cur_item < result->items || cur_duration != cur_item->duration)
			{
				cur_item++;
				cur_item->repeat_count = 0;
				cur_item->segment_index = segment_index;
				cur_item->duration = cur_duration;
				cur_item->discontinuity = FALSE;
			}
			cur_item->repeat_count++;

			// move to the next segment
			segment_index++;
			segment_start = accum_duration;
		}

		accum_duration = total_duration;
	}

	// add the last segment / empty segments after the last keyframe (in case align to key frames is on)
	while (segment_index < result->segment_count)
	{
		// get the current duration and update to array
		cur_duration = accum_duration - segment_start;
		if (cur_item < result->items || cur_duration != cur_item->duration)
		{
			cur_item++;
			cur_item->repeat_count = 0;
			cur_item->segment_index = segment_index;
			cur_item->duration = cur_duration;
			cur_item->discontinuity = FALSE;
		}
		cur_item->repeat_count++;

		// move to the next segment
		segment_index++;
		segment_start = accum_duration;
	}

	result->item_count = cur_item + 1 - result->items;

	// remove any empty segments from the end
	if (result->item_count > 0 && cur_item->duration == 0)
	{
		result->item_count--;
		result->segment_count -= cur_item->repeat_count;
	}

	result->start_time = 0;
	result->end_time = duration_millis;

	return VOD_OK;
}
Ejemplo n.º 15
0
static vod_status_t
hds_muxer_init_state(
	request_context_t* request_context,
	mpeg_metadata_t *mpeg_metadata,
	read_cache_state_t* read_cache_state,
	write_callback_t write_callback,
	void* write_context,
	hds_muxer_state_t** result)
{
	mpeg_stream_metadata_t* cur_stream;
	hds_muxer_stream_state_t* stream_state;
	hds_muxer_state_t* state;
	vod_status_t rc;

	// allocate the state and stream states
	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_muxer_init_state: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	state->first_stream = vod_alloc(
		request_context->pool, 
		sizeof(state->first_stream[0]) * mpeg_metadata->streams.nelts);
	if (state->first_stream == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_muxer_init_state: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}
	state->last_stream = state->first_stream + mpeg_metadata->streams.nelts;
	state->request_context = request_context;
	state->cur_frame = NULL;

	state->read_cache_state = read_cache_state;
	write_buffer_init(&state->write_buffer_state, request_context, write_callback, write_context);

	stream_state = state->first_stream;
	for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++, stream_state++)
	{
		// initialize the stream
		stream_state->metadata = cur_stream;
		stream_state->media_type = cur_stream->media_info.media_type;
		stream_state->timescale = cur_stream->media_info.timescale;
		stream_state->first_frame = cur_stream->frames;
		stream_state->last_frame = cur_stream->frames + cur_stream->frame_count;

		stream_state->first_frame_time_offset = cur_stream->first_frame_time_offset;
		stream_state->next_frame_time_offset = cur_stream->first_frame_time_offset;
		stream_state->next_frame_dts = rescale_time(stream_state->next_frame_time_offset, stream_state->timescale, HDS_TIMESCALE);

		stream_state->cur_frame = stream_state->first_frame;

		stream_state->first_frame_input_offset = cur_stream->frame_offsets;
		stream_state->cur_frame_input_offset = stream_state->first_frame_input_offset;

		stream_state->first_frame_output_offset = vod_alloc(
			request_context->pool,
			cur_stream->frame_count * sizeof(uint32_t));
		if (stream_state->first_frame_output_offset == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"hds_muxer_init_state: vod_alloc failed (3)");
			return VOD_ALLOC_FAILED;
		}
		stream_state->cur_frame_output_offset = stream_state->first_frame_output_offset;

		if (cur_stream->media_info.media_type == MEDIA_TYPE_AUDIO)
		{
			rc = hds_get_sound_info(request_context, &cur_stream->media_info, &stream_state->sound_info);
			if (rc != VOD_OK)
			{
				vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
					"hds_muxer_init_state: hds_get_sound_info failed %i", rc);
				return rc;
			}
		}
	}

	*result = state;

	return VOD_OK;
}
Ejemplo n.º 16
0
vod_status_t
mss_packager_build_fragment_header(
	request_context_t* request_context,
	mpeg_stream_metadata_t* stream_metadata,
	uint32_t segment_index,
	size_t extra_traf_atoms_size,
	write_extra_traf_atoms_callback_t write_extra_traf_atoms_callback,
	void* write_extra_traf_atoms_context,
	bool_t size_only,
	vod_str_t* result,
	size_t* total_fragment_size)
{
	input_frame_t* last_frame;
	input_frame_t* cur_frame;
	size_t mdat_atom_size;
	size_t trun_atom_size;
	size_t moof_atom_size;
	size_t traf_atom_size;
	size_t result_size;
	u_char* p;

	// calculate sizes
	mdat_atom_size = ATOM_HEADER_SIZE + stream_metadata->total_frames_size;
	trun_atom_size = mp4_builder_get_trun_atom_size(stream_metadata->media_info.media_type, stream_metadata->frame_count);

	traf_atom_size =
		ATOM_HEADER_SIZE +
		ATOM_HEADER_SIZE + sizeof(tfhd_atom_t) +
		trun_atom_size +
		ATOM_HEADER_SIZE + sizeof(uuid_tfxd_atom_t) + 
		extra_traf_atoms_size;

	moof_atom_size =
		ATOM_HEADER_SIZE +
		ATOM_HEADER_SIZE + sizeof(mfhd_atom_t)+
		traf_atom_size;

	result_size =
		moof_atom_size +
		ATOM_HEADER_SIZE;		// mdat

	*total_fragment_size = result_size + stream_metadata->total_frames_size;

	// head request optimization
	if (size_only)
	{
		return VOD_OK;
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mss_packager_build_fragment_header: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	p = result->data;

	// moof
	write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f');

	// moof.mfhd
	p = mp4_builder_write_mfhd_atom(p, segment_index);

	// moof.traf
	write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f');

	// moof.traf.tfhd
	switch (stream_metadata->media_info.media_type)
	{
	case MEDIA_TYPE_VIDEO:
		p = mss_write_tfhd_atom(p, stream_metadata->media_info.track_id, 0x01010000);
		break;

	case MEDIA_TYPE_AUDIO:
		p = mss_write_tfhd_atom(p, stream_metadata->media_info.track_id, 0x02000000);
		break;
	}

	// moof.traf.trun
	last_frame = stream_metadata->frames + stream_metadata->frame_count;
	for (cur_frame = stream_metadata->frames; cur_frame < last_frame; cur_frame++)
	{
		cur_frame->duration = rescale_time(cur_frame->duration, stream_metadata->media_info.timescale, MSS_TIMESCALE);
	}

	p = mp4_builder_write_trun_atom(
		p,
		stream_metadata->media_info.media_type,
		stream_metadata->frames,
		stream_metadata->frame_count,
		moof_atom_size + ATOM_HEADER_SIZE);

	p = mss_write_uuid_tfxd_atom(p, stream_metadata);

	// moof.traf.xxx
	if (write_extra_traf_atoms_callback != NULL)
	{
		p = write_extra_traf_atoms_callback(write_extra_traf_atoms_context, p, moof_atom_size + ATOM_HEADER_SIZE);
	}

	// mdat
	write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't');

	result->len = p - result->data;

	if (result->len != result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"mss_packager_build_fragment_header: result length %uz is different than allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
Ejemplo n.º 17
0
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;
}
Ejemplo n.º 18
0
vod_status_t 
mss_packager_build_manifest(
	request_context_t* request_context, 
	segmenter_conf_t* segmenter_conf, 
	mpeg_metadata_t* mpeg_metadata, 
	size_t extra_tags_size,
	mss_write_tags_callback_t write_extra_tags,
	void* extra_tags_writer_context,
	vod_str_t* result)
{
	mpeg_stream_metadata_t* cur_stream;
	segment_durations_t segment_durations[MEDIA_TYPE_COUNT];
	uint64_t duration_100ns;
	uint32_t media_type;
	uint32_t stream_index;
	uint32_t bitrate;
	vod_status_t rc;
	size_t result_size;
	u_char* p;

	// calculate the result size
	result_size = 
		sizeof(MSS_MANIFEST_HEADER) - 1 + VOD_INT64_LEN + 
		extra_tags_size +
		sizeof(MSS_MANIFEST_FOOTER);
	for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++)
	{
		if (mpeg_metadata->longest_stream[media_type] == NULL)
		{
			continue;
		}

		rc = segmenter_conf->get_segment_durations(
			request_context,
			segmenter_conf,
			&mpeg_metadata->longest_stream[media_type],
			1,
			&segment_durations[media_type]);
		if (rc != VOD_OK)
		{
			return rc;
		}

		result_size +=
			sizeof(MSS_STREAM_INDEX_HEADER) - 1 + 2 * sizeof(MSS_STREAM_TYPE_VIDEO) + 2 * VOD_INT32_LEN +
			sizeof(MSS_STREAM_INDEX_FOOTER);

		result_size += segment_durations[media_type].segment_count * (sizeof(MSS_CHUNK_TAG) + VOD_INT32_LEN + VOD_INT64_LEN);
	}

	for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
	{
		switch (cur_stream->media_info.media_type)
		{
		case MEDIA_TYPE_VIDEO:
			result_size += 
				sizeof(MSS_VIDEO_QUALITY_LEVEL_HEADER) - 1 + 4 * VOD_INT32_LEN + cur_stream->media_info.extra_data_size * 2 + 
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;

		case MEDIA_TYPE_AUDIO:
			result_size += 
				sizeof(MSS_AUDIO_QUALITY_LEVEL_HEADER) - 1 + 6 * VOD_INT32_LEN + cur_stream->media_info.extra_data_size * 2 +
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;
		}
	}

	// allocate the result
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mss_packager_build_manifest: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	duration_100ns = rescale_time(mpeg_metadata->duration, mpeg_metadata->timescale, MSS_TIMESCALE);
	p = vod_sprintf(result->data, MSS_MANIFEST_HEADER, duration_100ns);

	if (mpeg_metadata->longest_stream[MEDIA_TYPE_VIDEO] != NULL)
	{
		p = vod_sprintf(p, 
			MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_VIDEO,
			mpeg_metadata->stream_count[MEDIA_TYPE_VIDEO],
			segment_durations[MEDIA_TYPE_VIDEO].segment_count,
			MSS_STREAM_TYPE_VIDEO);

		stream_index = 0;
		for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
		{
			if (cur_stream->media_info.media_type != MEDIA_TYPE_VIDEO)
			{
				continue;
			}

			bitrate = cur_stream->media_info.bitrate;
			bitrate = mss_encode_indexes(bitrate, cur_stream->file_info.file_index, cur_stream->track_index);
			p = vod_sprintf(p, MSS_VIDEO_QUALITY_LEVEL_HEADER,
				stream_index++,
				bitrate,
				(uint32_t)cur_stream->media_info.u.video.width,
				(uint32_t)cur_stream->media_info.u.video.height);

			p = mss_append_hex_string(p, cur_stream->media_info.extra_data, cur_stream->media_info.extra_data_size);

			p = vod_copy(p, MSS_QUALITY_LEVEL_FOOTER, sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1);
		}

		p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_VIDEO]);

		p = vod_copy(p, MSS_STREAM_INDEX_FOOTER, sizeof(MSS_STREAM_INDEX_FOOTER) - 1);
	}

	if (mpeg_metadata->longest_stream[MEDIA_TYPE_AUDIO] != NULL)
	{
		p = vod_sprintf(p, MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_AUDIO,
			mpeg_metadata->stream_count[MEDIA_TYPE_AUDIO],
			segment_durations[MEDIA_TYPE_AUDIO].segment_count,
			MSS_STREAM_TYPE_AUDIO);

		stream_index = 0;
		for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
		{
			if (cur_stream->media_info.media_type != MEDIA_TYPE_AUDIO)
			{
				continue;
			}

			bitrate = cur_stream->media_info.bitrate;
			bitrate = mss_encode_indexes(bitrate, cur_stream->file_info.file_index, cur_stream->track_index);
			p = vod_sprintf(p, MSS_AUDIO_QUALITY_LEVEL_HEADER,
				stream_index++,
				bitrate,
				cur_stream->media_info.u.audio.sample_rate,
				(uint32_t)cur_stream->media_info.u.audio.channels,
				(uint32_t)cur_stream->media_info.u.audio.bits_per_sample,
				(uint32_t)cur_stream->media_info.u.audio.packet_size);

			p = mss_append_hex_string(p, cur_stream->media_info.extra_data, cur_stream->media_info.extra_data_size);

			p = vod_copy(p, MSS_QUALITY_LEVEL_FOOTER, sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1);
		}

		p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_AUDIO]);

		p = vod_copy(p, MSS_STREAM_INDEX_FOOTER, sizeof(MSS_STREAM_INDEX_FOOTER) - 1);
	}

	if (write_extra_tags != NULL)
	{
		p = write_extra_tags(extra_tags_writer_context, p, mpeg_metadata);
	}

	p = vod_copy(p, MSS_MANIFEST_FOOTER, sizeof(MSS_MANIFEST_FOOTER) - 1);

	result->len = p - result->data;
	
	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"mss_packager_build_manifest: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
Ejemplo n.º 19
0
vod_status_t
mss_packager_build_fragment_header(
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t segment_index,
	size_t extra_traf_atoms_size,
	mss_write_extra_traf_atoms_callback_t write_extra_traf_atoms_callback,
	void* write_extra_traf_atoms_context,
	bool_t size_only,
	vod_str_t* result,
	size_t* total_fragment_size)
{
	segment_timing_info_t timing_info;
	media_clip_filtered_t* cur_clip;
	media_sequence_t* sequence = media_set->sequences;
	input_frame_t* last_frame;
	input_frame_t* cur_frame;
	media_track_t* first_track = sequence->filtered_clips[0].first_track;
	media_track_t* track;
	uint32_t media_type = sequence->media_type;
	size_t mdat_atom_size;
	size_t trun_atom_size;
	size_t moof_atom_size;
	size_t traf_atom_size;
	size_t result_size;
	u_char* p;

	// calculate sizes
	mdat_atom_size = ATOM_HEADER_SIZE + sequence->total_frame_size;
	trun_atom_size = mp4_builder_get_trun_atom_size(media_type, sequence->total_frame_count);

	traf_atom_size =
		ATOM_HEADER_SIZE +
		ATOM_HEADER_SIZE + sizeof(tfhd_atom_t) +
		trun_atom_size +
		ATOM_HEADER_SIZE + sizeof(uuid_tfxd_atom_t) + 
		extra_traf_atoms_size;

	if (media_set->type == MEDIA_SET_LIVE)
	{
		traf_atom_size += TFRF_ATOM_SIZE;
	}

	moof_atom_size =
		ATOM_HEADER_SIZE +
		ATOM_HEADER_SIZE + sizeof(mfhd_atom_t)+
		traf_atom_size;

	result_size =
		moof_atom_size +
		ATOM_HEADER_SIZE;		// mdat

	*total_fragment_size = result_size + sequence->total_frame_size;

	// head request optimization
	if (size_only)
	{
		return VOD_OK;
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mss_packager_build_fragment_header: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	p = result->data;

	// moof
	write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f');

	// moof.mfhd
	p = mp4_builder_write_mfhd_atom(p, segment_index);

	// moof.traf
	write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f');

	// moof.traf.tfhd
	switch (media_type)
	{
	case MEDIA_TYPE_VIDEO:
		p = mss_write_tfhd_atom(p, first_track->media_info.track_id, 0x01010000);
		break;

	case MEDIA_TYPE_AUDIO:
		p = mss_write_tfhd_atom(p, first_track->media_info.track_id, 0x02000000);
		break;
	}

	// moof.traf.trun
	for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++)
	{
		track = cur_clip->first_track;
		cur_frame = track->first_frame;
		last_frame = track->last_frame;
		for (; cur_frame < last_frame; cur_frame++)
		{
			cur_frame->duration = rescale_time(cur_frame->duration, track->media_info.timescale, MSS_TIMESCALE);
			cur_frame->pts_delay = rescale_time(cur_frame->pts_delay, track->media_info.timescale, MSS_TIMESCALE);
		}
	}

	p = mp4_builder_write_trun_atom(
		p,
		sequence,
		moof_atom_size + ATOM_HEADER_SIZE);


	if (media_set->type == MEDIA_SET_LIVE)
	{
		// using only estimate timing info in live, since we don't have the accurate timing
		//	for the lookahead segments. the timestamp has to be consistent between segments/manifest
		//	otherwise some segments may be pulled more than once
		timing_info.timestamp = mss_rescale_millis(media_set->segment_start_time);
		timing_info.duration = mss_rescale_millis(media_set->segmenter_conf->segment_duration);

		p = mss_write_uuid_tfxd_atom(p, &timing_info);

		p = mss_write_uuid_tfrf_atom(p, &timing_info);
	}
	else
	{
		mss_get_segment_timing_info(sequence, &timing_info);

		p = mss_write_uuid_tfxd_atom(p, &timing_info);
	}

	// moof.traf.xxx
	if (write_extra_traf_atoms_callback != NULL)
	{
		p = write_extra_traf_atoms_callback(write_extra_traf_atoms_context, p, moof_atom_size);
	}

	// mdat
	write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't');

	result->len = p - result->data;

	if (result->len != result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"mss_packager_build_fragment_header: result length %uz is different than allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
Ejemplo n.º 20
0
vod_status_t
m3u8_builder_build_index_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	vod_str_t* base_url,
	vod_str_t* segments_base_url,
	request_params_t* request_params,
	hls_encryption_params_t* encryption_params,
	media_set_t* media_set,
	vod_str_t* result)
{
	segment_durations_t segment_durations;
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item;
	segmenter_conf_t* segmenter_conf = media_set->segmenter_conf;
	uint64_t duration_millis;
	uint32_t sequence_index;
	vod_str_t extinf;
	uint32_t segment_index;
	uint32_t last_segment_index;
	vod_str_t tracks_spec;
	uint32_t scale;
	size_t segment_length;
	size_t result_size;
	vod_status_t rc;
	u_char* p;

	sequence_index = media_set->has_multi_sequences ? media_set->sequences[0].index : INVALID_SEQUENCE_INDEX;

	// build the required tracks string
	rc = m3u8_builder_build_required_tracks_string(
		request_context,
		media_set,
		sequence_index,
		request_params,
		&tracks_spec);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the segment durations
	rc = segmenter_conf->get_segment_durations(
		request_context,
		segmenter_conf,
		media_set,
		NULL,
		MEDIA_TYPE_NONE,
		&segment_durations);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the required buffer length
	duration_millis = segment_durations.end_time - segment_durations.start_time;
	last_segment_index = media_set->initial_segment_index + segment_durations.segment_count;
	segment_length = sizeof("#EXTINF:.000,\n") - 1 + vod_get_int_print_len(vod_div_ceil(duration_millis, 1000)) +
		segments_base_url->len + conf->segment_file_name_prefix.len + 1 + vod_get_int_print_len(last_segment_index) + tracks_spec.len + sizeof(".ts\n") - 1;

	result_size =
		sizeof(M3U8_HEADER_PART1) + VOD_INT64_LEN +
		sizeof(M3U8_HEADER_VOD) +
		sizeof(M3U8_HEADER_PART2) + VOD_INT64_LEN + VOD_INT32_LEN +
		segment_length * segment_durations.segment_count +
		segment_durations.discontinuities * (sizeof(m3u8_discontinuity) - 1) +
		sizeof(m3u8_footer);

	if (encryption_params->type != HLS_ENC_NONE)
	{
		result_size +=
			sizeof(encryption_key_tag_part1) - 1 +
			sizeof(encryption_type_sample_aes) - 1 +
			sizeof(encryption_key_tag_part2) - 1 +
			base_url->len +
			conf->encryption_key_file_name.len +
			sizeof("-f") - 1 + VOD_INT32_LEN +
			sizeof(encryption_key_tag_part3) - 1;
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_index_playlist: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// write the header
	p = vod_sprintf(
		result->data,
		M3U8_HEADER_PART1,
		(segmenter_conf->max_segment_duration + 500) / 1000);

	if (media_set->type == MEDIA_SET_VOD)
	{
		p = vod_copy(p, M3U8_HEADER_VOD, sizeof(M3U8_HEADER_VOD) - 1);
	}

	if (encryption_params->type != HLS_ENC_NONE)
	{
		p = vod_copy(p, encryption_key_tag_part1, sizeof(encryption_key_tag_part1) - 1);
		switch (encryption_params->type)
		{
		case HLS_ENC_SAMPLE_AES:
			p = vod_copy(p, encryption_type_sample_aes, sizeof(encryption_type_sample_aes) - 1);
			break;

		default:		// HLS_ENC_AES_128
			p = vod_copy(p, encryption_type_aes_128, sizeof(encryption_type_aes_128) - 1);
			break;
		}
		p = vod_copy(p, encryption_key_tag_part2, sizeof(encryption_key_tag_part2) - 1);
		p = vod_copy(p, base_url->data, base_url->len);
		p = vod_copy(p, conf->encryption_key_file_name.data, conf->encryption_key_file_name.len);
		if (sequence_index != INVALID_SEQUENCE_INDEX)
		{
			p = vod_sprintf(p, "-f%uD", sequence_index + 1);
		}
		p = vod_copy(p, encryption_key_tag_part3, sizeof(encryption_key_tag_part3) - 1);
	}

	p = vod_sprintf(
		p,
		M3U8_HEADER_PART2,
		conf->m3u8_version, 
		media_set->initial_segment_index + 1);

	// write the segments
	scale = conf->m3u8_version >= 3 ? 1000 : 1;
	last_item = segment_durations.items + segment_durations.item_count;

	for (cur_item = segment_durations.items; cur_item < last_item; cur_item++)
	{
		segment_index = cur_item->segment_index;
		last_segment_index = segment_index + cur_item->repeat_count;

		if (cur_item->discontinuity)
		{
			p = vod_copy(p, m3u8_discontinuity, sizeof(m3u8_discontinuity) - 1);
		}

		// write the first segment
		extinf.data = p;
		p = m3u8_builder_append_extinf_tag(p, rescale_time(cur_item->duration, segment_durations.timescale, scale), scale);
		extinf.len = p - extinf.data;
		p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &tracks_spec);
		segment_index++;

		// write any additional segments
		for (; segment_index < last_segment_index; segment_index++)
		{
			p = vod_copy(p, extinf.data, extinf.len);
			p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &tracks_spec);
		}
	}

	// write the footer
	if (media_set->type == MEDIA_SET_VOD)
	{
		p = vod_copy(p, m3u8_footer, sizeof(m3u8_footer) - 1);
	}

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_index_playlist: result length %uz exceeded allocated length %uz", 
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
Ejemplo n.º 21
0
static vod_status_t
hds_muxer_start_frame(hds_muxer_state_t* state)
{
	hds_muxer_stream_state_t* selected_stream;
	uint64_t cur_frame_offset;
	uint64_t cur_frame_dts;
	size_t alloc_size;
	u_char* p;
	vod_status_t rc;

	rc = hds_muxer_choose_stream(state, &selected_stream);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// init the frame
	state->cur_frame = selected_stream->cur_frame;
	state->frames_source = selected_stream->frames_source;
	state->frames_source_context = selected_stream->frames_source_context;
	selected_stream->cur_frame++;
	cur_frame_offset = *selected_stream->cur_frame_input_offset;
	selected_stream->cur_frame_input_offset++;
	selected_stream->cur_frame_output_offset++;

	selected_stream->next_frame_time_offset += state->cur_frame->duration;
	cur_frame_dts = selected_stream->next_frame_dts + selected_stream->clip_start_time;
	selected_stream->next_frame_dts = rescale_time(selected_stream->next_frame_time_offset, selected_stream->timescale, HDS_TIMESCALE);

	state->cache_slot_id = selected_stream->media_type;

	// allocate room for the mux packet header
	state->frame_header_size = tag_size_by_media_type[selected_stream->media_type];

	alloc_size = state->frame_header_size;
	if (selected_stream->media_type == MEDIA_TYPE_VIDEO && state->cur_frame->key_frame)
	{
		alloc_size += state->codec_config_size;
	}

	rc = write_buffer_get_bytes(&state->write_buffer_state, alloc_size, NULL, &p);
	if (rc != VOD_OK)
	{
		vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
			"hds_muxer_start_frame: write_buffer_get_bytes failed %i", rc);
		return rc;
	}

	// write the mux packet header and optionally codec config
	if (selected_stream->media_type == MEDIA_TYPE_VIDEO && state->cur_frame->key_frame)
	{
		p = hds_muxer_write_codec_config(p, state, cur_frame_dts);
	}

	switch (selected_stream->media_type)
	{
	case MEDIA_TYPE_VIDEO:
		hds_write_video_tag_header(
			p,
			state->cur_frame->size,
			cur_frame_dts,
			state->cur_frame->key_frame ? FRAME_TYPE_KEY_FRAME : FRAME_TYPE_INTER_FRAME,
			AVC_PACKET_TYPE_NALU,
			rescale_time(state->cur_frame->pts_delay, selected_stream->timescale, HDS_TIMESCALE));
		break;

	case MEDIA_TYPE_AUDIO:
		hds_write_audio_tag_header(
			p,
			state->cur_frame->size,
			cur_frame_dts,
			selected_stream->sound_info,
			AAC_PACKET_TYPE_RAW);
	}

	rc = state->frames_source->start_frame(state->frames_source_context, state->cur_frame, cur_frame_offset);
	if (rc != VOD_OK)
	{
		return rc;
	}

	return VOD_OK;
}
Ejemplo n.º 22
0
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;
}
Ejemplo n.º 23
0
vod_status_t
m3u8_builder_build_index_playlist(
    request_context_t* request_context,
    m3u8_config_t* conf,
    vod_str_t* base_url,
    vod_str_t* segments_base_url,
    bool_t include_file_index,
    bool_t encryption_enabled,
    segmenter_conf_t* segmenter_conf,
    mpeg_metadata_t* mpeg_metadata,
    vod_str_t* result)
{
    segment_durations_t segment_durations;
    segment_duration_item_t* cur_item;
    segment_duration_item_t* last_item;
    vod_str_t extinf;
    uint32_t segment_index;
    uint32_t last_segment_index;
    vod_str_t required_tracks;
    uint32_t scale;
    size_t segment_length;
    size_t result_size;
    vod_status_t rc;
    u_char* p;

    // build the required tracks string
    rc = m3u8_builder_build_required_tracks_string(
             request_context,
             include_file_index,
             mpeg_metadata,
             &required_tracks);
    if (rc != VOD_OK)
    {
        return rc;
    }

    // get the segment durations
    rc = segmenter_conf->get_segment_durations(
             request_context,
             segmenter_conf,
             mpeg_metadata->longest_stream,
             MEDIA_TYPE_COUNT,
             &segment_durations);
    if (rc != VOD_OK)
    {
        return rc;
    }

    // get the required buffer length
    segment_length = sizeof("#EXTINF:.000,\n") - 1 + m3u8_builder_get_int_print_len(vod_div_ceil(mpeg_metadata->duration_millis, 1000)) +
                     segments_base_url->len + conf->segment_file_name_prefix.len + 1 + m3u8_builder_get_int_print_len(segment_durations.segment_count) + required_tracks.len + sizeof(".ts\n") - 1;

    result_size =
        sizeof(M3U8_HEADER_PART1) + VOD_INT64_LEN +
        sizeof(M3U8_HEADER_PART2) + VOD_INT64_LEN +
        segment_length * segment_durations.segment_count +
        sizeof(m3u8_footer);

    if (encryption_enabled)
    {
        result_size +=
            sizeof(encryption_key_tag_prefix) - 1 +
            base_url->len +
            conf->encryption_key_file_name.len +
            sizeof("-f") - 1 + VOD_INT32_LEN +
            sizeof(encryption_key_tag_postfix) - 1;
    }

    // allocate the buffer
    result->data = vod_alloc(request_context->pool, result_size);
    if (result->data == NULL)
    {
        vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
                       "m3u8_builder_build_index_playlist: vod_alloc failed");
        return VOD_ALLOC_FAILED;
    }

    // write the header
    p = vod_sprintf(
            result->data,
            M3U8_HEADER_PART1,
            (segmenter_conf->max_segment_duration + 500) / 1000);

    if (encryption_enabled)
    {
        p = vod_copy(p, encryption_key_tag_prefix, sizeof(encryption_key_tag_prefix) - 1);
        p = vod_copy(p, base_url->data, base_url->len);
        p = vod_copy(p, conf->encryption_key_file_name.data, conf->encryption_key_file_name.len);
        if (include_file_index)
        {
            p = vod_sprintf(p, "-f%uD", mpeg_metadata->first_stream->file_info.file_index + 1);
        }
        p = vod_copy(p, encryption_key_tag_postfix, sizeof(encryption_key_tag_postfix)-1);
    }

    p = vod_sprintf(
            p,
            M3U8_HEADER_PART2,
            conf->m3u8_version);

    // write the segments
    scale = conf->m3u8_version >= 3 ? 1000 : 1;
    last_item = segment_durations.items + segment_durations.item_count;

    for (cur_item = segment_durations.items; cur_item < last_item; cur_item++)
    {
        segment_index = cur_item->segment_index;
        last_segment_index = segment_index + cur_item->repeat_count;

        // write the first segment
        extinf.data = p;
        p = m3u8_builder_append_extinf_tag(p, rescale_time(cur_item->duration, segment_durations.timescale, scale), scale);
        extinf.len = p - extinf.data;
        p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &required_tracks);
        segment_index++;

        // write any additional segments
        for (; segment_index < last_segment_index; segment_index++)
        {
            p = vod_copy(p, extinf.data, extinf.len);
            p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &required_tracks);
        }
    }

    // write the footer
    p = vod_copy(p, m3u8_footer, sizeof(m3u8_footer) - 1);

    result->len = p - result->data;

    if (result->len > result_size)
    {
        vod_log_error(VOD_LOG_ERR, request_context->log, 0,
                      "m3u8_builder_build_index_playlist: result length %uz exceeded allocated length %uz",
                      result->len, result_size);
        return VOD_UNEXPECTED;
    }

    return VOD_OK;
}
Ejemplo n.º 24
0
vod_status_t
hls_muxer_simulate_get_iframes(
	request_context_t* request_context,
	segment_durations_t* segment_durations,
	hls_muxer_conf_t* muxer_conf,
	hls_encryption_params_t* encryption_params,
	media_set_t* media_set,
	hls_get_iframe_positions_callback_t callback,
	void* context)
{
	hls_muxer_stream_state_t* selected_stream;
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item;
	hls_muxer_state_t state;
	input_frame_t* cur_frame;
	uint32_t cur_frame_time;
	uint32_t frame_start = 0;
	uint32_t frame_size = 0;
	uint32_t frame_start_time = 0;
	uint32_t first_frame_time = 0;
	uint32_t end_time;
	uint32_t frame_segment_index = 0;
	uint32_t segment_index = 0;
	uint64_t cur_frame_dts;
	uint64_t cur_frame_time_offset;
	uint32_t repeat_count;
	uint64_t segment_end;
	bool_t simulation_supported;
	bool_t last_frame;
	vod_status_t rc;
#if (VOD_DEBUG)
	off_t cur_frame_start;
#endif
	
	cur_item = segment_durations->items;
	last_item = segment_durations->items + segment_durations->item_count;
	if (cur_item >= last_item)
	{
		return VOD_OK;
	}

	// initialize the muxer
	rc = hls_muxer_init_base(
		&state,
		request_context,
		muxer_conf,
		encryption_params,
		0,
		media_set,
		NULL,
		NULL,
		&simulation_supported, 
		NULL);
	if (rc != VOD_OK)
	{
		return rc;
	}

	if (!simulation_supported)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"hls_muxer_simulate_get_iframes: simulation not supported for this file, cant create iframe playlist");
		return VOD_BAD_REQUEST;
	}

	// initialize the repeat count, segment end, and the per stream limit
	repeat_count = cur_item->repeat_count - 1;
	segment_end = cur_item->duration;

	if (repeat_count <= 0 && (cur_item + 1 >= last_item || cur_item[1].discontinuity))
	{
		hls_muxer_simulation_set_segment_limit_unlimited(&state);
	}
	else
	{
		hls_muxer_simulation_set_segment_limit(&state, segment_end, segment_durations->timescale);
	}

	mpegts_encoder_simulated_start_segment(&state.queue);

	for (;;)
	{
		// get a frame
		for (;;)
		{
			// choose a stream for the current frame
			rc = hls_muxer_choose_stream(&state, &selected_stream);
			if (rc == VOD_OK)
			{
				break;
			}

			if (rc != VOD_NOT_FOUND)
			{
				return rc;
			}

			// update the limit for the next segment
			if (repeat_count <= 0)
			{
				cur_item++;
				if (cur_item >= last_item)
				{
					goto done;
				}

				repeat_count = cur_item->repeat_count;
			}

			repeat_count--;
			segment_end += cur_item->duration;

			if (repeat_count <= 0 && (cur_item + 1 >= last_item || cur_item[1].discontinuity))
			{
				hls_muxer_simulation_set_segment_limit_unlimited(&state);
			}
			else
			{
				hls_muxer_simulation_set_segment_limit(&state, segment_end, segment_durations->timescale);
			}

			// start the next segment
			mpegts_encoder_simulated_start_segment(&state.queue);
			segment_index++;
		}

		// update the stream state
		cur_frame = selected_stream->cur_frame;
		selected_stream->cur_frame++;
		cur_frame_time_offset = selected_stream->next_frame_time_offset;
		cur_frame_dts = selected_stream->next_frame_time_offset;
		selected_stream->next_frame_time_offset += cur_frame->duration;
		
		// flush any buffered frames if their delay becomes too big
		hls_muxer_simulation_flush_delayed_streams(&state, selected_stream, cur_frame_dts);

		// check whether this is the last frame of the selected stream in this segment
		last_frame = ((selected_stream->cur_frame >= selected_stream->cur_frame_part.last_frame && 
			selected_stream->cur_frame_part.next == NULL) ||
			selected_stream->next_frame_time_offset >= selected_stream->segment_limit);

		// write the frame
#if (VOD_DEBUG)
		cur_frame_start = state.queue.cur_offset;
#endif // VOD_DEBUG

		hls_muxer_simulation_write_frame(
			selected_stream,
			cur_frame,
			cur_frame_dts,
			last_frame);

#if (VOD_DEBUG)
		if (cur_frame_start != state.queue.cur_offset)
		{
			vod_log_debug4(VOD_LOG_DEBUG_LEVEL, state.request_context->log, 0,
				"hls_muxer_simulate_get_iframes: wrote frame segment %uD packets %uD-%uD dts %L",
				segment_index + 1,
				(uint32_t)(cur_frame_start / MPEGTS_PACKET_SIZE + 1),
				(uint32_t)(state.queue.cur_offset / MPEGTS_PACKET_SIZE + 1),
				cur_frame_dts);
		}
#endif // VOD_DEBUG

		// only care about video key frames
		if (selected_stream->media_type != MEDIA_TYPE_VIDEO)
		{
			continue;
		}

		if (!selected_stream->is_first_segment_frame && selected_stream->prev_key_frame)
		{
			// get the frame time
			cur_frame_time = rescale_time(selected_stream->prev_frame_pts, HLS_TIMESCALE, 1000);		// in millis
			if (frame_size != 0)
			{
				if (cur_frame_time > frame_start_time)
				{
					callback(context, frame_segment_index, cur_frame_time - frame_start_time, frame_start, frame_size);
				}
			}
			else
			{
				first_frame_time = cur_frame_time;
			}

			// save the info of the current keyframe
			frame_start = selected_stream->mpegts_encoder_state.last_frame_start_pos;
			frame_size = selected_stream->mpegts_encoder_state.last_frame_end_pos -
				selected_stream->mpegts_encoder_state.last_frame_start_pos;
			frame_start_time = cur_frame_time;
			frame_segment_index = segment_index;
		}

		if (last_frame && cur_frame[0].key_frame)
		{
			// get the frame time
			cur_frame_time = rescale_time(cur_frame_time_offset + cur_frame[0].pts_delay, HLS_TIMESCALE, 1000);		// in millis
			if (frame_size != 0)
			{
				if (cur_frame_time > frame_start_time)
				{
					callback(context, frame_segment_index, cur_frame_time - frame_start_time, frame_start, frame_size);
				}
			}
			else
			{
				first_frame_time = cur_frame_time;
			}

			// save the info of the current keyframe
			frame_start = selected_stream->mpegts_encoder_state.cur_frame_start_pos;
			frame_size = selected_stream->mpegts_encoder_state.cur_frame_end_pos -
				selected_stream->mpegts_encoder_state.cur_frame_start_pos;
			frame_start_time = cur_frame_time;
			frame_segment_index = segment_index;
		}

		selected_stream->prev_key_frame = cur_frame->key_frame;
		selected_stream->prev_frame_pts = cur_frame_time_offset + cur_frame->pts_delay;
		selected_stream->is_first_segment_frame = FALSE;
	}

done:

	// call the callback for the last frame
	end_time = first_frame_time + state.video_duration;
	if (frame_size != 0 && end_time > frame_start_time)
	{
		callback(context, frame_segment_index, end_time - frame_start_time, frame_start, frame_size);
	}

	return VOD_OK;
}
Ejemplo n.º 25
0
static vod_status_t
hds_calculate_output_offsets_and_write_afra_entries(
	hds_muxer_state_t* state, 
	uint32_t initial_value, 
	uint32_t afra_entries_base, 
	u_char** p)
{
	hds_muxer_stream_state_t* selected_stream;
	hds_muxer_stream_state_t* cur_stream;
	uint32_t cur_offset = initial_value;
	vod_status_t rc;

	for (;;)
	{
		// choose a stream
		rc = hds_muxer_choose_stream(state, &selected_stream);
		if (rc != VOD_OK)
		{
			if (rc == VOD_NOT_FOUND)
			{
				break;		// done
			}
			return rc;
		}

		// video key frames start with the codec info
		if (selected_stream->cur_frame->key_frame && selected_stream->media_type == MEDIA_TYPE_VIDEO && p != NULL)
		{
			*p = hds_write_afra_atom_entry(
				*p, 
				selected_stream->next_frame_dts + selected_stream->clip_start_time, 
				cur_offset + afra_entries_base);
			cur_offset += state->codec_config_size;
		}

		// skip the tag size
		cur_offset += tag_size_by_media_type[selected_stream->media_type];

		// set the offset (points to the beginning of the actual data)
		*selected_stream->cur_frame_output_offset = cur_offset;
		selected_stream->cur_frame_output_offset++;

		// move to the end of the frame
		cur_offset += selected_stream->cur_frame->size;
		cur_offset += sizeof(uint32_t);

		// move to the next frame
		selected_stream->next_frame_time_offset += selected_stream->cur_frame->duration;
		selected_stream->next_frame_dts = rescale_time(selected_stream->next_frame_time_offset, selected_stream->timescale, HDS_TIMESCALE);
		selected_stream->cur_frame++;
	}

	// reset the state
	if (state->clips_end > state->clips_start + 1)
	{
		state->cur_clip = state->clips_start;
		rc = hds_muxer_reinit_tracks(state);
		if (rc != VOD_OK)
		{
			vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
				"hds_calculate_output_offsets_and_write_afra_entries: unexpected - hds_muxer_reinit_tracks failed %i", rc);
			return rc;
		}

		for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++)
		{
			cur_stream->cur_frame_output_offset = cur_stream->first_frame_output_offset;
		}
	}
	else
	{
		for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++)
		{
			cur_stream->cur_frame = cur_stream->first_frame;
			cur_stream->cur_frame_output_offset = cur_stream->first_frame_output_offset;
			cur_stream->next_frame_time_offset = cur_stream->first_frame_time_offset;
			cur_stream->next_frame_dts = rescale_time(cur_stream->next_frame_time_offset, cur_stream->timescale, HDS_TIMESCALE);
		}
	}

	return VOD_OK;
}