vod_status_t read_cache_allocate_buffer_slots(read_cache_state_t* state, size_t buffer_count) { size_t alloc_size; if (buffer_count < MIN_BUFFER_COUNT) { buffer_count = MIN_BUFFER_COUNT; } if (state->buffer_count >= buffer_count) { return VOD_OK; } alloc_size = sizeof(state->buffers[0]) * buffer_count; state->buffers = vod_alloc(state->request_context->pool, alloc_size); if (state->buffers == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "read_cache_allocate_buffer_slots: vod_alloc failed"); return VOD_ALLOC_FAILED; } state->buffers_end = state->buffers + buffer_count; state->buffer_count = buffer_count; vod_memzero(state->buffers, alloc_size); return VOD_OK; }
void read_cache_init(read_cache_state_t* state, request_context_t* request_context, size_t buffer_size, size_t alignment) { vod_memzero(state, sizeof(*state)); state->request_context = request_context; state->buffer_size = buffer_size; state->alignment = alignment; }
void mp4_aes_ctr_set_iv( mp4_aes_ctr_state_t* state, u_char* iv) { vod_memcpy(state->counter, iv, MP4_AES_CTR_IV_SIZE); vod_memzero(state->counter + MP4_AES_CTR_IV_SIZE, sizeof(state->counter) - MP4_AES_CTR_IV_SIZE); state->block_offset = 0; }
void mp4_aes_ctr_set_iv( mp4_aes_ctr_state_t* state, u_char* iv) { vod_memcpy(state->counter, iv, MP4_AES_CTR_IV_SIZE); vod_memzero(state->counter + MP4_AES_CTR_IV_SIZE, sizeof(state->counter) - MP4_AES_CTR_IV_SIZE); state->encrypted_pos = NULL; state->encrypted_end = NULL; }
static void ngx_http_vod_hls_init_encryption_iv(u_char* iv, uint32_t segment_index) { u_char* p; // the IV is the segment index in big endian vod_memzero(iv, AES_BLOCK_SIZE - sizeof(uint32_t)); segment_index++; p = iv + AES_BLOCK_SIZE - sizeof(uint32_t); *p++ = (u_char)(segment_index >> 24); *p++ = (u_char)(segment_index >> 16); *p++ = (u_char)(segment_index >> 8); *p++ = (u_char)(segment_index); }
void audio_filter_free_state(void* context) { audio_filter_state_t* state = (audio_filter_state_t*)context; audio_filter_source_t* sources_cur; for (sources_cur = state->sources; sources_cur < state->sources_end; sources_cur++) { avcodec_close(sources_cur->decoder); av_free(sources_cur->decoder); } avcodec_close(state->sink.encoder); av_free(state->sink.encoder); avfilter_graph_free(&state->filter_graph); av_frame_free(&state->filtered_frame); av_frame_free(&state->decoded_frame); vod_memzero(state, sizeof(*state)); // support calling free twice }
vod_status_t mp4_decrypt_init( request_context_t* request_context, frames_source_t* frames_source, void* frames_source_context, u_char* key, media_encryption_t* encryption, void** result) { mp4_decrypt_state_t* state; vod_status_t rc; state = vod_alloc(request_context->pool, sizeof(*state)); if (state == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mp4_decrypt_init: vod_alloc failed"); return VOD_ALLOC_FAILED; } vod_memzero(state, sizeof(*state)); rc = mp4_aes_ctr_init(&state->cipher, request_context, key); if (rc != VOD_OK) { return rc; } vod_memcpy(state->key, key, sizeof(state->key)); state->request_context = request_context; state->frames_source = frames_source; state->frames_source_context = frames_source_context; state->reuse_buffers = TRUE; state->auxiliary_info_pos = encryption->auxiliary_info; state->auxiliary_info_end = encryption->auxiliary_info_end; state->use_subsamples = encryption->use_subsamples; *result = state; return VOD_OK; }
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 edash_packager_get_fragment_writer( segment_writer_t* result, request_context_t* request_context, media_set_t* media_set, uint32_t segment_index, bool_t single_nalu_per_frame, segment_writer_t* segment_writer, const u_char* iv, bool_t size_only, vod_str_t* fragment_header, size_t* total_fragment_size) { dash_fragment_header_extensions_t header_extensions; mp4_encrypt_passthrough_context_t passthrough_context; uint32_t media_type = media_set->sequences[0].media_type; vod_status_t rc; if (mp4_encrypt_passthrough_init(&passthrough_context, media_set->sequences)) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "edash_packager_get_fragment_writer: using encryption passthrough"); // get the header extensions header_extensions.extra_traf_atoms_size = passthrough_context.total_size + ATOM_HEADER_SIZE + sizeof(senc_atom_t); header_extensions.write_extra_traf_atoms_callback = edash_packager_passthrough_write_encryption_atoms; header_extensions.write_extra_traf_atoms_context = &passthrough_context; // build the fragment header rc = dash_packager_build_fragment_header( request_context, media_set, segment_index, 0, &header_extensions, size_only, fragment_header, total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "edash_packager_get_fragment_writer: dash_packager_build_fragment_header failed %i", rc); return rc; } // use original writer vod_memzero(result, sizeof(*result)); return VOD_OK; } switch (media_type) { case MEDIA_TYPE_VIDEO: return mp4_encrypt_video_get_fragment_writer( result, request_context, media_set, segment_index, single_nalu_per_frame, edash_packager_video_build_fragment_header, segment_writer, iv, fragment_header, total_fragment_size); case MEDIA_TYPE_AUDIO: rc = mp4_encrypt_audio_get_fragment_writer( result, request_context, media_set, segment_index, segment_writer, iv); if (rc != VOD_OK) { return rc; } rc = edash_packager_audio_build_fragment_header( result->context, size_only, fragment_header, total_fragment_size); if (rc != VOD_OK) { return rc; } return VOD_OK; } vod_log_error(VOD_LOG_ERR, request_context->log, 0, "edash_packager_get_fragment_writer: invalid media type %uD", media_type); return VOD_UNEXPECTED; }
static ngx_int_t ngx_http_vod_extract_uri_params( ngx_http_request_t* r, ngx_hash_t* params_hash, ngx_str_t* uri, media_sequence_t* sequence, uint32_t* clip_id, media_clip_source_t* source_clip, media_clip_t** result) { ngx_http_vod_uri_param_def_t* param_def = NULL; request_context_t request_context; media_clip_t* rate_filter = NULL; ngx_uint_t cur_key_hash = 0; ngx_str_t cur_param; ngx_int_t rc; uint32_t parsed_params_mask = 0; uint32_t param_index; u_char param_name[MAX_URI_PARAM_NAME_LEN + 1]; u_char* param_name_end = param_name + sizeof(param_name); u_char* param_name_pos = param_name; u_char* copy_start = uri->data; u_char* cur_pos; u_char* end_pos = uri->data + uri->len; u_char* last_slash = NULL; u_char* p; // set the source defaults vod_memzero(source_clip, sizeof(*source_clip)); source_clip->base.type = MEDIA_CLIP_SOURCE; source_clip->base.id = (*clip_id)++; source_clip->clip_to = UINT_MAX; source_clip->tracks_mask[MEDIA_TYPE_AUDIO] = 0xffffffff; source_clip->tracks_mask[MEDIA_TYPE_VIDEO] = 0xffffffff; source_clip->uri = *uri; source_clip->sequence = sequence; *result = &source_clip->base; // allocate the stripped uri p = ngx_palloc(r->pool, uri->len + 1); if (p == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_extract_uri_params: ngx_palloc failed (1)"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } source_clip->stripped_uri.data = p; for (cur_pos = uri->data; cur_pos <= end_pos; cur_pos++) { if (cur_pos < end_pos && *cur_pos != '/') { if (param_name_pos < param_name_end) { *param_name_pos = ngx_tolower(*cur_pos); cur_key_hash = ngx_hash(cur_key_hash, *param_name_pos); param_name_pos++; } continue; } if (last_slash == NULL) { last_slash = cur_pos; cur_key_hash = 0; param_name_pos = param_name; continue; } if (param_def == NULL) { param_def = ngx_hash_find(params_hash, cur_key_hash, param_name, param_name_pos - param_name); if (param_def != NULL) { p = ngx_copy(p, copy_start, last_slash - copy_start); copy_start = last_slash; } } else { param_index = param_def - uri_param_defs; if ((parsed_params_mask & (1 << param_index)) == 0) // first instance of a param takes priority { parsed_params_mask |= (1 << param_index); cur_param.data = last_slash + 1; cur_param.len = cur_pos - (last_slash + 1); if (param_def->name_conf_offset == offsetof(ngx_http_vod_loc_conf_t, speed_param_name)) { request_context.pool = r->pool; request_context.log = r->connection->log; rc = rate_filter_create_from_string( &request_context, &cur_param, &source_clip->base, &rate_filter); if (rc != VOD_OK) { return ngx_http_vod_status_to_ngx_error(rc); } rate_filter->id = (*clip_id)++; *result = rate_filter; } else { rc = param_def->parser(&cur_param, source_clip, param_def->target_offset); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_extract_uri_params: %s parser failed %i", param_def->name, rc); return rc; } } } copy_start = cur_pos; param_def = NULL; } last_slash = cur_pos; cur_key_hash = 0; param_name_pos = param_name; } if (source_clip->clip_from >= source_clip->clip_to) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_extract_uri_params: clip from %uD is larger than clip to %uD", source_clip->clip_from, source_clip->clip_to); return NGX_HTTP_BAD_REQUEST; } p = ngx_copy(p, copy_start, end_pos - copy_start); *p = '\0'; source_clip->stripped_uri.len = p - source_clip->stripped_uri.data; source_clip->mapped_uri = source_clip->stripped_uri; return NGX_OK; }
vod_status_t audio_filter_alloc_state( request_context_t* request_context, media_sequence_t* sequence, media_clip_t* clip, media_track_t* output_track, size_t* cache_buffer_count, void** result) { audio_filter_init_context_t init_context; u_char filter_name[VOD_INT32_LEN + 1]; audio_filter_state_t* state; vod_pool_cleanup_t *cln; AVFilterInOut *outputs = NULL; AVFilterInOut *inputs = NULL; uint32_t initial_alloc_size; vod_status_t rc; int avrc; 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; } // get the source count and graph desc size init_context.request_context = request_context; init_context.graph_desc_size = 0; init_context.source_count = 0; init_context.output_frame_count = 0; rc = audio_filter_walk_filters_prepare_init(&init_context, &clip, 100, 100); if (rc != VOD_OK) { return rc; } if (clip == NULL || init_context.source_count <= 0) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "audio_filter_alloc_state: unexpected - no sources found"); return VOD_UNEXPECTED; } if (clip->type == MEDIA_CLIP_SOURCE) { // got left with a source, following a mix of a single source, nothing to do return VOD_OK; } if (init_context.output_frame_count > MAX_FRAME_COUNT) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "audio_filter_alloc_state: expected output frame count %uD too big", init_context.output_frame_count); return VOD_BAD_REQUEST; } // 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)); // add to the cleanup pool cln = vod_pool_cleanup_add(request_context->pool, 0); if (cln == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "audio_filter_alloc_state: vod_pool_cleanup_add failed"); return VOD_ALLOC_FAILED; } cln->handler = audio_filter_free_state; cln->data = state; // allocate the filter graph state->filter_graph = avfilter_graph_alloc(); if (state->filter_graph == NULL) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "audio_filter_alloc_state: avfilter_graph_alloc failed"); return VOD_ALLOC_FAILED; } // allocate the graph desc and sources init_context.graph_desc = vod_alloc(request_context->pool, init_context.graph_desc_size + sizeof(state->sources[0]) * init_context.source_count); if (init_context.graph_desc == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "audio_filter_alloc_state: vod_alloc failed (1)"); return VOD_ALLOC_FAILED; } state->sources = (void*)(init_context.graph_desc + init_context.graph_desc_size); state->sources_end = state->sources + init_context.source_count; vod_memzero(state->sources, (u_char*)state->sources_end - (u_char*)state->sources); // initialize the sources and the graph description init_context.filter_graph = state->filter_graph; init_context.outputs = &outputs; init_context.cur_source = state->sources; init_context.graph_desc_pos = init_context.graph_desc; init_context.max_frame_size = 0; init_context.cache_slot_id = 0; rc = audio_filter_init_sources_and_graph_desc(&init_context, clip); if (rc != VOD_OK) { goto end; } *init_context.graph_desc_pos = '\0'; // initialize the sink vod_sprintf(filter_name, "%uD%Z", clip->id); rc = audio_filter_init_sink( request_context, state->filter_graph, output_track, filter_name, &state->sink, &inputs); if (rc != VOD_OK) { goto end; } // parse the graph description avrc = avfilter_graph_parse_ptr(state->filter_graph, (char*)init_context.graph_desc, &inputs, &outputs, NULL); if (avrc < 0) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "audio_filter_alloc_state: avfilter_graph_parse_ptr failed %d", avrc); rc = VOD_UNEXPECTED; goto end; } // validate and configure the graph avrc = avfilter_graph_config(state->filter_graph, NULL); if (avrc < 0) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "audio_filter_alloc_state: avfilter_graph_config failed %d", avrc); rc = VOD_UNEXPECTED; goto end; } // set the buffer sink frame size if ((state->sink.encoder->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) == 0) { av_buffersink_set_frame_size(state->sink.buffer_sink, state->sink.encoder->frame_size); } // 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)"); return VOD_ALLOC_FAILED; } 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)"); return VOD_ALLOC_FAILED; } // allocate the frame buffer state->frame_buffer = vod_alloc(request_context->pool, init_context.max_frame_size); if (state->frame_buffer == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "audio_filter_alloc_state: vod_alloc failed (2)"); rc = VOD_ALLOC_FAILED; goto end; } // initialize the output arrays initial_alloc_size = init_context.output_frame_count + 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)"); return VOD_ALLOC_FAILED; } state->request_context = request_context; state->sequence = sequence; state->output = output_track; state->cur_frame_pos = 0; state->first_time = TRUE; state->cur_source = NULL; *cache_buffer_count = init_context.cache_slot_id; *result = state; end: avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); return rc; }
static vod_status_t audio_filter_process_frame(audio_filter_state_t* state, u_char* buffer) { audio_filter_source_t* source = state->cur_source; input_frame_t* frame = source->cur_frame; AVPacket input_packet; int got_frame; int avrc; #ifdef AUDIO_FILTER_DEBUG size_t data_size; #endif // AUDIO_FILTER_DEBUG #ifdef AUDIO_FILTER_DEBUG audio_filter_append_debug_data("input", "aac", buffer, frame->size); #endif // AUDIO_FILTER_DEBUG vod_memzero(&input_packet, sizeof(input_packet)); input_packet.data = buffer; input_packet.size = frame->size; input_packet.dts = state->dts; input_packet.pts = state->dts + frame->pts_delay; input_packet.duration = frame->duration; input_packet.flags = AV_PKT_FLAG_KEY; state->dts += frame->duration; av_frame_unref(state->decoded_frame); got_frame = 0; avrc = avcodec_decode_audio4(source->decoder, state->decoded_frame, &got_frame, &input_packet); if (avrc < 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "audio_filter_process_frame: avcodec_decode_audio4 failed %d", avrc); return VOD_BAD_DATA; } if (!got_frame) { return VOD_OK; } #ifdef AUDIO_FILTER_DEBUG data_size = av_samples_get_buffer_size( NULL, source->decoder->channels, state->decoded_frame->nb_samples, source->decoder->sample_fmt, 1); audio_filter_append_debug_data(source->buffer_src->name, "pcm", state->decoded_frame->data[0], data_size); #endif // AUDIO_FILTER_DEBUG avrc = av_buffersrc_add_frame_flags(source->buffer_src, state->decoded_frame, AV_BUFFERSRC_FLAG_PUSH); if (avrc < 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "audio_filter_process_frame: av_buffersrc_add_frame_flags failed %d", avrc); return VOD_ALLOC_FAILED; } return audio_filter_read_filter_sink(state); }
ngx_int_t ngx_http_vod_parse_uri_path( ngx_http_request_t* r, ngx_str_t* multi_uri_suffix, ngx_hash_t* params_hash, ngx_str_t* uri, request_params_t* request_params, media_set_t* media_set) { media_sequence_t* cur_sequence; media_clip_source_t* cur_source; media_clip_source_t* sources_head; ngx_http_vod_multi_uri_t multi_uri; media_clip_t** cur_clip_ptr; media_clip_t* cur_clip; ngx_str_t parts[3]; ngx_str_t cur_uri; ngx_int_t rc; uint32_t sequences_mask; uint32_t parts_mask; uint32_t media_type; uint32_t clip_id = 1; uint32_t i; bool_t has_tracks; int uri_count; media_set->uri = *uri; // must save the uri before calling ngx_http_vod_parse_multi_uri as it may change multi_uri.parts_count = 0; rc = ngx_http_vod_parse_multi_uri(r, uri, multi_uri_suffix, &multi_uri); if (rc != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_http_vod_parse_multi_uri failed %i", rc); return rc; } if (multi_uri.parts_count > 1 && request_params->sequence_ids[0].len == 0) { sequences_mask = request_params->sequences_mask; request_params->sequences_mask = 0xffffffff; // reset the sequences mask so that it won't be applied again on the mapping request } else { sequences_mask = 0xffffffff; } parts_mask = (1 << multi_uri.parts_count) - 1; uri_count = vod_get_number_of_set_bits(sequences_mask & parts_mask); if (uri_count == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_path: request has no uris"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } cur_sequence = ngx_palloc(r->pool, (sizeof(*cur_sequence) + sizeof(*cur_source) + sizeof(*cur_clip_ptr)) * uri_count); if (cur_sequence == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_palloc failed"); return ngx_http_vod_status_to_ngx_error(r, VOD_ALLOC_FAILED); } media_set->sequences = cur_sequence; cur_source = (void*)(cur_sequence + uri_count); cur_clip_ptr = (void*)(cur_source + uri_count); sources_head = NULL; parts[0] = multi_uri.prefix; parts[2] = multi_uri.postfix; for (i = 0; i < multi_uri.parts_count; i++) { if ((sequences_mask & (1 << i)) == 0) { continue; } cur_sequence->id.len = 0; cur_sequence->language = 0; cur_sequence->label.len = 0; cur_sequence->first_key_frame_offset = 0; cur_sequence->key_frame_durations = NULL; cur_sequence->drm_info = NULL; vod_memzero(cur_sequence->bitrate, sizeof(cur_sequence->bitrate)); parts[1] = multi_uri.middle_parts[i]; rc = ngx_http_vod_merge_string_parts(r, parts, 3, &cur_uri); if (rc != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_http_vod_merge_string_parts failed %i", rc); return rc; } rc = ngx_http_vod_extract_uri_params(r, params_hash, &cur_uri, cur_sequence, &clip_id, cur_source, &cur_clip); if (rc != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_http_vod_extract_uri_params failed %i", rc); return rc; } has_tracks = FALSE; for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { if ((cur_source->tracks_mask[media_type] & request_params->tracks_mask[media_type]) != 0) { has_tracks = TRUE; break; } } if (!has_tracks) { continue; } *cur_clip_ptr = cur_clip; cur_source->next = sources_head; sources_head = cur_source; cur_sequence->clips = cur_clip_ptr; cur_sequence->index = i; cur_sequence->stripped_uri = cur_source->stripped_uri; cur_sequence->mapped_uri = cur_source->stripped_uri; cur_source++; cur_sequence++; cur_clip_ptr++; } // need to test again since we filtered sub uris that didn't have any required tracks media_set->sequence_count = cur_sequence - media_set->sequences; if (media_set->sequence_count <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_path: request has no uris after track filtering"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } media_set->sources_head = sources_head; media_set->sequences_end = cur_sequence; media_set->has_multi_sequences = (multi_uri.parts_count > 1); media_set->timing.total_count = 1; media_set->clip_count = 1; media_set->presentation_end = TRUE; return NGX_OK; }
vod_status_t audio_filter_process_frame(void* context, input_frame_t* frame, u_char* buffer) { audio_filter_state_t* state = (audio_filter_state_t*)context; vod_status_t rc; AVPacket output_packet; AVPacket input_packet; int got_packet; int got_frame; int ret; #ifdef AUDIO_FILTER_DEBUG size_t data_size; #endif // AUDIO_FILTER_DEBUG if (frame == NULL) { return audio_filter_flush_encoder(state); } #ifdef AUDIO_FILTER_DEBUG audio_filter_append_debug_data(AUDIO_FILTER_DEBUG_FILENAME_INPUT, buffer, frame->size); #endif // AUDIO_FILTER_DEBUG vod_memzero(&input_packet, sizeof(input_packet)); input_packet.data = buffer; input_packet.size = frame->size; input_packet.dts = state->dts; input_packet.pts = (state->dts + frame->pts_delay); input_packet.duration = frame->duration; input_packet.flags = AV_PKT_FLAG_KEY; state->dts += frame->duration; avcodec_get_frame_defaults(state->decoded_frame); got_frame = 0; ret = avcodec_decode_audio4(state->decoder, state->decoded_frame, &got_frame, &input_packet); if (ret < 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "audio_filter_process_frame: avcodec_decode_audio4 failed %d", ret); return VOD_BAD_DATA; } if (!got_frame) { return VOD_OK; } #ifdef AUDIO_FILTER_DEBUG data_size = av_samples_get_buffer_size( NULL, state->decoder->channels, state->decoded_frame->nb_samples, state->decoder->sample_fmt, 1); audio_filter_append_debug_data(AUDIO_FILTER_DEBUG_FILENAME_DECODED, state->decoded_frame->data[0], data_size); #endif // AUDIO_FILTER_DEBUG ret = av_buffersrc_add_frame_flags(state->buffer_src, state->decoded_frame, AV_BUFFERSRC_FLAG_PUSH); if (ret < 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "audio_filter_process_frame: av_buffersrc_add_frame_flags failed %d", ret); return VOD_ALLOC_FAILED; } for (;;) { ret = av_buffersink_get_frame_flags(state->buffer_sink, state->filtered_frame, AV_BUFFERSINK_FLAG_NO_REQUEST); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } if (ret < 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "audio_filter_process_frame: av_buffersink_get_frame_flags failed %d", ret); return VOD_UNEXPECTED; } #ifdef AUDIO_FILTER_DEBUG data_size = av_samples_get_buffer_size( NULL, state->encoder->channels, state->filtered_frame->nb_samples, state->encoder->sample_fmt, 1); audio_filter_append_debug_data(AUDIO_FILTER_DEBUG_FILENAME_FILTERED, state->filtered_frame->data[0], data_size); #endif // AUDIO_FILTER_DEBUG av_init_packet(&output_packet); output_packet.data = NULL; // packet data will be allocated by the encoder output_packet.size = 0; got_packet = 0; ret = avcodec_encode_audio2(state->encoder, &output_packet, state->filtered_frame, &got_packet); if (ret < 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "audio_filter_process_frame: avcodec_encode_audio2 failed %d", ret); return VOD_ALLOC_FAILED; } if (got_packet) { rc = audio_filter_write_frame(state, &output_packet); av_free_packet(&output_packet); if (rc != VOD_OK) { return rc; } } av_frame_unref(state->filtered_frame); } 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; }
static vod_status_t segmenter_get_segment_durations_estimate_internal( request_context_t* request_context, segmenter_conf_t* conf, media_set_t* media_set, media_sequence_t* sequence, uint32_t* clip_durations, uint32_t total_clip_count, uint64_t cur_clip_duration, segment_durations_t* result) { align_to_key_frames_context_t align_context; segment_duration_item_t* cur_item; uint64_t clip_start_offset; uint64_t ignore; uint64_t next_clip_offset; uint64_t next_aligned_offset; uint64_t aligned_offset = 0; uint64_t clip_offset = 0; uint32_t* end_duration = clip_durations + total_clip_count; uint32_t* cur_duration = clip_durations; uint32_t bootstrap_segment_limit; uint32_t segment_index = media_set->initial_segment_index; uint32_t clip_segment_limit; uint32_t segment_duration; uint32_t alloc_count; bool_t discontinuity; if (sequence->key_frame_durations != NULL) { align_context.request_context = request_context; align_context.part = sequence->key_frame_durations; align_context.offset = sequence->first_key_frame_offset; align_context.cur_pos = align_context.part->first; alloc_count = conf->bootstrap_segments_count + total_clip_count + vod_div_ceil(result->end_time - result->start_time, conf->segment_duration); } else { vod_memzero(&align_context, sizeof(align_context)); alloc_count = conf->bootstrap_segments_count + 2 * total_clip_count; } // allocate the result buffer result->items = vod_alloc(request_context->pool, sizeof(result->items[0]) * alloc_count); if (result->items == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "segmenter_get_segment_durations_estimate_internal: vod_alloc failed"); return VOD_ALLOC_FAILED; } cur_item = result->items - 1; discontinuity = FALSE; for (;;) { // find the clip start offset segmenter_get_start_end_offsets(conf, segment_index, &clip_start_offset, &ignore); // get segment limit for the current clip clip_segment_limit = conf->get_segment_count(conf, clip_start_offset + cur_clip_duration); if (clip_segment_limit == INVALID_SEGMENT_COUNT) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "segmenter_get_segment_durations_estimate_internal: segment count is invalid"); return VOD_BAD_DATA; } if (clip_segment_limit <= segment_index) { clip_segment_limit = segment_index + 1; } next_clip_offset = clip_offset + cur_clip_duration; // bootstrap segments bootstrap_segment_limit = vod_min(clip_segment_limit - 1, conf->bootstrap_segments_count); for (; segment_index < bootstrap_segment_limit; segment_index++) { segment_duration = conf->bootstrap_segments_durations[segment_index]; clip_offset += segment_duration; if (sequence->key_frame_durations != NULL) { next_aligned_offset = segmenter_align_to_key_frames(&align_context, clip_offset, next_clip_offset); segment_duration = next_aligned_offset - aligned_offset; aligned_offset = next_aligned_offset; } if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity) { cur_item++; cur_item->repeat_count = 0; cur_item->segment_index = segment_index; cur_item->duration = segment_duration; cur_item->discontinuity = discontinuity; discontinuity = FALSE; } cur_item->repeat_count++; } // remaining segments if (sequence->key_frame_durations != NULL) { for (; segment_index + 1 < clip_segment_limit; segment_index++) { clip_offset += conf->segment_duration; next_aligned_offset = segmenter_align_to_key_frames(&align_context, clip_offset, next_clip_offset); segment_duration = next_aligned_offset - aligned_offset; aligned_offset = next_aligned_offset; if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity) { cur_item++; cur_item->repeat_count = 0; cur_item->segment_index = segment_index; cur_item->duration = segment_duration; cur_item->discontinuity = discontinuity; discontinuity = FALSE; } cur_item->repeat_count++; } clip_offset = aligned_offset; // the last segment duration should be calcuated according to the aligned offset } else if (segment_index + 1 < clip_segment_limit) { segment_duration = conf->segment_duration; if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity) { cur_item++; cur_item->repeat_count = 0; cur_item->segment_index = segment_index; cur_item->duration = segment_duration; cur_item->discontinuity = discontinuity; discontinuity = FALSE; } cur_item->repeat_count += clip_segment_limit - segment_index - 1; clip_offset += (uint64_t)segment_duration * (clip_segment_limit - segment_index - 1); segment_index = clip_segment_limit - 1; } // last segment if (segment_index < clip_segment_limit && clip_offset < next_clip_offset) { segment_duration = next_clip_offset - clip_offset; if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity) { cur_item++; cur_item->repeat_count = 0; cur_item->segment_index = segment_index; cur_item->duration = segment_duration; cur_item->discontinuity = discontinuity; } cur_item->repeat_count++; segment_index = clip_segment_limit; } // move to the next clip cur_duration++; if (cur_duration >= end_duration) { break; } clip_offset = next_clip_offset; cur_clip_duration = *cur_duration; // update clip_start_offset discontinuity = TRUE; } // finalize the result result->segment_count = clip_segment_limit - media_set->initial_segment_index; if (result->segment_count > MAX_SEGMENT_COUNT) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "segmenter_get_segment_durations_estimate_internal: segment count %uD is invalid", result->segment_count); return VOD_BAD_MAPPING; } result->item_count = cur_item + 1 - result->items; result->timescale = 1000; result->discontinuities = total_clip_count - 1; return VOD_OK; }
vod_status_t filter_init_filtered_clips( request_context_t* request_context, media_set_t* media_set) { filters_init_state_t init_state; media_clip_filtered_t* output_clip; media_sequence_t* sequence; media_clip_t** clips_end; media_clip_t** cur_clip; media_clip_t* input_clip; media_track_t* new_track; uint32_t track_count[MEDIA_TYPE_COUNT]; uint32_t max_duration = 0; uint32_t clip_index; media_set->audio_filtering_needed = FALSE; media_set->track_count[MEDIA_TYPE_VIDEO] = 0; media_set->track_count[MEDIA_TYPE_AUDIO] = 0; // allocate the filtered clips output_clip = vod_alloc( request_context->pool, sizeof(output_clip[0]) * media_set->sequence_count * media_set->clip_count); if (output_clip == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "filter_init_filtered_clips: vod_alloc failed (1)"); return VOD_ALLOC_FAILED; } for (sequence = media_set->sequences; sequence < media_set->sequences_end; sequence++) { // init sequence counters sequence->total_frame_size = 0; sequence->video_key_frame_count = 0; sequence->total_frame_count = 0; // get max number of tracks in the clips of the sequence sequence->track_count[MEDIA_TYPE_VIDEO] = 0; sequence->track_count[MEDIA_TYPE_AUDIO] = 0; clips_end = sequence->clips + media_set->clip_count; for (cur_clip = sequence->clips; cur_clip < clips_end; cur_clip++) { track_count[MEDIA_TYPE_VIDEO] = 0; track_count[MEDIA_TYPE_AUDIO] = 0; filter_get_clip_track_count(*cur_clip, track_count); if (cur_clip[0]->type != MEDIA_CLIP_SOURCE && track_count[MEDIA_TYPE_AUDIO] > 1) { track_count[MEDIA_TYPE_AUDIO] = 1; // audio filtering supports only a single output track } if (cur_clip == sequence->clips) { sequence->track_count[MEDIA_TYPE_VIDEO] = track_count[MEDIA_TYPE_VIDEO]; sequence->track_count[MEDIA_TYPE_AUDIO] = track_count[MEDIA_TYPE_AUDIO]; } else if (sequence->track_count[MEDIA_TYPE_VIDEO] != track_count[MEDIA_TYPE_VIDEO] || sequence->track_count[MEDIA_TYPE_AUDIO] != track_count[MEDIA_TYPE_AUDIO]) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "filter_init_filtered_clips: track count mismatch, first clip had v=%uD,a=%uD current clip has v=%uD,a=%uD", sequence->track_count[MEDIA_TYPE_VIDEO], sequence->track_count[MEDIA_TYPE_AUDIO], track_count[MEDIA_TYPE_VIDEO], track_count[MEDIA_TYPE_AUDIO]); return VOD_BAD_MAPPING; } } // update the media set total track count media_set->track_count[MEDIA_TYPE_VIDEO] += sequence->track_count[MEDIA_TYPE_VIDEO]; media_set->track_count[MEDIA_TYPE_AUDIO] += sequence->track_count[MEDIA_TYPE_AUDIO]; sequence->total_track_count = sequence->track_count[MEDIA_TYPE_VIDEO] + sequence->track_count[MEDIA_TYPE_AUDIO]; // set the sequence media type if (sequence->total_track_count == 1) { sequence->media_type = sequence->track_count[MEDIA_TYPE_VIDEO] > 0 ? MEDIA_TYPE_VIDEO : MEDIA_TYPE_AUDIO; } else { sequence->media_type = MEDIA_TYPE_NONE; } // initialize the filtered clips array sequence->filtered_clips = output_clip; output_clip += media_set->clip_count; sequence->filtered_clips_end = output_clip; } // allocate the output tracks media_set->total_track_count = media_set->track_count[MEDIA_TYPE_VIDEO] + media_set->track_count[MEDIA_TYPE_AUDIO]; init_state.output_track = vod_alloc( request_context->pool, sizeof(*init_state.output_track)* media_set->total_track_count * media_set->clip_count); if (init_state.output_track == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "filter_init_filtered_clips: vod_alloc failed (2)"); return VOD_ALLOC_FAILED; } media_set->filtered_tracks = init_state.output_track; for (clip_index = 0; clip_index < media_set->clip_count; clip_index++) { for (sequence = media_set->sequences; sequence < media_set->sequences_end; sequence++) { input_clip = sequence->clips[clip_index]; output_clip = &sequence->filtered_clips[clip_index]; output_clip->first_track = init_state.output_track; output_clip->longest_track[MEDIA_TYPE_VIDEO] = NULL; output_clip->longest_track[MEDIA_TYPE_AUDIO] = NULL; // initialize the state init_state.sequence = sequence; init_state.output_clip = output_clip; init_state.audio_reference_track = NULL; // in case of source, just copy all tracks as is if (input_clip->type == MEDIA_CLIP_SOURCE) { filter_init_filtered_clip_from_source(&init_state, (media_clip_source_t*)input_clip); } else { init_state.has_audio_frames = FALSE; init_state.source_count = 0; filter_scale_video_tracks(&init_state, input_clip, 100, 100); if (init_state.source_count != 1) { // got more than one mvhd, clear it vod_memzero(&output_clip->mvhd_atom, sizeof(output_clip->mvhd_atom)); } } if (init_state.audio_reference_track != NULL) { // add the audio filter output track new_track = filter_copy_track_to_clip(&init_state, init_state.audio_reference_track); if (init_state.audio_reference_track_speed_nom != init_state.audio_reference_track_speed_denom) { rate_filter_scale_track_timestamps( new_track, init_state.audio_reference_track_speed_nom, init_state.audio_reference_track_speed_denom); } if (init_state.has_audio_frames) { new_track->source_clip = input_clip; media_set->audio_filtering_needed = TRUE; } } output_clip->last_track = init_state.output_track; // calculate the max duration, only relevant in case of single clip if (output_clip->longest_track[MEDIA_TYPE_VIDEO] != NULL && output_clip->longest_track[MEDIA_TYPE_VIDEO]->media_info.duration_millis > max_duration) { max_duration = output_clip->longest_track[MEDIA_TYPE_VIDEO]->media_info.duration_millis; } if (output_clip->longest_track[MEDIA_TYPE_AUDIO] != NULL && output_clip->longest_track[MEDIA_TYPE_AUDIO]->media_info.duration_millis > max_duration) { max_duration = output_clip->longest_track[MEDIA_TYPE_AUDIO]->media_info.duration_millis; } } } media_set->filtered_tracks_end = init_state.output_track; if (media_set->durations == NULL) { media_set->total_duration = max_duration; } return VOD_OK; }
vod_status_t rate_filter_parse( void* ctx, vod_json_value_t* element, void** result) { media_filter_parse_context_t* context = ctx; media_clip_rate_filter_t* filter; media_range_t* new_range; media_range_t* old_range; vod_json_value_t* params[RATE_FILTER_PARAM_COUNT]; vod_json_value_t* source; vod_json_value_t* rate; uint32_t old_duration; vod_status_t rc; vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0, "rate_filter_parse: started"); vod_memzero(params, sizeof(params)); vod_json_get_object_values( element, &rate_filter_hash, params); rate = params[RATE_FILTER_PARAM_RATE]; source = params[RATE_FILTER_PARAM_SOURCE]; if (rate == NULL || source == NULL) { vod_log_error(VOD_LOG_ERR, context->request_context->log, 0, "rate_filter_parse: \"rate\" and \"source\" are mandatory for rate filter"); return VOD_BAD_MAPPING; } if (rate->v.num.denom > 100) { vod_log_error(VOD_LOG_ERR, context->request_context->log, 0, "rate_filter_parse: invalid rate, only 2 decimal points are allowed"); return VOD_BAD_MAPPING; } if (rate->v.num.nom < 0 || rate->v.num.denom > (uint64_t)rate->v.num.nom * 2 || (uint64_t)rate->v.num.nom > rate->v.num.denom * 2) { vod_log_error(VOD_LOG_ERR, context->request_context->log, 0, "rate_filter_parse: invalid rate %L/%uL, must be between 0.5 and 2", rate->v.num.nom, rate->v.num.denom); return VOD_BAD_MAPPING; } filter = vod_alloc(context->request_context->pool, sizeof(*filter) + sizeof(filter->base.sources[0])); if (filter == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0, "rate_filter_parse: vod_alloc failed (1)"); return VOD_ALLOC_FAILED; } filter->base.sources = (void*)(filter + 1); filter->base.source_count = 1; filter->base.type = MEDIA_CLIP_RATE_FILTER; filter->base.audio_filter = &rate_filter; filter->rate.nom = rate->v.num.nom; filter->rate.denom = rate->v.num.denom; old_range = context->range; if (old_range != NULL) { new_range = vod_alloc(context->request_context->pool, sizeof(*new_range)); if (new_range == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0, "rate_filter_parse: vod_alloc failed (2)"); return VOD_ALLOC_FAILED; } new_range->start = (old_range->start * filter->rate.nom) / filter->rate.denom; new_range->end = (old_range->end * filter->rate.nom) / filter->rate.denom; new_range->timescale = old_range->timescale; context->range = new_range; } old_duration = context->duration; context->duration = ((uint64_t)old_duration * filter->rate.nom) / filter->rate.denom; rc = media_set_parse_clip( context, source, &filter->base, &filter->base.sources[0]); if (rc != VOD_JSON_OK) { return rc; } context->range = old_range; context->duration = old_duration; *result = &filter->base; vod_log_debug2(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0, "rate_filter_parse: done, rate=%uD/%uD", filter->rate.nom, filter->rate.denom); return VOD_OK; }
vod_status_t mss_playready_get_fragment_writer( segment_writer_t* result, request_context_t* request_context, media_set_t* media_set, uint32_t segment_index, segment_writer_t* segment_writer, const u_char* iv, bool_t size_only, vod_str_t* fragment_header, size_t* total_fragment_size) { mp4_encrypt_passthrough_context_t passthrough_context; uint32_t media_type = media_set->sequences[0].media_type; vod_status_t rc; if (mp4_encrypt_passthrough_init(&passthrough_context, media_set->sequences)) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mss_playready_get_fragment_writer: using encryption passthrough"); // build the fragment header rc = mss_packager_build_fragment_header( request_context, media_set, segment_index, passthrough_context.total_size + ATOM_HEADER_SIZE + sizeof(uuid_piff_atom_t), mss_playready_passthrough_write_encryption_atoms, &passthrough_context, size_only, fragment_header, total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mss_playready_get_fragment_writer: mss_packager_build_fragment_header failed %i", rc); return rc; } // use original writer vod_memzero(result, sizeof(*result)); return VOD_OK; } switch (media_type) { case MEDIA_TYPE_VIDEO: return mp4_encrypt_video_get_fragment_writer( result, request_context, media_set, segment_index, mss_playready_video_write_fragment_header, segment_writer, iv); case MEDIA_TYPE_AUDIO: rc = mp4_encrypt_audio_get_fragment_writer( result, request_context, media_set, segment_index, segment_writer, iv); if (rc != VOD_OK) { return rc; } rc = mss_playready_audio_build_fragment_header( result->context, size_only, fragment_header, total_fragment_size); if (rc != VOD_OK) { return rc; } return VOD_OK; } vod_log_error(VOD_LOG_ERR, request_context->log, 0, "mss_playready_get_fragment_writer: invalid media type %uD", media_type); return VOD_UNEXPECTED; }
vod_status_t edash_packager_build_mpd( request_context_t* request_context, dash_manifest_config_t* conf, vod_str_t* base_url, media_set_t* media_set, bool_t drm_single_key, vod_str_t* result) { write_content_protection_context_t context; dash_manifest_extensions_t extensions; media_sequence_t* cur_sequence; drm_system_info_t* cur_info; tags_writer_t content_prot_writer; drm_info_t* drm_info; size_t representation_tags_size; size_t cur_drm_tags_size; size_t cur_pssh_size; size_t max_pssh_size = 0; vod_status_t rc; representation_tags_size = 0; for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++) { drm_info = (drm_info_t*)cur_sequence->drm_info; cur_drm_tags_size = sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC) - 1; for (cur_info = drm_info->pssh_array.first; cur_info < drm_info->pssh_array.last; cur_info++) { if (vod_memcmp(cur_info->system_id, edash_playready_system_id, sizeof(edash_playready_system_id)) == 0) { cur_drm_tags_size += sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_V2_PART1) - 1 + VOD_GUID_LENGTH + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_V2_PART2) - 1 + VOD_GUID_LENGTH + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART3) - 1 + vod_base64_encoded_length(cur_info->data.len) + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART4) - 1; } else { cur_pssh_size = ATOM_HEADER_SIZE + sizeof(pssh_atom_t) + cur_info->data.len; if (cur_pssh_size > max_pssh_size) { max_pssh_size = cur_pssh_size; } cur_drm_tags_size += sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART1) - 1 + VOD_GUID_LENGTH + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART2) - 1 + VOD_GUID_LENGTH + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART3) - 1 + vod_base64_encoded_length(cur_pssh_size) + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART4) - 1; continue; } } representation_tags_size += cur_drm_tags_size * cur_sequence->total_track_count; } context.write_playready_kid = conf->write_playready_kid; if (max_pssh_size > 0) { context.temp_buffer = vod_alloc(request_context->pool, max_pssh_size); if (context.temp_buffer == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "edash_packager_build_mpd: vod_alloc failed"); return VOD_ALLOC_FAILED; } } content_prot_writer.size = representation_tags_size; content_prot_writer.write = edash_packager_write_content_protection; content_prot_writer.context = &context; if (drm_single_key) { // write the ContentProtection tags under AdaptationSet extensions.adaptation_set = content_prot_writer; vod_memzero(&extensions.representation, sizeof(extensions.representation)); } else { // write the ContentProtection tags under Representation vod_memzero(&extensions.adaptation_set, sizeof(extensions.adaptation_set)); extensions.representation = content_prot_writer; } rc = dash_packager_build_mpd( request_context, conf, base_url, media_set, &extensions, result); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "edash_packager_build_mpd: dash_packager_build_mpd failed %i", rc); return rc; } return VOD_OK; }