/*! * \brief Parse a mp4 file. * \param *video A pointer to a VideoFile_t structure. * \return retcode 1 if succeed, 0 otherwise. * * This parser is based on the 'ISO/IEC 13818-1' international standard, part 1: * 'Transmission multiplexing and synchronization'. */ int ps_fileParse(VideoFile_t *video) { TRACE_INFO(MPS, BLD_GREEN "ps_fileParse()\n" CLR_RESET); int retcode = SUCCESS; int sid = 0; // Init bitstream to parse container infos Bitstream_t *bitstr = init_bitstream(video, NULL); if (bitstr != NULL) { // Init bitstream_map to store container infos retcode = init_bitstream_map(&video->tracks_audio[0], 999999); retcode = init_bitstream_map(&video->tracks_video[0], 999999); if (retcode == SUCCESS) { video->tracks_audio[0]->stream_type = stream_AUDIO; video->tracks_audio[0]->stream_level = stream_level_PES; video->tracks_audio[0]->stream_codec = CODEC_MPEG_L3; video->tracks_audio[0]->sample_alignment = false; video->tracks_video[0]->stream_type = stream_VIDEO; video->tracks_video[0]->stream_level = stream_level_PES; video->tracks_video[0]->stream_codec = CODEC_MPEG12; video->tracks_video[0]->sample_alignment = false; // Read bitstream while (retcode == SUCCESS && (bitstream_get_absolute_byte_offset(bitstr) + 4) < video->file_size && next_bits(bitstr, 32) != PES_PROGRAM_END) { PackHeader_t pack_header; SystemHeader_t system_header; if (read_bits(bitstr, 32) == PES_PACK_HEADER) { // Parse pack & system header retcode = parse_pack_header(bitstr, &pack_header, &system_header); // Then loop on PES while (retcode == SUCCESS && (bitstream_get_absolute_byte_offset(bitstr) + 4) < video->file_size && next_bits(bitstr, 32) != PES_PACK_HEADER && next_bits(bitstr, 32) != PES_PROGRAM_END) { // Init PesPacket_t pes_packet; ProgramStreamMap_t pes_streammap; ProgramStreamDirectory_t pes_streamdirectory; // Parse start code pes_packet.packet_start_offset = bitstream_get_absolute_byte_offset(bitstr); pes_packet.packet_start_code = read_bits(bitstr, 24); pes_packet.stream_id = (uint8_t)read_bits(bitstr, 8); if (pes_packet.packet_start_code == PES_PACKETSTARTCODE) { switch (pes_packet.stream_id) { case SID_PROGRAM_STREAM_MAP: retcode = parse_program_stream_map(bitstr, &pes_streammap); break; case SID_PROGRAM_STREAM_DIRECTORY: retcode = parse_program_stream_directory(bitstr, &pes_streamdirectory); break; case SID_PADDING: retcode = parse_pes_padding(bitstr, &pes_packet); break; case SID_PRIVATE_STREAM_1: TRACE_2(MPS, BLD_GREEN "Private Stream 1 PES" CLR_RESET " @ %i\n", pes_packet.packet_start_offset); retcode = skip_pes(bitstr, &pes_packet); break; case SID_PRIVATE_STREAM_2: TRACE_2(MPS, BLD_GREEN "Private Stream 2 PES" CLR_RESET " @ %i\n", pes_packet.packet_start_offset); retcode = skip_pes(bitstr, &pes_packet); break; case SID_VIDEO: TRACE_INFO(MPS, BLD_GREEN "parse_pes_video()" CLR_RESET " @ %i\n", pes_packet.packet_start_offset); retcode = parse_pes(bitstr, &pes_packet); //print_pes(&pes_packet); // Set sample into the bitstream_map video->tracks_video[0]->sample_count++; sid = video->tracks_video[0]->sample_count; if (sid < 999999) { video->tracks_video[0]->sample_type[sid] = sample_VIDEO; video->tracks_video[0]->sample_size[sid] = pes_packet.PES_packet_length + 6; video->tracks_video[0]->sample_offset[sid] = pes_packet.packet_start_offset; video->tracks_video[0]->sample_pts[sid] = pes_packet.PTS; video->tracks_video[0]->sample_dts[sid] = pes_packet.DTS; } break; case SID_AUDIO: TRACE_INFO(MPS, BLD_GREEN "parse_pes_audio()" CLR_RESET " @ %i\n", pes_packet.packet_start_offset); retcode = parse_pes(bitstr, &pes_packet); //print_pes(&pes_packet); // Set sample into the bitstream_map video->tracks_audio[0]->sample_count++; sid = video->tracks_audio[0]->sample_count; if (sid < 999999) { video->tracks_audio[0]->sample_type[sid] = sample_AUDIO; video->tracks_audio[0]->sample_size[sid] = pes_packet.PES_packet_length + 6; video->tracks_audio[0]->sample_offset[sid] = pes_packet.packet_start_offset; video->tracks_audio[0]->sample_pts[sid] = pes_packet.PTS; video->tracks_audio[0]->sample_dts[sid] = pes_packet.DTS; } break; default: TRACE_WARNING(MPS, "Unknown PES type (0x%06X%02X) @ %i\n", pes_packet.packet_start_code, pes_packet.stream_id, pes_packet.packet_start_offset); retcode = skip_pes(bitstr, &pes_packet); break; } } else { TRACE_ERROR(MPS, "No valid packet_start_code at %i\n", pes_packet.packet_start_offset); retcode = FAILURE; } } } else { TRACE_ERROR(MPS, "No pack header\n"); retcode = FAILURE; } } } // Free bitstream free_bitstream(&bitstr); } return retcode; }
int ps_fileParse(MediaFile_t *media) { int retcode = SUCCESS; TRACE_INFO(MPS, BLD_GREEN "ps_fileParse()" CLR_RESET); // Init bitstream to parse container infos Bitstream_t *bitstr = init_bitstream(media, NULL); if (bitstr != NULL) { // Init an MpegPs structure MpegPs_t mpg; memset(&mpg, 0, sizeof(MpegPs_t)); // A convenient way to stop the parser mpg.run = true; // stuff const int64_t min_packet_size = 4; // Loop on PES packets while (mpg.run == true && retcode == SUCCESS && bitstream_get_absolute_byte_offset(bitstr) < (media->file_size - min_packet_size)) { // Init PackHeader_t pack_header; memset(&pack_header, 0, sizeof(PackHeader_t)); SystemHeader_t system_header; memset(&system_header, 0, sizeof(SystemHeader_t)); PesHeader_t pes_header; memset(&pes_header, 0, sizeof(PesHeader_t)); PesPacket_t pes_packet; memset(&pes_packet, 0, sizeof(PesPacket_t)); ProgramStreamMap_t pes_streammap; memset(&pes_streammap, 0, sizeof(ProgramStreamMap_t)); ProgramStreamDirectory_t pes_streamdirectory; memset(&pes_streamdirectory, 0, sizeof(ProgramStreamDirectory_t)); // Parse packet header parse_pes_header(bitstr, &pes_header); switch (pes_header.stream_id) { case SID_PACK_HEADER: retcode = parse_pack_header(bitstr, &pes_header, &pack_header); mpg.stat_packheader++; break; case SID_SYSTEM_HEADER: retcode = parse_system_header(bitstr, &pes_header, &system_header); mpg.stat_systemheader++; break; case SID_PROGRAM_STREAM_MAP: retcode = parse_program_stream_map(bitstr, &pes_header, &pes_streammap); mpg.stat_packet_psm++; break; case SID_PROGRAM_STREAM_DIRECTORY: retcode = parse_program_stream_directory(bitstr, &pes_header, &pes_streamdirectory); mpg.stat_packet_psd++; break; case SID_PRIVATE_STREAM_2: TRACE_2(MPS, BLD_GREEN "Private Stream 2 PES" CLR_RESET " @ %lli", pes_header.offset_start); mpg.stat_packet_private++; break; case SID_PADDING: retcode = parse_pes_padding(bitstr, &pes_header, &pes_packet); mpg.stat_packet_other++; break; case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7: case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7: case 0xD8: case 0xD9: case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE: case 0xDF: case SID_PRIVATE_STREAM_1: case SID_AUDIO: { TRACE_INFO(MPS, BLD_GREEN "parse_pes_audio()" CLR_RESET " @ %lli", pes_header.offset_start); // Find a trackid unsigned track_id = pes_header.stream_id - 0xC0; if (pes_header.stream_id == SID_PRIVATE_STREAM_1) track_id = 0; // If a private stream is in the stream, the 'regular' trackid order is shifted //while (media->tracks_audio[track_id] == NULL) // track_id--; // Init bitstream_map (as needed) to store samples if (media->tracks_audio[track_id] == NULL) { retcode = init_bitstream_map(&media->tracks_audio[track_id], 0, 999999); media->tracks_audio_count++; media->tracks_audio[track_id]->stream_type = stream_AUDIO; } retcode = parse_pes(bitstr, &pes_header, &pes_packet); parse_pes_a(bitstr, &pes_header, &pes_packet, media->tracks_audio[track_id]); mpg.stat_packet_audio++; // Set sample into the bitstream_map unsigned sample_id = media->tracks_audio[track_id]->sample_count++; if (sample_id < 999999) { media->tracks_audio[track_id]->sample_type[sample_id] = sample_AUDIO; media->tracks_audio[track_id]->sample_size[sample_id] = pes_header.payload_length - pes_packet.PES_header_data_length; media->tracks_audio[track_id]->sample_offset[sample_id] = pes_header.offset_start + 6 + pes_packet.PES_header_data_length; media->tracks_audio[track_id]->sample_pts[sample_id] = pes_packet.PTS; media->tracks_audio[track_id]->sample_dts[sample_id] = pes_packet.DTS; } } break; case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8: case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xED: case 0xEE: case 0xEF: case SID_VIDEO: { TRACE_INFO(MPS, BLD_GREEN "parse_pes_video()" CLR_RESET " @ %lli", pes_header.offset_start + 6); // Init bitstream_map (as needed) to store samples unsigned track_id = pes_header.stream_id - 0xE0; if (media->tracks_video[track_id] == NULL) { retcode = init_bitstream_map(&media->tracks_video[track_id], 0, 999999); media->tracks_video_count++; media->tracks_video[track_id]->stream_type = stream_VIDEO; } retcode = parse_pes(bitstr, &pes_header, &pes_packet); parse_pes_v(bitstr, &pes_header, &pes_packet, media->tracks_video[track_id]); mpg.stat_packet_video++; // Set sample into the bitstream_map unsigned sample_id = media->tracks_video[track_id]->sample_count++; if (sample_id < 999999) { media->tracks_video[track_id]->sample_type[sample_id] = sample_VIDEO; media->tracks_video[track_id]->sample_size[sample_id] = pes_header.payload_length - pes_packet.PES_header_data_length; media->tracks_video[track_id]->sample_offset[sample_id] = pes_header.offset_start + 6 + pes_packet.PES_header_data_length; media->tracks_video[track_id]->sample_pts[sample_id] = pes_packet.PTS; media->tracks_video[track_id]->sample_dts[sample_id] = pes_packet.DTS; } } break; case SID_PROGRAM_END: mpg.stat_packet_other++; mpg.run = false; break; default: TRACE_WARNING(MPS, "Unknown PES packet type (0x%02X) @ %lli", pes_header.stream_id, pes_header.offset_start); mpg.stat_packet_other++; break; } retcode = jumpy_pes(bitstr, &pes_header); } // Free bitstream free_bitstream(&bitstr); // Recap TRACE_INFO(MPS, "MPEG PS (version %u) stats", mpg.mpeg_version); TRACE_INFO(MPS, "- Pack Headers: %u", mpg.stat_packheader); TRACE_INFO(MPS, "- System Headers: %u", mpg.stat_systemheader); TRACE_INFO(MPS, "- PSM packets: %u", mpg.stat_packet_psm); TRACE_INFO(MPS, "- PSD packets: %u", mpg.stat_packet_psd); TRACE_INFO(MPS, "- Private packets: %u", mpg.stat_packet_private); TRACE_INFO(MPS, "- Audio packets: %u", mpg.stat_packet_audio); TRACE_INFO(MPS, "- Video packets: %u", mpg.stat_packet_video); TRACE_INFO(MPS, "- Unknown packets: %u", mpg.stat_packet_other); } else { retcode = FAILURE; } return retcode; }