static vod_status_t mp4_encrypt_write_encrypted( mp4_encrypt_state_t* state, u_char* cur_pos, uint32_t write_size) { uint32_t cur_write_size; size_t alloc_size; u_char* write_end; u_char* output; vod_status_t rc; write_end = cur_pos + write_size; while (cur_pos < write_end) { rc = write_buffer_get_bytes(&state->write_buffer, MIN_ALLOC_SIZE, &alloc_size, &output); if (rc != VOD_OK) { return rc; } cur_write_size = write_end - cur_pos; cur_write_size = vod_min(cur_write_size, alloc_size); rc = mp4_aes_ctr_process(&state->cipher, output, cur_pos, cur_write_size); if (rc != VOD_OK) { return rc; } cur_pos += cur_write_size; state->write_buffer.cur_pos += cur_write_size; } return VOD_OK; }
static void segmenter_boundary_iterator_skip(segmenter_boundary_iterator_context_t* context, uint32_t count) { context->segment_index = vod_min(context->segment_index + count, context->segment_count); if (context->segment_index > context->conf->bootstrap_segments_count) { context->last_boundary = context->conf->bootstrap_segments_total_duration + (context->segment_index - context->conf->bootstrap_segments_count) * context->conf->segment_duration; } }
static vod_status_t mp4_encrypt_audio_write_buffer(void* context, u_char* buffer, uint32_t size) { mp4_encrypt_state_t* state = (mp4_encrypt_state_t*)context; u_char* buffer_end = buffer + size; u_char* cur_pos = buffer; uint32_t write_size; bool_t ignore; vod_status_t rc; while (cur_pos < buffer_end) { if (state->frame_size_left <= 0) { rc = mp4_encrypt_start_frame(state); if (rc != VOD_OK) { return rc; } } write_size = (uint32_t)(buffer_end - cur_pos); write_size = vod_min(write_size, state->frame_size_left); rc = mp4_encrypt_write_encrypted(state, cur_pos, write_size); if (rc != VOD_OK) { return rc; } cur_pos += write_size; state->frame_size_left -= write_size; if (state->frame_size_left > 0) { break; } // finished a frame if (!mp4_encrypt_move_to_next_frame(state, &ignore)) { // finished all frames rc = write_buffer_flush(&state->write_buffer, FALSE); if (rc != VOD_OK) { return rc; } } } return VOD_OK; }
vod_status_t mp4_aes_ctr_process(mp4_aes_ctr_state_t* state, u_char* dest, const u_char* src, uint32_t size) { const u_char* src_end = src + size; const u_char* cur_end_pos; u_char* encrypted_counter_pos; int out_size; while (src < src_end) { if (state->block_offset == 0) { if (1 != EVP_EncryptUpdate( &state->cipher, state->encrypted_counter, &out_size, state->counter, sizeof(state->counter)) || out_size != sizeof(state->encrypted_counter)) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "mp4_aes_ctr_process: EVP_EncryptUpdate failed"); return VOD_UNEXPECTED; } mp4_aes_ctr_increment_be64(state->counter + 8); } encrypted_counter_pos = state->encrypted_counter + state->block_offset; cur_end_pos = src + MP4_AES_CTR_COUNTER_SIZE - state->block_offset; cur_end_pos = vod_min(cur_end_pos, src_end); state->block_offset += cur_end_pos - src; state->block_offset &= (MP4_AES_CTR_COUNTER_SIZE - 1); while (src < cur_end_pos) { *dest++ = *src++ ^ *encrypted_counter_pos++; } } return VOD_OK; }
static int64_t segmenter_align_to_key_frames( align_to_key_frames_context_t* context, int64_t offset, int64_t limit) { int64_t cur_duration; for (; context->offset < offset; context->cur_pos++) { if ((void*)context->cur_pos >= context->part->last) { if (context->part->next == NULL) { return limit; } context->part = context->part->next; context->cur_pos = context->part->first; } cur_duration = *context->cur_pos; if (cur_duration <= 0 || cur_duration > MAX_CLIP_DURATION) { vod_log_error(VOD_LOG_WARN, context->request_context->log, 0, "segmenter_align_to_key_frames: ignoring invalid key frame duration %L", cur_duration); continue; } context->offset += cur_duration; if (context->offset >= limit) { return limit; } } return vod_min(context->offset, limit); }
vod_status_t read_cache_get_read_buffer(read_cache_state_t* state, uint32_t* file_index, uint64_t* out_offset, u_char** buffer, uint32_t* size) { cache_buffer_t* target_buffer; cache_buffer_t* cur_buffer; cache_buffer_t* buffers_end = state->buffers + CACHED_BUFFERS; uint32_t read_size; // select a buffer target_buffer = state->target_buffer; // make sure the buffer is allocated if (target_buffer->buffer == NULL) { target_buffer->buffer = vod_memalign(state->request_context->pool, state->buffer_size + 1, state->alignment); if (target_buffer->buffer == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "read_cache_get_read_buffer: vod_memalign failed"); return VOD_ALLOC_FAILED; } } // make sure we don't read anything we already have in cache read_size = state->buffer_size; for (cur_buffer = state->buffers; cur_buffer < buffers_end; cur_buffer++) { if (cur_buffer != target_buffer && cur_buffer->start_offset > target_buffer->start_offset) { read_size = vod_min(read_size, cur_buffer->start_offset - target_buffer->start_offset); } } // return the target buffer pointer and size *file_index = target_buffer->file_index; *out_offset = target_buffer->start_offset; *buffer = target_buffer->buffer; *size = read_size; return VOD_OK; }
bool_t read_cache_get_from_cache( read_cache_state_t* state, read_cache_request_t* request, u_char** buffer, uint32_t* size) { media_clip_source_t* source = request->source; cache_buffer_t* target_buffer; cache_buffer_t* cur_buffer; uint32_t read_size; uint64_t aligned_last_offset; uint64_t offset = request->cur_offset; size_t alignment; // check whether we already have the requested offset for (cur_buffer = state->buffers; cur_buffer < state->buffers_end; cur_buffer++) { if (cur_buffer->source == source && offset >= cur_buffer->start_offset && offset < cur_buffer->end_offset) { *buffer = cur_buffer->buffer_pos + (offset - cur_buffer->start_offset); *size = cur_buffer->end_offset - offset; return TRUE; } } // don't have the offset in cache alignment = state->alignment - 1; target_buffer = &state->buffers[request->cache_slot_id % state->buffer_count]; // start reading from the min offset, if that would contain the whole frame // Note: this condition is intended to optimize the case in which the frame order // in the output segment is <video1><audio1> while on disk it's <audio1><video1>. // in this case it would be better to start reading from the beginning, even // though the first frame that is requested is the second one if (request->min_offset < offset && request->end_offset < (request->min_offset & ~alignment) + state->buffer_size) { offset = request->min_offset; } offset &= ~alignment; // calculate the read size read_size = state->buffer_size; // don't read anything that is already in the cache for (cur_buffer = state->buffers; cur_buffer < state->buffers_end; cur_buffer++) { if (cur_buffer != target_buffer && cur_buffer->source == source && cur_buffer->start_offset > offset) { read_size = vod_min(read_size, cur_buffer->start_offset - offset); } } // don't read past the max required offset if (offset + read_size > source->last_offset) { aligned_last_offset = (source->last_offset + alignment) & ~alignment; if (aligned_last_offset > offset) { read_size = aligned_last_offset - offset; } } target_buffer->source = source; target_buffer->start_offset = offset; target_buffer->buffer_size = read_size; state->target_buffer = target_buffer; return FALSE; }
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 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; }
static vod_status_t segmenter_get_segment_durations_estimate_internal( request_context_t* request_context, segmenter_conf_t* conf, uint32_t* clip_durations, uint32_t total_clip_count, segment_durations_t* result) { segment_duration_item_t* cur_item; uint64_t clip_start_offset = 0; uint64_t ignore; uint32_t* end_duration = clip_durations + total_clip_count; uint32_t* cur_duration; uint32_t bootstrap_segment_limit; uint32_t segment_index = 0; uint32_t clip_segment_limit = 0; uint32_t clip_offset; uint32_t segment_duration; bool_t discontinuity; // allocate the result buffer result->items = vod_alloc(request_context->pool, sizeof(result->items[0]) * (conf->bootstrap_segments_count + 2 * total_clip_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 (cur_duration = clip_durations; cur_duration < end_duration; cur_duration++) { // get segment limit for the current clip clip_segment_limit = conf->get_segment_count(conf, clip_start_offset + *cur_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; } clip_offset = 0; // 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]; 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 += segment_duration; } // remaining segments 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 += segment_duration * (clip_segment_limit - segment_index - 1); segment_index = clip_segment_limit - 1; } // last segment if (segment_index < clip_segment_limit && clip_offset < *cur_duration) { segment_duration = *cur_duration - 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; } // update clip_start_offset segmenter_get_start_end_offsets(conf, clip_segment_limit, &clip_start_offset, &ignore); discontinuity = TRUE; } // finalize the result result->segment_count = clip_segment_limit; result->item_count = cur_item + 1 - result->items; result->timescale = 1000; result->discontinuities = total_clip_count - 1; return VOD_OK; }
static vod_status_t mp4_decrypt_read(void* ctx, u_char** buffer, uint32_t* size, bool_t* frame_done) { mp4_decrypt_state_t* state = ctx; vod_status_t rc; uint32_t cur_size; size_t buffer_size; // make sure there is some output space if (state->output_pos + MIN_BUFFER_SIZE >= state->output_end) { if (!state->reuse_buffers || state->output_start == NULL) { buffer_size = BUFFER_SIZE; state->output_start = buffer_pool_alloc( state->request_context, state->request_context->output_buffer_pool, &buffer_size); if (state->output_start == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mp4_decrypt_read: vod_alloc failed"); return VOD_ALLOC_FAILED; } state->output_end = state->output_start + buffer_size; } state->output_pos = state->output_start; } // make sure there is some input buffer if (state->input_size <= 0) { rc = state->frames_source->read( state->frames_source_context, &state->input_pos, &state->input_size, &state->frame_done); if (rc != VOD_OK) { return rc; } } // process the min of input size and output size cur_size = state->output_end - state->output_pos; cur_size = vod_min(cur_size, state->input_size); state->input_size -= cur_size; *buffer = state->output_pos; *size = cur_size; *frame_done = state->input_size <= 0 ? state->frame_done : FALSE; rc = mp4_decrypt_process(state, cur_size); if (rc != VOD_OK) { return rc; } return VOD_OK; }
static vod_status_t mp4_decrypt_process( mp4_decrypt_state_t* state, size_t size) { u_char* dest = state->output_pos; u_char* src = state->input_pos; vod_status_t rc; size_t cur_size; while (size > 0) { if (state->clear_bytes <= 0 && state->encrypted_bytes <= 0) { // finished a subsample, read the next one if (state->subsample_count <= 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "mp4_decrypt_process: exhausted subsample bytes"); return VOD_BAD_DATA; } if (state->auxiliary_info_pos + sizeof(cenc_sample_auxiliary_data_subsample_t) > state->auxiliary_info_end) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "mp4_decrypt_process: failed to get subsample info from auxiliary info"); return VOD_BAD_DATA; } read_be16(state->auxiliary_info_pos, state->clear_bytes); read_be32(state->auxiliary_info_pos, state->encrypted_bytes); state->subsample_count--; } if (state->clear_bytes > 0) { // copy clear bytes cur_size = vod_min(state->clear_bytes, size); dest = vod_copy(dest, src, cur_size); src += cur_size; size -= cur_size; state->clear_bytes -= cur_size; } // decrypt encrypted bytes cur_size = vod_min(state->encrypted_bytes, size); rc = mp4_aes_ctr_process(&state->cipher, dest, src, cur_size); if (rc != VOD_OK) { return rc; } dest += cur_size; src += cur_size; size -= cur_size; state->encrypted_bytes -= cur_size; } state->output_pos = dest; state->input_pos = src; return VOD_OK; }
vod_status_t hds_muxer_process_frames(hds_muxer_state_t* state) { u_char* read_buffer; uint32_t read_size; uint32_t write_size; uint64_t offset; vod_status_t rc; bool_t first_time = (state->cur_frame == NULL); bool_t wrote_data = FALSE; for (;;) { // start a new frame if we don't have a frame if (state->cur_frame == NULL) { rc = hds_muxer_start_frame(state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: hds_muxer_start_frame failed %i", rc); return rc; } if (state->cur_frame == NULL) { break; // done } } // read some data from the frame offset = state->cur_frame_offset + state->cur_frame_pos; if (!read_cache_get_from_cache(state->read_cache_state, state->cur_frame->size - state->cur_frame_pos, state->cache_slot_id, state->cur_file_index, offset, &read_buffer, &read_size)) { if (!wrote_data && !first_time) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "hds_muxer_process_frames: no data was handled, probably a truncated file"); return VOD_BAD_DATA; } return VOD_AGAIN; } wrote_data = TRUE; // write the frame write_size = vod_min(state->cur_frame->size - state->cur_frame_pos, read_size); rc = write_buffer_write(&state->write_buffer_state, read_buffer, write_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_write failed %i", rc); return rc; } state->cur_frame_pos += write_size; // flush the frame if we finished writing it if (state->cur_frame_pos >= state->cur_frame->size) { rc = hds_muxer_end_frame(state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_write failed %i", rc); return rc; } state->cur_frame = NULL; } } // flush the buffer rc = write_buffer_flush(&state->write_buffer_state, FALSE); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_flush failed %i", rc); return rc; } return VOD_OK; }
vod_status_t sample_aes_avc_filter_write_nal_body(void* context, const u_char* buffer, uint32_t size) { sample_aes_avc_filter_state_t* state = (sample_aes_avc_filter_state_t*)context; uint32_t end_offset; uint32_t cur_size; u_char encrypted_block[AES_BLOCK_SIZE]; vod_status_t rc; int out_size; if (!state->encrypt) { return state->write_callback(state->write_context, buffer, size); } for (end_offset = state->cur_offset + size; state->cur_offset < end_offset; state->cur_offset += cur_size, buffer += cur_size) { if (state->cur_offset < state->next_encrypt_offset) { // unencrypted part cur_size = vod_min(state->next_encrypt_offset, end_offset) - state->cur_offset; rc = sample_aes_avc_write_emulation_prevention(state, buffer, cur_size); if (rc != VOD_OK) { return rc; } continue; } // encrypted block cur_size = vod_min(state->next_encrypt_offset + AES_BLOCK_SIZE, end_offset) - state->cur_offset; if (1 != EVP_EncryptUpdate(&state->cipher, encrypted_block, &out_size, buffer, cur_size)) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "sample_aes_avc_filter_write_nal_body: EVP_EncryptUpdate failed"); return VOD_UNEXPECTED; } if (out_size <= 0) { continue; } // write the encrypted block rc = sample_aes_avc_write_emulation_prevention(state, encrypted_block, AES_BLOCK_SIZE); if (rc != VOD_OK) { return rc; } // find the next encrypt offset state->next_encrypt_offset += ENCRYPTED_BLOCK_PERIOD * AES_BLOCK_SIZE; if (state->next_encrypt_offset >= state->max_encrypt_offset) { state->next_encrypt_offset = UINT_MAX; } } return VOD_OK; }
static vod_status_t mp4_encrypt_video_write_buffer(void* context, u_char* buffer, uint32_t size) { mp4_encrypt_video_state_t* state = (mp4_encrypt_video_state_t*)context; vod_str_t fragment_header; u_char* buffer_end = buffer + size; u_char* cur_pos = buffer; u_char* output; uint32_t write_size; int32_t cur_shift; size_t ignore; bool_t init_track; vod_status_t rc; while (cur_pos < buffer_end) { switch (state->cur_state) { case STATE_PACKET_SIZE: if (state->base.frame_size_left <= 0) { rc = mp4_encrypt_video_start_frame(state); if (rc != VOD_OK) { return rc; } if (state->base.frame_size_left <= 0) { state->cur_state = STATE_PACKET_DATA; break; } } for (; state->length_bytes_left && cur_pos < buffer_end; state->length_bytes_left--) { state->packet_size_left = (state->packet_size_left << 8) | *cur_pos++; } if (cur_pos >= buffer_end) { break; } if (state->base.frame_size_left < state->nal_packet_size_length + state->packet_size_left) { vod_log_error(VOD_LOG_ERR, state->base.request_context->log, 0, "mp4_encrypt_video_write_buffer: frame size %uD too small, nalu size %uD packet size %uD", state->base.frame_size_left, state->nal_packet_size_length, state->packet_size_left); return VOD_BAD_DATA; } state->base.frame_size_left -= state->nal_packet_size_length + state->packet_size_left; state->cur_state++; // fall through case STATE_NAL_TYPE: // write the packet size and nal type rc = write_buffer_get_bytes(&state->base.write_buffer, state->nal_packet_size_length + 1, NULL, &output); if (rc != VOD_OK) { return rc; } for (cur_shift = (state->nal_packet_size_length - 1) * 8; cur_shift >= 0; cur_shift -= 8) { *output++ = (state->packet_size_left >> cur_shift) & 0xff; } *output++ = *cur_pos++; // nal type // update the packet size if (state->packet_size_left <= 0) { vod_log_error(VOD_LOG_ERR, state->base.request_context->log, 0, "mp4_encrypt_video_write_buffer: zero size packet"); return VOD_BAD_DATA; } state->packet_size_left--; // add the subsample rc = mp4_encrypt_video_add_subsample(state, state->nal_packet_size_length + 1, state->packet_size_left); if (rc != VOD_OK) { return rc; } state->cur_state++; // fall through case STATE_PACKET_DATA: write_size = (uint32_t)(buffer_end - cur_pos); write_size = vod_min(write_size, state->packet_size_left); rc = mp4_encrypt_write_encrypted(&state->base, cur_pos, write_size); if (rc != VOD_OK) { return rc; } cur_pos += write_size; state->packet_size_left -= write_size; if (state->packet_size_left > 0) { break; } // finished a packet state->cur_state = STATE_PACKET_SIZE; state->length_bytes_left = state->nal_packet_size_length; state->packet_size_left = 0; if (state->base.frame_size_left > 0) { break; } // finished a frame rc = mp4_encrypt_video_end_frame(state); if (rc != VOD_OK) { return rc; } // move to the next frame if (mp4_encrypt_move_to_next_frame(&state->base, &init_track)) { if (init_track) { rc = mp4_encrypt_video_init_track(state, state->base.cur_clip->first_track); if (rc != VOD_OK) { return rc; } } break; } // finished all frames rc = write_buffer_flush(&state->base.write_buffer, FALSE); if (rc != VOD_OK) { return rc; } mp4_encrypt_video_prepare_saiz_saio(state); rc = state->build_fragment_header(state, &fragment_header, &ignore); if (rc != VOD_OK) { return rc; } rc = state->base.segment_writer.write_head( state->base.segment_writer.context, fragment_header.data, fragment_header.len); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "mp4_encrypt_video_write_buffer: write_head failed %i", rc); return rc; } break; } } return VOD_OK; }
static vod_status_t mp4_encrypt_video_snpf_write_buffer(void* context, u_char* buffer, uint32_t size) { mp4_encrypt_video_state_t* state = (mp4_encrypt_video_state_t*)context; u_char* buffer_end = buffer + size; u_char* cur_pos = buffer; u_char* cur_end_pos; u_char* output; uint32_t write_size; bool_t init_track; vod_status_t rc; while (cur_pos < buffer_end) { switch (state->cur_state) { case STATE_CLEAR_BYTES: // start a new frame if needed if (state->base.frame_size_left <= 0) { rc = mp4_encrypt_start_frame(&state->base); if (rc != VOD_OK) { return rc; } if (state->base.frame_size_left <= 0) { state->cur_state = STATE_ENCRYPTED_BYTES; break; } } // copy the clear bytes write_size = (uint32_t)(buffer_end - cur_pos); write_size = vod_min(write_size, state->length_bytes_left + 1); rc = write_buffer_get_bytes(&state->base.write_buffer, write_size, NULL, &output); if (rc != VOD_OK) { return rc; } // copy the nalu length if (write_size >= state->length_bytes_left) { cur_end_pos = cur_pos + state->length_bytes_left; state->length_bytes_left = 0; } else { cur_end_pos = cur_pos + write_size; state->length_bytes_left -= write_size; } while (cur_pos < cur_end_pos) { state->packet_size_left = (state->packet_size_left << 8) | *cur_pos; *output++ = *cur_pos++; } if (cur_pos >= buffer_end) { break; } // copy the nalu type *output++ = *cur_pos++; if (state->base.frame_size_left < state->nal_packet_size_length + 1) { vod_log_error(VOD_LOG_ERR, state->base.request_context->log, 0, "mp4_encrypt_video_snpf_write_buffer: frame size %uD too small, nalu size %uD", state->base.frame_size_left, state->nal_packet_size_length); return VOD_BAD_DATA; } state->base.frame_size_left -= state->nal_packet_size_length; if (state->packet_size_left != state->base.frame_size_left && !state->single_nalu_warning_printed) { vod_log_error(VOD_LOG_WARN, state->base.request_context->log, 0, "mp4_encrypt_video_snpf_write_buffer: frame does not contain a single nalu, " "consider changing vod_min_single_nalu_per_frame_segment, " "packet size=%uD, frame size=%uD", state->packet_size_left, state->base.frame_size_left); state->single_nalu_warning_printed = TRUE; } state->base.frame_size_left--; state->cur_state++; // fall through case STATE_ENCRYPTED_BYTES: write_size = (uint32_t)(buffer_end - cur_pos); write_size = vod_min(write_size, state->base.frame_size_left); rc = mp4_encrypt_write_encrypted(&state->base, cur_pos, write_size); if (rc != VOD_OK) { return rc; } cur_pos += write_size; state->base.frame_size_left -= write_size; if (state->base.frame_size_left > 0) { break; } // finished a packet state->cur_state = STATE_CLEAR_BYTES; state->length_bytes_left = state->nal_packet_size_length; state->packet_size_left = 0; // move to the next frame if (mp4_encrypt_move_to_next_frame(&state->base, &init_track)) { if (init_track) { rc = mp4_encrypt_video_init_track(state, state->base.cur_clip->first_track); if (rc != VOD_OK) { return rc; } } break; } // finished all frames rc = write_buffer_flush(&state->base.write_buffer, FALSE); if (rc != VOD_OK) { return rc; } break; } } return VOD_OK; }