vod_status_t edash_packager_build_init_mp4( request_context_t* request_context, media_set_t* media_set, bool_t has_clear_lead, bool_t size_only, vod_str_t* result) { media_track_t* first_track = media_set->sequences[0].filtered_clips[0].first_track; mp4_encrypt_info_t* drm_info = (mp4_encrypt_info_t*)media_set->sequences[0].drm_info; stsd_writer_context_t stsd_writer_context; atom_writer_t pssh_atom_writer; atom_writer_t stsd_atom_writer; mp4_encrypt_system_info_t* cur_info; vod_status_t rc; rc = edash_packager_init_stsd_writer_context( request_context, first_track->media_info.media_type, &first_track->raw_atoms[RTA_STSD], has_clear_lead, drm_info->key_id, &stsd_writer_context); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "edash_packager_build_init_mp4: edash_packager_init_stsd_writer_context failed %i", rc); return rc; } // build the pssh writer pssh_atom_writer.atom_size = 0; for (cur_info = drm_info->pssh_array.first; cur_info < drm_info->pssh_array.last; cur_info++) { pssh_atom_writer.atom_size += ATOM_HEADER_SIZE + sizeof(pssh_atom_t) + cur_info->data.len; } pssh_atom_writer.write = edash_packager_write_pssh; pssh_atom_writer.context = &drm_info->pssh_array; // build the stsd writer stsd_atom_writer.atom_size = stsd_writer_context.stsd_atom_size; stsd_atom_writer.write = edash_packager_write_stsd; stsd_atom_writer.context = &stsd_writer_context; rc = dash_packager_build_init_mp4( request_context, media_set, size_only, &pssh_atom_writer, &stsd_atom_writer, result); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "edash_packager_build_init_mp4: dash_packager_build_init_mp4 failed %i", rc); return rc; } return VOD_OK; }
static vod_status_t mp4_encrypt_video_start_frame(mp4_encrypt_video_state_t* state) { vod_status_t rc; // add an auxiliary data entry rc = vod_dynamic_buf_reserve(&state->auxiliary_data, sizeof(cenc_sample_auxiliary_data_t)); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "mp4_encrypt_video_start_frame: vod_dynamic_buf_reserve failed %i", rc); return rc; } state->auxiliary_data.pos = vod_copy(state->auxiliary_data.pos, state->base.iv, sizeof(state->base.iv)); state->auxiliary_data.pos += sizeof(uint16_t); // write the subsample count on frame end state->subsample_count = 0; // call the base start frame rc = mp4_encrypt_start_frame(&state->base); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "mp4_encrypt_video_start_frame: mp4_encrypt_start_frame failed %i", rc); return rc; } return VOD_OK; }
static vod_status_t edash_packager_video_write_fragment_header(mp4_encrypt_video_state_t* state) { dash_fragment_header_extensions_t header_extensions; atom_writer_t auxiliary_data_writer; vod_str_t fragment_header; vod_status_t rc; size_t total_fragment_size; bool_t reuse_buffer; // get the auxiliary data writer auxiliary_data_writer.atom_size = state->auxiliary_data.pos - state->auxiliary_data.start; auxiliary_data_writer.write = edash_packager_video_write_auxiliary_data; auxiliary_data_writer.context = state; header_extensions.extra_traf_atoms_size = state->base.saiz_atom_size + state->base.saio_atom_size; header_extensions.write_extra_traf_atoms_callback = (write_extra_traf_atoms_callback_t)mp4_encrypt_video_write_saiz_saio; header_extensions.write_extra_traf_atoms_context = state; header_extensions.mdat_prefix_writer = &auxiliary_data_writer; // build the fragment header rc = dash_packager_build_fragment_header( state->base.request_context, state->base.media_set, state->base.segment_index, 0, &header_extensions, FALSE, &fragment_header, &total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "edash_packager_video_write_fragment_header: dash_packager_build_fragment_header failed %i", rc); return rc; } rc = state->base.segment_writer.write_head( state->base.segment_writer.context, fragment_header.data, fragment_header.len, &reuse_buffer); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "edash_packager_video_write_fragment_header: write_head failed %i", rc); return rc; } return VOD_OK; }
vod_status_t write_buffer_queue_flush(write_buffer_queue_t* queue) { buffer_header_t* cur_buffer; vod_status_t rc; while (!is_list_empty(&queue->buffers)) { cur_buffer = (buffer_header_t*)queue->buffers.next; remove_entry_list(&cur_buffer->link); if (cur_buffer->cur_pos <= cur_buffer->start_pos) { continue; } rc = queue->write_callback(queue->write_context, cur_buffer->start_pos, cur_buffer->cur_pos - cur_buffer->start_pos); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, queue->request_context->log, 0, "write_buffer_queue_flush: write_callback failed %i", rc); return rc; } // no reason to reuse the buffer here } return VOD_OK; }
vod_status_t mp4_encrypt_audio_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) { mp4_encrypt_state_t* state; vod_status_t 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, "mp4_encrypt_audio_get_fragment_writer: vod_alloc failed"); return VOD_ALLOC_FAILED; } rc = mp4_encrypt_init_state(state, request_context, media_set, segment_index, segment_writer, iv); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mp4_encrypt_audio_get_fragment_writer: mp4_encrypt_init_state failed %i", rc); return rc; } result->write_tail = mp4_encrypt_audio_write_buffer; result->write_head = NULL; result->context = state; return VOD_OK; }
static vod_status_t mss_playready_audio_build_fragment_header( mp4_encrypt_state_t* state, bool_t size_only, vod_str_t* fragment_header, size_t* total_fragment_size) { vod_status_t rc; mss_playready_audio_extra_traf_atoms_context writer_context; writer_context.uuid_piff_atom_size = ATOM_HEADER_SIZE + sizeof(uuid_piff_atom_t) + mp4_encrypt_audio_get_auxiliary_data_size(state); writer_context.state = state; rc = mss_packager_build_fragment_header( state->request_context, state->media_set, state->segment_index, writer_context.uuid_piff_atom_size + state->saiz_atom_size + state->saio_atom_size, mss_playready_audio_write_extra_traf_atoms, &writer_context, size_only, fragment_header, total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mss_playready_audio_build_fragment_header: mss_packager_build_fragment_header failed %i", rc); return rc; } return VOD_OK; }
static vod_status_t mss_playready_video_write_fragment_header(mp4_encrypt_video_state_t* state) { mss_playready_video_extra_traf_atoms_context writer_context; vod_str_t fragment_header; size_t total_fragment_size; bool_t reuse_buffer; vod_status_t rc; writer_context.uuid_piff_atom_size = ATOM_HEADER_SIZE + sizeof(uuid_piff_atom_t) + state->auxiliary_data.pos - state->auxiliary_data.start; writer_context.state = state; rc = mss_packager_build_fragment_header( state->base.request_context, state->base.sequence, state->base.segment_index, writer_context.uuid_piff_atom_size + state->base.saiz_atom_size + state->base.saio_atom_size, mss_playready_video_write_extra_traf_atoms, &writer_context, FALSE, &fragment_header, &total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "mss_playready_video_write_fragment_header: mss_packager_build_fragment_header failed %i", rc); return rc; } rc = state->base.segment_writer.write_head( state->base.segment_writer.context, fragment_header.data, fragment_header.len, &reuse_buffer); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "mss_playready_video_write_fragment_header: write_head failed %i", rc); return rc; } return VOD_OK; }
vod_status_t edash_packager_build_mpd( request_context_t* request_context, dash_manifest_config_t* conf, vod_str_t* base_url, segmenter_conf_t* segmenter_conf, media_set_t* media_set, vod_str_t* result) { media_sequence_t* cur_sequence; mp4_encrypt_info_t* drm_info; size_t representation_tags_size; size_t cur_drm_tags_size; vod_status_t rc; representation_tags_size = 0; for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++) { drm_info = (mp4_encrypt_info_t*)cur_sequence->drm_info; cur_drm_tags_size = sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC) - 1 + (sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PREFIX) - 1 + VOD_GUID_LENGTH + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_SUFFIX) - 1) * drm_info->pssh_array.count; representation_tags_size += cur_drm_tags_size * cur_sequence->total_track_count; } rc = dash_packager_build_mpd( request_context, conf, base_url, segmenter_conf, media_set, representation_tags_size, edash_packager_write_content_protection, NULL, 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; }
static vod_status_t mp4_encrypt_video_add_subsample(mp4_encrypt_video_state_t* state, uint16_t bytes_of_clear_data, uint32_t bytes_of_encrypted_data) { vod_status_t rc; rc = vod_dynamic_buf_reserve(&state->auxiliary_data, sizeof(cenc_sample_auxiliary_data_subsample_t)); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0, "mp4_encrypt_video_add_subsample: vod_dynamic_buf_reserve failed %i", rc); return rc; } write_be16(state->auxiliary_data.pos, bytes_of_clear_data); write_be32(state->auxiliary_data.pos, bytes_of_encrypted_data); state->subsample_count++; return VOD_OK; }
vod_status_t write_buffer_queue_send(write_buffer_queue_t* queue, off_t max_offset) { buffer_header_t* cur_buffer; vod_status_t rc; while (!is_list_empty(&queue->buffers)) { cur_buffer = (buffer_header_t*)queue->buffers.next; if (cur_buffer->cur_pos <= cur_buffer->start_pos) { break; } if (cur_buffer->end_offset > max_offset) { break; } remove_entry_list(&cur_buffer->link); if (cur_buffer == queue->cur_write_buffer) { queue->cur_write_buffer = NULL; } rc = queue->write_callback(queue->write_context, cur_buffer->start_pos, cur_buffer->cur_pos - cur_buffer->start_pos); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, queue->request_context->log, 0, "write_buffer_queue_send: write_callback failed %i", rc); return rc; } if (!queue->reuse_buffers) { cur_buffer->start_pos = NULL; } cur_buffer->cur_pos = cur_buffer->start_pos; insert_tail_list(&queue->buffers, &cur_buffer->link); } return VOD_OK; }
static vod_status_t hds_muxer_end_frame(hds_muxer_state_t* state) { uint32_t packet_size = state->frame_header_size + state->cur_frame->size; vod_status_t rc; u_char* p; // write the frame size rc = write_buffer_get_bytes(&state->write_buffer_state, sizeof(uint32_t), NULL, &p); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_end_frame: write_buffer_get_bytes failed %i", rc); return rc; } write_be32(p, packet_size); return VOD_OK; }
vod_status_t write_buffer_flush(write_buffer_state_t* state, bool_t reallocate) { vod_status_t rc; if (state->cur_pos > state->start_pos) { rc = state->write_callback(state->write_context, state->start_pos, state->cur_pos - state->start_pos); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "write_buffer_flush: write_callback failed %i", rc); return rc; } if (state->reuse_buffers) { state->cur_pos = state->start_pos; return VOD_OK; } } if (reallocate) { state->start_pos = vod_alloc(state->request_context->pool, WRITE_BUFFER_SIZE); if (state->start_pos == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "write_buffer_flush: vod_alloc failed"); return VOD_ALLOC_FAILED; } state->end_pos = state->start_pos + WRITE_BUFFER_SIZE; state->cur_pos = state->start_pos; } else { state->start_pos = state->end_pos = state->cur_pos = NULL; } return VOD_OK; }
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; }
static vod_status_t edash_packager_audio_build_fragment_header( mp4_encrypt_state_t* state, bool_t size_only, vod_str_t* fragment_header, size_t* total_fragment_size) { dash_fragment_header_extensions_t header_extensions; atom_writer_t auxiliary_data_writer; vod_status_t rc; auxiliary_data_writer.atom_size = MP4_ENCRYPT_IV_SIZE * state->sequence->total_frame_count; auxiliary_data_writer.write = (atom_writer_func_t)mp4_encrypt_audio_write_auxiliary_data; auxiliary_data_writer.context = state; header_extensions.extra_traf_atoms_size = state->saiz_atom_size + state->saio_atom_size; header_extensions.write_extra_traf_atoms_callback = (write_extra_traf_atoms_callback_t)mp4_encrypt_audio_write_saiz_saio; header_extensions.write_extra_traf_atoms_context = state; header_extensions.mdat_prefix_writer = &auxiliary_data_writer; rc = dash_packager_build_fragment_header( state->request_context, state->media_set, state->segment_index, 0, &header_extensions, size_only, fragment_header, total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "edash_packager_audio_build_fragment_header: dash_packager_build_fragment_header failed %i", rc); return rc; } return VOD_OK; }
static vod_status_t edash_packager_audio_build_fragment_header( mp4_encrypt_state_t* state, bool_t size_only, vod_str_t* fragment_header, size_t* total_fragment_size) { dash_fragment_header_extensions_t header_extensions; vod_status_t rc; // get the header extensions header_extensions.extra_traf_atoms_size = state->saiz_atom_size + state->saio_atom_size + ATOM_HEADER_SIZE + sizeof(senc_atom_t) + MP4_AES_CTR_IV_SIZE * state->sequence->total_frame_count; header_extensions.write_extra_traf_atoms_callback = edash_packager_audio_write_encryption_atoms; header_extensions.write_extra_traf_atoms_context = state; // build the fragment header rc = dash_packager_build_fragment_header( state->request_context, state->media_set, state->segment_index, 0, &header_extensions, size_only, fragment_header, total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "edash_packager_audio_build_fragment_header: dash_packager_build_fragment_header failed %i", rc); return rc; } return VOD_OK; }
static vod_status_t mp4_metadata_reader_read( void* ctx, uint64_t offset, vod_str_t* buffer, media_format_read_metadata_result_t* result) { mp4_read_metadata_state_t* state = ctx; const u_char* ftyp_ptr; size_t ftyp_size; u_char* uncomp_buffer; off_t moov_offset; size_t moov_size; vod_status_t rc; if (state->state == STATE_READ_MOOV_DATA) { // make sure we got the whole moov atom moov_size = state->parts[MP4_METADATA_PART_MOOV].len; if (buffer->len < moov_size) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "mp4_metadata_reader_read: buffer size %uz is smaller than moov size %uz", buffer->len, moov_size); return VOD_BAD_DATA; } moov_offset = 0; goto done; } if (state->parts[MP4_METADATA_PART_FTYP].len == 0) { // try to find the ftyp atom rc = mp4_parser_get_ftyp_atom_into( state->request_context, buffer->data, buffer->len, &ftyp_ptr, &ftyp_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mp4_metadata_reader_read: mp4_parser_get_ftyp_atom_into failed %i", rc); return rc; } if (ftyp_size > 0 && ftyp_ptr + ftyp_size <= buffer->len + buffer->data) { // got a full ftyp atom state->parts[MP4_METADATA_PART_FTYP].data = vod_alloc(state->request_context->pool, ftyp_size); if (state->parts[MP4_METADATA_PART_FTYP].data == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mp4_metadata_reader_read: vod_alloc failed"); return VOD_ALLOC_FAILED; } vod_memcpy(state->parts[MP4_METADATA_PART_FTYP].data, ftyp_ptr, ftyp_size); state->parts[MP4_METADATA_PART_FTYP].len = ftyp_size; } else { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mp4_metadata_reader_read: ftyp atom not found"); } } // get moov atom offset and size rc = mp4_parser_get_moov_atom_info( state->request_context, buffer->data, buffer->len, &moov_offset, &moov_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mp4_metadata_reader_read: mp4_parser_get_moov_atom_info failed %i", rc); return rc; } if (moov_size <= 0) { // moov not found if ((size_t)moov_offset < buffer->len) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "mp4_metadata_reader_read: moov start offset %O is smaller than the buffer size %uz", moov_offset, buffer->len); return VOD_BAD_DATA; } if (state->moov_start_reads <= 0) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "mp4_metadata_reader_read: exhausted all moov read attempts"); return VOD_BAD_DATA; } state->moov_start_reads--; // perform another read attempt result->read_req.read_offset = offset + moov_offset; result->read_req.read_size = 0; result->read_req.realloc_buffer = FALSE; return VOD_AGAIN; } // save the moov size state->parts[MP4_METADATA_PART_MOOV].len = moov_size; // check whether we already have the whole atom if (moov_offset + moov_size <= buffer->len) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mp4_metadata_reader_read: already read the full moov atom"); goto done; } // validate the moov size if (moov_size > state->max_moov_size) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "mp4_metadata_reader_read: moov size %uD exceeds the max %uz", moov_size, state->max_moov_size); return VOD_BAD_DATA; } state->state = STATE_READ_MOOV_DATA; result->read_req.read_offset = offset + moov_offset; result->read_req.read_size = moov_size; result->read_req.realloc_buffer = FALSE; return VOD_AGAIN; done: state->parts[MP4_METADATA_PART_MOOV].data = buffer->data + moov_offset; // uncompress the moov atom if needed rc = mp4_parser_uncompress_moov( state->request_context, state->parts[MP4_METADATA_PART_MOOV].data, moov_size, state->max_moov_size, &uncomp_buffer, &moov_offset, &moov_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "mp4_metadata_reader_read: mp4_parser_uncompress_moov failed %i", rc); return rc; } if (uncomp_buffer != NULL) { state->parts[MP4_METADATA_PART_MOOV].data = uncomp_buffer + moov_offset; state->parts[MP4_METADATA_PART_MOOV].len = moov_size; } result->parts = state->parts; result->part_count = MP4_METADATA_PART_COUNT; 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; }
vod_status_t mp4_encrypt_video_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, mp4_encrypt_video_build_fragment_header_t build_fragment_header, segment_writer_t* segment_writer, const u_char* iv, vod_str_t* fragment_header, size_t* total_fragment_size) { media_sequence_t* sequence = &media_set->sequences[0]; mp4_encrypt_video_state_t* state; vod_status_t rc; uint32_t initial_size; bool_t ignore; // 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, "mp4_encrypt_video_get_fragment_writer: vod_alloc failed (1)"); return VOD_ALLOC_FAILED; } rc = mp4_encrypt_init_state(&state->base, request_context, media_set, segment_index, segment_writer, iv); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mp4_encrypt_video_get_fragment_writer: mp4_encrypt_init_state failed %i", rc); return rc; } if (!mp4_encrypt_move_to_next_frame(&state->base, &ignore)) { // an empty segment - write won't be called so we need to write the header here state->auxiliary_data.start = NULL; state->auxiliary_data.pos = NULL; state->auxiliary_sample_sizes = NULL; state->auxiliary_sample_sizes_pos = NULL; state->default_auxiliary_sample_size = 0; state->saiz_sample_count = 0; rc = build_fragment_header(state, fragment_header, total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mp4_encrypt_video_get_fragment_writer: write_fragment_header failed %i", rc); return rc; } return VOD_OK; } if (single_nalu_per_frame) { // each frame is a single nal unit, can generate the auxiliary data and write the header now state->build_fragment_header = NULL; state->single_nalu_warning_printed = FALSE; rc = mp4_encrypt_video_snpf_build_auxiliary_data(state); if (rc != VOD_OK) { return rc; } rc = build_fragment_header(state, fragment_header, total_fragment_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mp4_encrypt_video_get_fragment_writer: write_fragment_header failed %i", rc); return rc; } result->write_tail = mp4_encrypt_video_snpf_write_buffer; } else { state->build_fragment_header = build_fragment_header; // for progressive AVC a frame usually contains a single nalu, except the first frame which may contain codec copyright info initial_size = (sizeof(cenc_sample_auxiliary_data_t) + sizeof(cenc_sample_auxiliary_data_subsample_t)) * sequence->total_frame_count + sizeof(cenc_sample_auxiliary_data_subsample_t); rc = vod_dynamic_buf_init(&state->auxiliary_data, request_context, initial_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mp4_encrypt_video_get_fragment_writer: vod_dynamic_buf_init failed %i", rc); return rc; } state->auxiliary_sample_sizes = vod_alloc(request_context->pool, sequence->total_frame_count); if (state->auxiliary_sample_sizes == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "mp4_encrypt_video_get_fragment_writer: vod_alloc failed (2)"); return VOD_ALLOC_FAILED; } state->auxiliary_sample_sizes_pos = state->auxiliary_sample_sizes; result->write_tail = mp4_encrypt_video_write_buffer; } // init writing for the first track rc = mp4_encrypt_video_init_track(state, state->base.cur_clip->first_track); if (rc != VOD_OK) { return rc; } result->write_head = NULL; result->context = state; return VOD_OK; }
vod_status_t hds_muxer_process_frames(hds_muxer_state_t* state) { u_char* read_buffer; uint32_t read_size; vod_status_t rc; bool_t wrote_data = FALSE; bool_t frame_done; for (;;) { // read some data from the frame rc = state->frames_source->read(state->frames_source_context, &read_buffer, &read_size, &frame_done); if (rc != VOD_OK) { if (rc != VOD_AGAIN) { return rc; } if (!wrote_data && !state->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; } state->first_time = FALSE; return VOD_AGAIN; } wrote_data = TRUE; // write the frame rc = write_buffer_write(&state->write_buffer_state, read_buffer, read_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; } // if not done, ask the cache for more data if (!frame_done) { continue; } // end the frame and start a new one 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; } rc = hds_muxer_start_frame(state); if (rc != VOD_OK) { if (rc == VOD_NOT_FOUND) { break; // done } 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; } } // 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; }
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; }
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; }
vod_status_t hds_muxer_init_fragment( request_context_t* request_context, hds_fragment_config_t* conf, uint32_t segment_index, media_sequence_t* sequence, write_callback_t write_callback, void* write_context, bool_t size_only, vod_str_t* header, size_t* total_fragment_size, hds_muxer_state_t** processor_state) { media_clip_filtered_t* cur_clip; media_track_t* cur_track; hds_muxer_stream_state_t* cur_stream; input_frame_t* cur_frame; input_frame_t* frames_end; hds_muxer_state_t* state; vod_status_t rc; uint32_t track_id = 1; uint32_t* output_offset; uint32_t frame_metadata_size; uint32_t video_key_frame_count; uint32_t codec_config_size; size_t afra_atom_size; size_t moof_atom_size; size_t traf_atom_size; size_t mdat_atom_size; size_t result_size; u_char* p; // initialize the muxer state rc = hds_muxer_init_state( request_context, sequence, write_callback, write_context, &state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: hds_muxer_init_state failed %i", rc); return rc; } // get moof atom size mdat_atom_size = ATOM_HEADER_SIZE; for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { codec_config_size = 0; video_key_frame_count = 0; for (cur_track = cur_clip->first_track; cur_track < cur_clip->last_track; cur_track++) { frame_metadata_size = tag_size_by_media_type[cur_track->media_info.media_type] + sizeof(uint32_t); codec_config_size += frame_metadata_size + cur_track->media_info.extra_data_size; mdat_atom_size += cur_track->total_frames_size + cur_track->frame_count * frame_metadata_size; if (cur_track->media_info.media_type == MEDIA_TYPE_VIDEO) { video_key_frame_count += cur_track->key_frame_count; } } mdat_atom_size += video_key_frame_count * codec_config_size; } // get the fragment header size if (conf->generate_moof_atom) { afra_atom_size = ATOM_HEADER_SIZE + sizeof(afra_atom_t) + sizeof(afra_entry_t) * sequence->video_key_frame_count; moof_atom_size = ATOM_HEADER_SIZE + ATOM_HEADER_SIZE + sizeof(mfhd_atom_t); for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++) { moof_atom_size += hds_get_traf_atom_size(cur_stream); } } else { afra_atom_size = 0; moof_atom_size = 0; } result_size = afra_atom_size + moof_atom_size + ATOM_HEADER_SIZE; // mdat // audio only - output the codec config up front, video - output the codec config before every key frame if (sequence->video_key_frame_count == 0) { result_size += state->codec_config_size; mdat_atom_size += state->codec_config_size; } *total_fragment_size = afra_atom_size + moof_atom_size + mdat_atom_size; // head request optimization if (size_only) { return VOD_OK; } // allocate the response header->data = vod_alloc(request_context->pool, result_size); if (header->data == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: vod_alloc failed"); return VOD_ALLOC_FAILED; } p = header->data; if (conf->generate_moof_atom) { // afra p = hds_write_afra_atom_header(p, afra_atom_size, sequence->video_key_frame_count); rc = hds_calculate_output_offsets_and_write_afra_entries(state, ATOM_HEADER_SIZE, afra_atom_size + moof_atom_size, &p); if (rc != VOD_OK) { return rc; } // moof write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f'); // moof.mfhd p = mp4_builder_write_mfhd_atom(p, segment_index); for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++) { // moof.traf traf_atom_size = hds_get_traf_atom_size(cur_stream); write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f'); // moof.traf.tfhd p = hds_write_tfhd_atom(p, track_id, ATOM_HEADER_SIZE + sizeof(afra_atom_t) + moof_atom_size); // moof.traf.trun switch (cur_stream->media_type) { case MEDIA_TYPE_VIDEO: for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { cur_track = cur_clip->first_track + cur_stream->index; frames_end = cur_track->last_frame; for (cur_frame = cur_track->first_frame, output_offset = cur_stream->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_video_frame_trun_atom(p, cur_frame, *output_offset); } } break; case MEDIA_TYPE_AUDIO: for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { cur_track = cur_clip->first_track + cur_stream->index; frames_end = cur_track->last_frame; for (cur_frame = cur_track->first_frame, output_offset = cur_stream->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_audio_frame_trun_atom(p, cur_frame, *output_offset); } } break; } } } else { // calculate the output offsets rc = hds_calculate_output_offsets_and_write_afra_entries(state, 0, 0, NULL); if (rc != VOD_OK) { return rc; } } // mdat write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't'); if (sequence->video_key_frame_count == 0) { p = hds_muxer_write_codec_config( p, state, state->first_stream->next_frame_dts + state->first_stream->clip_start_time); } header->len = p - header->data; if (header->len != result_size) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "hds_muxer_init_fragment: result length %uz exceeded allocated length %uz", header->len, result_size); return VOD_UNEXPECTED; } rc = hds_muxer_start_frame(state); if (rc != VOD_OK) { if (rc == VOD_NOT_FOUND) { *processor_state = NULL; // no frames, nothing to do return VOD_OK; } vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: hds_muxer_start_frame failed %i", rc); return rc; } *processor_state = state; return VOD_OK; }
vod_status_t hds_muxer_init_fragment( request_context_t* request_context, uint32_t segment_index, mpeg_metadata_t *mpeg_metadata, read_cache_state_t* read_cache_state, write_callback_t write_callback, void* write_context, bool_t size_only, vod_str_t* header, size_t* total_fragment_size, hds_muxer_state_t** processor_state) { mpeg_stream_metadata_t* cur_stream; hds_muxer_stream_state_t* stream_state; input_frame_t* cur_frame; input_frame_t* frames_end; hds_muxer_state_t* state; vod_status_t rc; uint32_t track_id = 1; uint32_t* output_offset; uint32_t frame_metadata_size; size_t afra_atom_size; size_t moof_atom_size; size_t traf_atom_size; size_t mdat_atom_size; size_t result_size; u_char* p; // initialize the muxer state rc = hds_muxer_init_state(request_context, mpeg_metadata, read_cache_state, write_callback, write_context, &state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: hds_muxer_init_state failed %i", rc); return rc; } // get moof atom size moof_atom_size = ATOM_HEADER_SIZE + ATOM_HEADER_SIZE + sizeof(mfhd_atom_t); mdat_atom_size = ATOM_HEADER_SIZE; state->codec_config_size = 0; for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++) { moof_atom_size += hds_get_traf_atom_size(cur_stream); frame_metadata_size = tag_size_by_media_type[cur_stream->media_info.media_type] + sizeof(uint32_t); state->codec_config_size += frame_metadata_size + cur_stream->media_info.extra_data_size; mdat_atom_size += cur_stream->total_frames_size + cur_stream->frame_count * frame_metadata_size; } mdat_atom_size += mpeg_metadata->video_key_frame_count * state->codec_config_size; // get the fragment header size afra_atom_size = ATOM_HEADER_SIZE + sizeof(afra_atom_t) + sizeof(afra_entry_t) * mpeg_metadata->video_key_frame_count; result_size = afra_atom_size + moof_atom_size + ATOM_HEADER_SIZE; // mdat // audio only - output the codec config up front, video - output the codec config before every key frame if (mpeg_metadata->video_key_frame_count == 0) { result_size += state->codec_config_size; mdat_atom_size += state->codec_config_size; } *total_fragment_size = afra_atom_size + moof_atom_size + mdat_atom_size; // head request optimization if (size_only) { return VOD_OK; } // allocate the response header->data = vod_alloc(request_context->pool, result_size); if (header->data == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: vod_alloc failed"); return VOD_ALLOC_FAILED; } // afra p = hds_write_afra_atom_header(header->data, afra_atom_size, mpeg_metadata->video_key_frame_count); p = hds_calculate_output_offsets_and_write_afra_entries(state, ATOM_HEADER_SIZE, afra_atom_size + moof_atom_size, p); // moof write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f'); // moof.mfhd p = mp4_builder_write_mfhd_atom(p, segment_index); for (stream_state = state->first_stream; stream_state < state->last_stream; stream_state++) { cur_stream = stream_state->metadata; // moof.traf traf_atom_size = hds_get_traf_atom_size(cur_stream); write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f'); // moof.traf.tfhd p = hds_write_tfhd_atom(p, track_id, ATOM_HEADER_SIZE + sizeof(afra_atom_t) + moof_atom_size); // moof.traf.trun frames_end = cur_stream->frames + cur_stream->frame_count; switch (cur_stream->media_info.media_type) { case MEDIA_TYPE_VIDEO: for (cur_frame = cur_stream->frames, output_offset = stream_state->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_video_frame_trun_atom(p, cur_frame, *output_offset); } break; case MEDIA_TYPE_AUDIO: for (cur_frame = cur_stream->frames, output_offset = stream_state->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_audio_frame_trun_atom(p, cur_frame, *output_offset); } break; } } // mdat write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't'); if (mpeg_metadata->video_key_frame_count == 0) { p = hds_muxer_write_codec_config(p, state, state->first_stream->next_frame_dts); } header->len = p - header->data; if (header->len != result_size) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "hds_muxer_init_fragment: result length %uz exceeded allocated length %uz", header->len, result_size); return VOD_UNEXPECTED; } *processor_state = state; return VOD_OK; }
vod_status_t hds_muxer_process_frames(hds_muxer_state_t* state, uint64_t* required_offset) { 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->cache_slot_id, 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; } *required_offset = offset; return VOD_AGAIN; } wrote_data = TRUE; // write the frame write_size = 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 edash_packager_build_mpd( request_context_t* request_context, dash_manifest_config_t* conf, vod_str_t* base_url, media_set_t* media_set, vod_str_t* result) { media_sequence_t* cur_sequence; drm_system_info_t* cur_info; drm_info_t* drm_info; u_char* pssh_temp_buffer = NULL; 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_PART1) - 1 + VOD_GUID_LENGTH + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART2) - 1 + vod_base64_encoded_length(cur_info->data.len) + sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART3) - 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; } if (max_pssh_size > 0) { pssh_temp_buffer = vod_alloc(request_context->pool, max_pssh_size); if (pssh_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; } } rc = dash_packager_build_mpd( request_context, conf, base_url, media_set, representation_tags_size, edash_packager_write_content_protection, pssh_temp_buffer, 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; }
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; }
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; }