diff options
Diffstat (limited to 'demux')
42 files changed, 19204 insertions, 0 deletions
diff --git a/demux/asf.h b/demux/asf.h new file mode 100644 index 0000000000..2886a4d752 --- /dev/null +++ b/demux/asf.h @@ -0,0 +1,251 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_ASF_H +#define MPLAYER_ASF_H + +#include <sys/types.h> +#include <inttypes.h> +#include "libavutil/common.h" +#include "mpbswap.h" + +/////////////////////// +// ASF Object Header +/////////////////////// +typedef struct __attribute__((packed)) { + uint8_t guid[16]; + uint64_t size; +} ASF_obj_header_t; + +//////////////// +// ASF Header +//////////////// +typedef struct __attribute__((packed)) { + ASF_obj_header_t objh; + uint32_t cno; // number of subchunks + uint8_t v1; // unknown (0x01) + uint8_t v2; // unknown (0x02) +} ASF_header_t; + +///////////////////// +// ASF File Header +///////////////////// +typedef struct __attribute__((packed)) { + uint8_t stream_id[16]; // stream GUID + uint64_t file_size; + uint64_t creation_time; //File creation time FILETIME 8 + uint64_t num_packets; //Number of packets UINT64 8 + uint64_t play_duration; //Timestamp of the end position UINT64 8 + uint64_t send_duration; //Duration of the playback UINT64 8 + uint64_t preroll; //Time to bufferize before playing UINT64 8 + uint32_t flags; //Unknown, maybe flags ( usually contains 2 ) UINT32 4 + uint32_t min_packet_size; //Min size of the packet, in bytes UINT32 4 + uint32_t max_packet_size; //Max size of the packet UINT32 4 + uint32_t max_bitrate; //Maximum bitrate of the media (sum of all the stream) +} ASF_file_header_t; + +/////////////////////// +// ASF Stream Header +/////////////////////// +typedef struct __attribute__((packed)) { + uint8_t type[16]; // Stream type (audio/video) GUID 16 + uint8_t concealment[16]; // Audio error concealment type GUID 16 + uint64_t unk1; // Unknown, maybe reserved ( usually contains 0 ) UINT64 8 + uint32_t type_size; //Total size of type-specific data UINT32 4 + uint32_t stream_size; //Size of stream-specific data UINT32 4 + uint16_t stream_no; //Stream number UINT16 2 + uint32_t unk2; //Unknown UINT32 4 +} ASF_stream_header_t; + +/////////////////////////// +// ASF Content Description +/////////////////////////// +typedef struct __attribute__((packed)) { + uint16_t title_size; + uint16_t author_size; + uint16_t copyright_size; + uint16_t comment_size; + uint16_t rating_size; +} ASF_content_description_t; + +//////////////////////// +// ASF Segment Header +//////////////////////// +typedef struct __attribute__((packed)) { + uint8_t streamno; + uint8_t seq; + uint32_t x; + uint8_t flag; +} ASF_segmhdr_t; + +////////////////////// +// ASF Stream Chunck +////////////////////// +typedef struct __attribute__((packed)) { + uint16_t type; + uint16_t size; + uint32_t sequence_number; + uint16_t unknown; + uint16_t size_confirm; +} ASF_stream_chunck_t; + +// Definition of the stream type +#if BYTE_ORDER == BIG_ENDIAN + #define ASF_STREAMING_CLEAR 0x2443 // $C + #define ASF_STREAMING_DATA 0x2444 // $D + #define ASF_STREAMING_END_TRANS 0x2445 // $E + #define ASF_STREAMING_HEADER 0x2448 // $H +#else + #define ASF_STREAMING_CLEAR 0x4324 // $C + #define ASF_STREAMING_DATA 0x4424 // $D + #define ASF_STREAMING_END_TRANS 0x4524 // $E + #define ASF_STREAMING_HEADER 0x4824 // $H +#endif + +// Definition of the differents type of ASF streaming +typedef enum { + ASF_Unknown_e, + ASF_Live_e, + ASF_Prerecorded_e, + ASF_Redirector_e, + ASF_PlainText_e, + ASF_Authenticate_e +} ASF_StreamType_e; + +typedef struct { + ASF_StreamType_e streaming_type; + int request; + int packet_size; + int *audio_streams,n_audio,*video_streams,n_video; + int audio_id, video_id; +} asf_http_streaming_ctrl_t; + + +/* + * Some macros to swap little endian structures read from an ASF file + * into machine endian format + */ +#if BYTE_ORDER == BIG_ENDIAN +#define le2me_ASF_obj_header_t(h) { \ + (h)->size = le2me_64((h)->size); \ +} +#define le2me_ASF_header_t(h) { \ + le2me_ASF_obj_header_t(&(h)->objh); \ + (h)->cno = le2me_32((h)->cno); \ +} +#define le2me_ASF_stream_header_t(h) { \ + (h)->unk1 = le2me_64((h)->unk1); \ + (h)->type_size = le2me_32((h)->type_size); \ + (h)->stream_size = le2me_32((h)->stream_size); \ + (h)->stream_no = le2me_16((h)->stream_no); \ + (h)->unk2 = le2me_32((h)->unk2); \ +} +#define le2me_ASF_file_header_t(h) { \ + (h)->file_size = le2me_64((h)->file_size); \ + (h)->creation_time = le2me_64((h)->creation_time); \ + (h)->num_packets = le2me_64((h)->num_packets); \ + (h)->play_duration = le2me_64((h)->play_duration); \ + (h)->send_duration = le2me_64((h)->send_duration); \ + (h)->preroll = le2me_64((h)->preroll); \ + (h)->flags = le2me_32((h)->flags); \ + (h)->min_packet_size = le2me_32((h)->min_packet_size); \ + (h)->max_packet_size = le2me_32((h)->max_packet_size); \ + (h)->max_bitrate = le2me_32((h)->max_bitrate); \ +} +#define le2me_ASF_content_description_t(h) { \ + (h)->title_size = le2me_16((h)->title_size); \ + (h)->author_size = le2me_16((h)->author_size); \ + (h)->copyright_size = le2me_16((h)->copyright_size); \ + (h)->comment_size = le2me_16((h)->comment_size); \ + (h)->rating_size = le2me_16((h)->rating_size); \ +} +#define le2me_BITMAPINFOHEADER(h) { \ + (h)->biSize = le2me_32((h)->biSize); \ + (h)->biWidth = le2me_32((h)->biWidth); \ + (h)->biHeight = le2me_32((h)->biHeight); \ + (h)->biPlanes = le2me_16((h)->biPlanes); \ + (h)->biBitCount = le2me_16((h)->biBitCount); \ + (h)->biCompression = le2me_32((h)->biCompression); \ + (h)->biSizeImage = le2me_32((h)->biSizeImage); \ + (h)->biXPelsPerMeter = le2me_32((h)->biXPelsPerMeter); \ + (h)->biYPelsPerMeter = le2me_32((h)->biYPelsPerMeter); \ + (h)->biClrUsed = le2me_32((h)->biClrUsed); \ + (h)->biClrImportant = le2me_32((h)->biClrImportant); \ +} +#define le2me_WAVEFORMATEX(h) { \ + (h)->wFormatTag = le2me_16((h)->wFormatTag); \ + (h)->nChannels = le2me_16((h)->nChannels); \ + (h)->nSamplesPerSec = le2me_32((h)->nSamplesPerSec); \ + (h)->nAvgBytesPerSec = le2me_32((h)->nAvgBytesPerSec); \ + (h)->nBlockAlign = le2me_16((h)->nBlockAlign); \ + (h)->wBitsPerSample = le2me_16((h)->wBitsPerSample); \ + (h)->cbSize = le2me_16((h)->cbSize); \ +} +#define le2me_ASF_stream_chunck_t(h) { \ + (h)->size = le2me_16((h)->size); \ + (h)->sequence_number = le2me_32((h)->sequence_number); \ + (h)->unknown = le2me_16((h)->unknown); \ + (h)->size_confirm = le2me_16((h)->size_confirm); \ +} +#else +#define le2me_ASF_obj_header_t(h) /**/ +#define le2me_ASF_header_t(h) /**/ +#define le2me_ASF_stream_header_t(h) /**/ +#define le2me_ASF_file_header_t(h) /**/ +#define le2me_ASF_content_description_t(h) /**/ +#define le2me_BITMAPINFOHEADER(h) /**/ +#define le2me_WAVEFORMATEX(h) /**/ +#define le2me_ASF_stream_chunck_t(h) /**/ +#endif + +// priv struct for the demuxer +struct asf_priv { + ASF_header_t header; + unsigned char* packet; + int scrambling_h; + int scrambling_w; + int scrambling_b; + unsigned packetsize; + double packetrate; + double movielength; + int asf_is_dvr_ms; + uint32_t asf_frame_state; + int asf_frame_start_found; + double dvr_last_vid_pts; + uint64_t vid_frame_ct; + uint64_t play_duration; + uint64_t num_packets; + int new_vid_frame_seg; + int *vid_repdata_sizes; + int *aud_repdata_sizes; + int vid_repdata_count; + int aud_repdata_count; + uint64_t avg_vid_frame_time; + uint64_t last_key_payload_time; + uint64_t last_aud_pts; + uint64_t last_aud_diff; + int found_first_key_frame; + uint32_t last_vid_seq; + int vid_ext_timing_index; + int aud_ext_timing_index; + int vid_ext_frame_index; + int know_frame_time; + unsigned bps; +}; + +#endif /* MPLAYER_ASF_H */ diff --git a/demux/asfguid.h b/demux/asfguid.h new file mode 100644 index 0000000000..91b2760b28 --- /dev/null +++ b/demux/asfguid.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2001 Reimar Döffinger + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_ASFGUID_H +#define MPLAYER_ASFGUID_H + +#include <inttypes.h> +#include "libavutil/common.h" +#include "mpbswap.h" + + +#define ASF_LOAD_GUID_PREFIX(guid) AV_RL32(guid) + +#define ASF_GUID_PREFIX_audio_stream 0xF8699E40 +#define ASF_GUID_PREFIX_video_stream 0xBC19EFC0 +#define ASF_GUID_PREFIX_audio_conceal_none 0x49f1a440 +#define ASF_GUID_PREFIX_audio_conceal_interleave 0xbfc3cd50 +#define ASF_GUID_PREFIX_header 0x75B22630 +#define ASF_GUID_PREFIX_data_chunk 0x75b22636 +#define ASF_GUID_PREFIX_index_chunk 0x33000890 +#define ASF_GUID_PREFIX_stream_header 0xB7DC0791 +#define ASF_GUID_PREFIX_header_2_0 0xD6E229D1 +#define ASF_GUID_PREFIX_file_header 0x8CABDCA1 +#define ASF_GUID_PREFIX_content_desc 0x75b22633 +#define ASF_GUID_PREFIX_stream_group 0x7bf875ce +#define ASF_GUID_PREFIX_ext_audio_stream 0x31178C9D +#define ASF_GUID_PREFIX_ext_stream_embed_stream_header 0x3AFB65E2 +#define ASF_GUID_PREFIX_dvr_ms_timing_rep_data 0xFD3CC02A +#define ASF_GUID_PREFIX_dvr_ms_vid_frame_rep_data 0xDD6432CC + +/* +const char asf_audio_stream_guid[16] = {0x40, 0x9e, 0x69, 0xf8, + 0x4d, 0x5b, 0xcf, 0x11, 0xa8, 0xfd, 0x00, 0x80, 0x5f, 0x5c, 0x44, 0x2b}; +const char asf_video_stream_guid[16] = {0xc0, 0xef, 0x19, 0xbc, + 0x4d, 0x5b, 0xcf, 0x11, 0xa8, 0xfd, 0x00, 0x80, 0x5f, 0x5c, 0x44, 0x2b}; +*/ +static const char asf_stream_header_guid[16] = {0x91, 0x07, 0xdc, 0xb7, + 0xb7, 0xa9, 0xcf, 0x11, 0x8e, 0xe6, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65}; +static const char asf_file_header_guid[16] = {0xa1, 0xdc, 0xab, 0x8c, + 0x47, 0xa9, 0xcf, 0x11, 0x8e, 0xe4, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65}; +static const char asf_content_desc_guid[16] = {0x33, 0x26, 0xb2, 0x75, + 0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c}; +static const char asf_stream_group_guid[16] = {0xce, 0x75, 0xf8, 0x7b, + 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2}; +static const char asf_data_chunk_guid[16] = {0x36, 0x26, 0xb2, 0x75, + 0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c}; +static const char asf_ext_stream_embed_stream_header[16] = {0xe2, 0x65, 0xfb, 0x3a, + 0xef, 0x47, 0xf2, 0x40, 0xac, 0x2c, 0x70, 0xa9, 0x0d, 0x71, 0xd3, 0x43}; +static const char asf_ext_stream_audio[16] = {0x9d, 0x8c, 0x17, 0x31, + 0xe1, 0x03, 0x28, 0x45, 0xb5, 0x82, 0x3d, 0xf9, 0xdb, 0x22, 0xf5, 0x03}; +static const char asf_ext_stream_header[16] = {0xCB, 0xA5, 0xE6, 0x14, + 0x72, 0xC6, 0x32, 0x43, 0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}; +static const char asf_metadata_header[16] = {0xea, 0xcb, 0xf8, 0xc5, + 0xaf, 0x5b, 0x77, 0x48, 0x84, 0x67, 0xaa, 0x8c, 0x44, 0xfa, 0x4c, 0xca}; +static const char asf_content_encryption[16] = {0xfb, 0xb3, 0x11, 0x22, + 0x23, 0xbd, 0xd2, 0x11, 0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e}; +static const char asf_dvr_ms_timing_rep_data[16] = {0x2a, 0xc0, 0x3c,0xfd, + 0xdb, 0x06, 0xfa, 0x4c, 0x80, 0x1c, 0x72, 0x12, 0xd3, 0x87, 0x45, 0xe4}; +static const char asf_dvr_ms_vid_frame_rep_data[16] = {0xcc, 0x32, 0x64, 0xdd, + 0x29, 0xe2, 0xdb, 0x40, 0x80, 0xf6, 0xd2, 0x63, 0x28, 0xd2, 0x76, 0x1f}; + +static int find_asf_guid(char *buf, const char *guid, int cur_pos, int buf_len) +{ + int i; + for (i = cur_pos; i < buf_len - 19; i++) { + if (memcmp(&buf[i], guid, 16) == 0) + return i + 16 + 8; // point after guid + length + } + return -1; +} + +#endif /* MPLAYER_ASFGUID_H */ diff --git a/demux/asfheader.c b/demux/asfheader.c new file mode 100644 index 0000000000..bd775b7660 --- /dev/null +++ b/demux/asfheader.c @@ -0,0 +1,717 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// .asf fileformat docs from http://divx.euro.ru + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <libavutil/intreadwrite.h> +#include <libavutil/common.h> + +#include "config.h" +#include "mp_msg.h" + +#include "stream/stream.h" +#include "aviprint.h" +#include "demuxer.h" +#include "stheader.h" + +#include "asf.h" +#include "asfguid.h" +#include "asfheader.h" + +typedef struct { + // must be 0 for metadata record, might be non-zero for metadata lib record + uint16_t lang_list_index; + uint16_t stream_num; + uint16_t name_length; + uint16_t data_type; + uint32_t data_length; + uint16_t* name; + void* data; +} ASF_meta_record_t; + +static char* get_ucs2str(const uint16_t* inbuf, uint16_t inlen) +{ + char* outbuf = calloc(inlen, 2); + char* q; + int i; + + if (!outbuf) { + mp_tmsg(MSGT_HEADER, MSGL_ERR, "Memory allocation failed.\n"); + return NULL; + } + q = outbuf; + for (i = 0; i < inlen / 2; i++) { + uint8_t tmp; + PUT_UTF8(AV_RL16(&inbuf[i]), tmp, *q++ = tmp;) + } + return outbuf; +} + +static const char* asf_chunk_type(unsigned char* guid) { + static char tmp[60]; + char *p; + int i; + + switch(ASF_LOAD_GUID_PREFIX(guid)){ + case ASF_GUID_PREFIX_audio_stream: + return "guid_audio_stream"; + case ASF_GUID_PREFIX_ext_audio_stream: + return "guid_ext_audio_stream"; + case ASF_GUID_PREFIX_ext_stream_embed_stream_header: + return "guid_ext_stream_embed_stream_header"; + case ASF_GUID_PREFIX_video_stream: + return "guid_video_stream"; + case ASF_GUID_PREFIX_audio_conceal_none: + return "guid_audio_conceal_none"; + case ASF_GUID_PREFIX_audio_conceal_interleave: + return "guid_audio_conceal_interleave"; + case ASF_GUID_PREFIX_header: + return "guid_header"; + case ASF_GUID_PREFIX_data_chunk: + return "guid_data_chunk"; + case ASF_GUID_PREFIX_index_chunk: + return "guid_index_chunk"; + case ASF_GUID_PREFIX_stream_header: + return "guid_stream_header"; + case ASF_GUID_PREFIX_header_2_0: + return "guid_header_2_0"; + case ASF_GUID_PREFIX_file_header: + return "guid_file_header"; + case ASF_GUID_PREFIX_content_desc: + return "guid_content_desc"; + case ASF_GUID_PREFIX_dvr_ms_timing_rep_data: + return "guid_dvr_ms_timing_rep_data"; + case ASF_GUID_PREFIX_dvr_ms_vid_frame_rep_data: + return "guid_dvr_ms_vid_frame_rep_data"; + default: + strcpy(tmp, "unknown guid "); + p = tmp + strlen(tmp); + for (i = 0; i < 16; i++) { + if ((1 << i) & ((1<<4) | (1<<6) | (1<<8))) *p++ = '-'; + sprintf(p, "%02x", guid[i]); + p += 2; + } + return tmp; + } +} + +int asf_check_header(demuxer_t *demuxer){ + unsigned char asfhdrguid[16]={0x30,0x26,0xB2,0x75,0x8E,0x66,0xCF,0x11,0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C}; + struct asf_priv* asf = calloc(1,sizeof(*asf)); + asf->scrambling_h=asf->scrambling_w=asf->scrambling_b=1; + stream_read(demuxer->stream,(char*) &asf->header,sizeof(asf->header)); // header obj + le2me_ASF_header_t(&asf->header); // swap to machine endian +// for(i=0;i<16;i++) printf(" %02X",temp[i]);printf("\n"); +// for(i=0;i<16;i++) printf(" %02X",asfhdrguid[i]);printf("\n"); + if(memcmp(asfhdrguid,asf->header.objh.guid,16)){ + mp_msg(MSGT_HEADER,MSGL_V,"ASF_check: not ASF guid!\n"); + free(asf); + return 0; // not ASF guid + } + if(asf->header.cno>256){ + mp_msg(MSGT_HEADER,MSGL_V,"ASF_check: invalid subchunks_no %d\n",(int) asf->header.cno); + free(asf); + return 0; // invalid header??? + } + demuxer->priv = asf; + return DEMUXER_TYPE_ASF; +} + +static int get_ext_stream_properties(char *buf, int buf_len, int stream_num, struct asf_priv* asf, int is_video) +{ + int pos=0; + uint8_t *buffer = &buf[0]; + uint64_t avg_ft av_unused; + unsigned bitrate; + + while ((pos = find_asf_guid(buf, asf_ext_stream_header, pos, buf_len)) >= 0) { + int this_stream_num, stnamect, payct, i; + int buf_max_index=pos+50; + if (buf_max_index > buf_len) return 0; + buffer = &buf[pos]; + + // the following info is available + // some of it may be useful but we're skipping it for now + // starttime(8 bytes), endtime(8), + // leak-datarate(4), bucket-datasize(4), init-bucket-fullness(4), + // alt-leak-datarate(4), alt-bucket-datasize(4), alt-init-bucket-fullness(4), + // max-object-size(4), + // flags(4) (reliable,seekable,no_cleanpoints?,resend-live-cleanpoints, rest of bits reserved) + + buffer += 8+8; + bitrate = AV_RL32(buffer); + buffer += 8*4; + this_stream_num=AV_RL16(buffer);buffer+=2; + + if (this_stream_num == stream_num) { + buf_max_index+=14; + if (buf_max_index > buf_len) return 0; + buffer+=2; //skip stream-language-id-index + avg_ft = AV_RL64(buffer); // provided in 100ns units + buffer+=8; + asf->bps = bitrate / 8; + + // after this are values for stream-name-count and + // payload-extension-system-count + // followed by associated info for each + stnamect = AV_RL16(buffer);buffer+=2; + payct = AV_RL16(buffer);buffer+=2; + + // need to read stream names if present in order + // to get lengths - values are ignored for now + for (i=0; i<stnamect; i++) { + int stream_name_len; + buf_max_index+=4; + if (buf_max_index > buf_len) return 0; + buffer+=2; //language_id_index + stream_name_len = AV_RL16(buffer);buffer+=2; + buffer+=stream_name_len; //stream_name + buf_max_index+=stream_name_len; + if (buf_max_index > buf_len) return 0; + } + + if (is_video) { + asf->vid_repdata_count = payct; + asf->vid_repdata_sizes = malloc(payct*sizeof(int)); + } else { + asf->aud_repdata_count = payct; + asf->aud_repdata_sizes = malloc(payct*sizeof(int)); + } + + for (i=0; i<payct; i++) { + int payload_len; + buf_max_index+=22; + if (buf_max_index > buf_len) return 0; + // Each payload extension definition starts with a GUID. + // In dvr-ms files one of these indicates the presence an + // extension that contains pts values and this is always present + // in the video and audio streams. + // Another GUID indicates the presence of an extension + // that contains useful video frame demuxing information. + // Note that the extension data in each packet does not contain + // these GUIDs and that this header section defines the order the data + // will appear in. + if (memcmp(buffer, asf_dvr_ms_timing_rep_data, 16) == 0) { + if (is_video) + asf->vid_ext_timing_index = i; + else + asf->aud_ext_timing_index = i; + } else if (is_video && memcmp(buffer, asf_dvr_ms_vid_frame_rep_data, 16) == 0) + asf->vid_ext_frame_index = i; + buffer+=16; + + payload_len = AV_RL16(buffer);buffer+=2; + + if (is_video) + asf->vid_repdata_sizes[i] = payload_len; + else + asf->aud_repdata_sizes[i] = payload_len; + buffer+=4;//sys_len + } + + return 1; + } + } + return 1; +} + +#define CHECKDEC(l, n) if (((l) -= (n)) < 0) return 0 +static char* read_meta_record(ASF_meta_record_t* dest, char* buf, + int* buf_len) +{ + CHECKDEC(*buf_len, 2 + 2 + 2 + 2 + 4); + dest->lang_list_index = AV_RL16(buf); + dest->stream_num = AV_RL16(&buf[2]); + dest->name_length = AV_RL16(&buf[4]); + dest->data_type = AV_RL16(&buf[6]); + dest->data_length = AV_RL32(&buf[8]); + buf += 2 + 2 + 2 + 2 + 4; + CHECKDEC(*buf_len, dest->name_length); + dest->name = (uint16_t*)buf; + buf += dest->name_length; + CHECKDEC(*buf_len, dest->data_length); + dest->data = buf; + buf += dest->data_length; + return buf; +} + +static int get_meta(char *buf, int buf_len, int this_stream_num, + float* asp_ratio) +{ + int pos = 0; + uint16_t records_count; + uint16_t x = 0, y = 0; + + if ((pos = find_asf_guid(buf, asf_metadata_header, pos, buf_len)) < 0) + return 0; + + CHECKDEC(buf_len, pos); + buf += pos; + CHECKDEC(buf_len, 2); + records_count = AV_RL16(buf); + buf += 2; + + while (records_count--) { + ASF_meta_record_t record_entry; + char* name; + + if (!(buf = read_meta_record(&record_entry, buf, &buf_len))) + return 0; + /* reserved, must be zero */ + if (record_entry.lang_list_index) + continue; + /* match stream number: 0 to match all */ + if (record_entry.stream_num && record_entry.stream_num != this_stream_num) + continue; + if (!(name = get_ucs2str(record_entry.name, record_entry.name_length))) { + mp_tmsg(MSGT_HEADER, MSGL_ERR, "Memory allocation failed.\n"); + continue; + } + if (strcmp(name, "AspectRatioX") == 0) + x = AV_RL16(record_entry.data); + else if (strcmp(name, "AspectRatioY") == 0) + y = AV_RL16(record_entry.data); + free(name); + } + if (x && y) { + *asp_ratio = (float)x / (float)y; + return 1; + } + return 0; +} + +static int is_drm(char* buf, int buf_len) +{ + uint32_t data_len, type_len, key_len, url_len; + int pos = find_asf_guid(buf, asf_content_encryption, 0, buf_len); + + if (pos < 0) + return 0; + + CHECKDEC(buf_len, pos + 4); + buf += pos; + data_len = AV_RL32(buf); + buf += 4; + CHECKDEC(buf_len, data_len); + buf += data_len; + type_len = AV_RL32(buf); + if (type_len < 4) + return 0; + CHECKDEC(buf_len, 4 + type_len + 4); + buf += 4; + + if (buf[0] != 'D' || buf[1] != 'R' || buf[2] != 'M' || buf[3] != '\0') + return 0; + + buf += type_len; + key_len = AV_RL32(buf); + CHECKDEC(buf_len, key_len + 4); + buf += 4; + + buf[key_len - 1] = '\0'; + mp_msg(MSGT_HEADER, MSGL_V, "DRM Key ID: %s\n", buf); + + buf += key_len; + url_len = AV_RL32(buf); + CHECKDEC(buf_len, url_len); + buf += 4; + + buf[url_len - 1] = '\0'; + mp_tmsg(MSGT_HEADER, MSGL_INFO, "DRM License URL: %s\n", buf); + return 1; +} + +static int asf_init_audio_stream(demuxer_t *demuxer,struct asf_priv* asf, sh_audio_t* sh_audio, ASF_stream_header_t *streamh, int *ppos, uint8_t** buf, char *hdr, unsigned int hdr_len) +{ + uint8_t *buffer = *buf; + int pos = *ppos; + + sh_audio->wf=calloc(FFMAX(streamh->type_size, sizeof(*sh_audio->wf)), 1); + memcpy(sh_audio->wf,buffer,streamh->type_size); + le2me_WAVEFORMATEX(sh_audio->wf); + sh_audio->format=sh_audio->wf->wFormatTag; + if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_wave_header(sh_audio->wf,MSGL_V); + if(ASF_LOAD_GUID_PREFIX(streamh->concealment)==ASF_GUID_PREFIX_audio_conceal_interleave){ + buffer = &hdr[pos]; + pos += streamh->stream_size; + if (pos > hdr_len) return 0; + asf->scrambling_h=buffer[0]; + asf->scrambling_w=(buffer[2]<<8)|buffer[1]; + asf->scrambling_b=(buffer[4]<<8)|buffer[3]; + if(asf->scrambling_b>0){ + asf->scrambling_w/=asf->scrambling_b; + } + } else { + asf->scrambling_b=asf->scrambling_h=asf->scrambling_w=1; + } + mp_msg(MSGT_HEADER,MSGL_V,"ASF: audio scrambling: %d x %d x %d\n",asf->scrambling_h,asf->scrambling_w,asf->scrambling_b); + return 1; +} + +static int find_backwards_asf_guid(char *buf, const char *guid, int cur_pos) +{ + int i; + for (i=cur_pos-16; i>0; i--) { + if (memcmp(&buf[i], guid, 16) == 0) + return i + 16 + 8; // point after guid + length + } + return -1; +} + +int read_asf_header(demuxer_t *demuxer,struct asf_priv* asf){ + int hdr_len = asf->header.objh.size - sizeof(asf->header); + int hdr_skip = 0; + char *hdr = NULL; + char guid_buffer[16]; + int pos, start = stream_tell(demuxer->stream); + uint32_t* streams = NULL; + int audio_streams=0; + int video_streams=0; + uint16_t stream_count=0; + int best_video = -1; + int best_audio = -1; + uint64_t data_len; + ASF_stream_header_t *streamh; + uint8_t *buffer; + int audio_pos=0; + + if(hdr_len < 0) { + mp_msg(MSGT_HEADER, MSGL_FATAL, "Header size is too small.\n"); + return 0; + } + + if (hdr_len > 1024 * 1024) { + mp_tmsg(MSGT_HEADER, MSGL_ERR, "FATAL: header size bigger than 1 MB (%d)!\nPlease contact MPlayer authors, and upload/send this file.\n", + hdr_len); + hdr_skip = hdr_len - 1024 * 1024; + hdr_len = 1024 * 1024; + } + hdr = malloc(hdr_len); + if (!hdr) { + mp_tmsg(MSGT_HEADER, MSGL_FATAL, "Could not allocate %d bytes for header.\n", + hdr_len); + return 0; + } + stream_read(demuxer->stream, hdr, hdr_len); + if (hdr_skip) + stream_skip(demuxer->stream, hdr_skip); + if (stream_eof(demuxer->stream)) { + mp_tmsg(MSGT_HEADER, MSGL_FATAL, "EOF while reading ASF header, broken/incomplete file?\n"); + goto err_out; + } + + if (is_drm(hdr, hdr_len)) + mp_tmsg(MSGT_HEADER, MSGL_FATAL, "This file has been encumbered with DRM encryption, it will not play in MPlayer!\n"); + + if ((pos = find_asf_guid(hdr, asf_ext_stream_audio, 0, hdr_len)) >= 0) + { + // Special case: found GUID for dvr-ms audio. + // Now skip back to associated stream header. + int sh_pos=0; + + sh_pos = find_backwards_asf_guid(hdr, asf_stream_header_guid, pos); + + if (sh_pos > 0) { + sh_audio_t *sh_audio; + + mp_msg(MSGT_HEADER, MSGL_V, "read_asf_header found dvr-ms audio stream header pos=%d\n", sh_pos); + // found audio stream header - following code reads header and + // initializes audio stream. + audio_pos = pos - 16 - 8; + streamh = (ASF_stream_header_t *)&hdr[sh_pos]; + le2me_ASF_stream_header_t(streamh); + audio_pos += 64; //16+16+4+4+4+16+4; + buffer = &hdr[audio_pos]; + sh_audio=new_sh_audio(demuxer,streamh->stream_no & 0x7F); + sh_audio->needs_parsing = 1; + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "[%s] Audio stream found, -aid %d\n", "asfheader", streamh->stream_no & 0x7F); + ++audio_streams; + if (!asf_init_audio_stream(demuxer, asf, sh_audio, streamh, &audio_pos, &buffer, hdr, hdr_len)) + goto len_err_out; + if (!get_ext_stream_properties(hdr, hdr_len, streamh->stream_no, asf, 0)) + goto len_err_out; + } + } + // find stream headers + // only reset pos if we didnt find dvr_ms audio stream + // if we did find it then we want to avoid reading its header twice + if (audio_pos == 0) + pos = 0; + + while ((pos = find_asf_guid(hdr, asf_stream_header_guid, pos, hdr_len)) >= 0) + { + streamh = (ASF_stream_header_t *)&hdr[pos]; + pos += sizeof(ASF_stream_header_t); + if (pos > hdr_len) goto len_err_out; + le2me_ASF_stream_header_t(streamh); + mp_msg(MSGT_HEADER, MSGL_V, "stream type: %s\n", + asf_chunk_type(streamh->type)); + mp_msg(MSGT_HEADER, MSGL_V, "stream concealment: %s\n", + asf_chunk_type(streamh->concealment)); + mp_msg(MSGT_HEADER, MSGL_V, "type: %d bytes, stream: %d bytes ID: %d\n", + (int)streamh->type_size, (int)streamh->stream_size, + (int)streamh->stream_no); + mp_msg(MSGT_HEADER, MSGL_V, "unk1: %lX unk2: %X\n", + (unsigned long)streamh->unk1, (unsigned int)streamh->unk2); + mp_msg(MSGT_HEADER, MSGL_V, "FILEPOS=0x%X\n", pos + start); + // type-specific data: + buffer = &hdr[pos]; + pos += streamh->type_size; + if (pos > hdr_len) goto len_err_out; + switch(ASF_LOAD_GUID_PREFIX(streamh->type)){ + case ASF_GUID_PREFIX_audio_stream: { + sh_audio_t* sh_audio=new_sh_audio(demuxer,streamh->stream_no & 0x7F); + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "[%s] Audio stream found, -aid %d\n", "asfheader", streamh->stream_no & 0x7F); + ++audio_streams; + if (!asf_init_audio_stream(demuxer, asf, sh_audio, streamh, &pos, &buffer, hdr, hdr_len)) + goto len_err_out; + //if(demuxer->audio->id==-1) demuxer->audio->id=streamh.stream_no & 0x7F; + break; + } + case ASF_GUID_PREFIX_video_stream: { + unsigned int len; + float asp_ratio; + sh_video_t* sh_video=new_sh_video(demuxer,streamh->stream_no & 0x7F); + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "[%s] Video stream found, -vid %d\n", "asfheader", streamh->stream_no & 0x7F); + len=streamh->type_size-(4+4+1+2); + ++video_streams; +// sh_video->bih=malloc(chunksize); memset(sh_video->bih,0,chunksize); + sh_video->bih=calloc((len<sizeof(*sh_video->bih))?sizeof(*sh_video->bih):len,1); + memcpy(sh_video->bih,&buffer[4+4+1+2],len); + le2me_BITMAPINFOHEADER(sh_video->bih); + if (sh_video->bih->biSize > len && sh_video->bih->biSize > sizeof(*sh_video->bih)) + sh_video->bih->biSize = len; + if (sh_video->bih->biCompression == mmioFOURCC('D', 'V', 'R', ' ')) { + //mp_tmsg(MSGT_DEMUXER, MSGL_WARN, "DVR will probably only work with libavformat, try -demuxer 35 if you have problems\n"); + //sh_video->fps=(float)sh_video->video.dwRate/(float)sh_video->video.dwScale; + //sh_video->frametime=(float)sh_video->video.dwScale/(float)sh_video->video.dwRate; + asf->asf_frame_state=-1; + asf->asf_frame_start_found=0; + asf->asf_is_dvr_ms=1; + asf->dvr_last_vid_pts=0.0; + } else asf->asf_is_dvr_ms=0; + if (!get_ext_stream_properties(hdr, hdr_len, streamh->stream_no, asf, 1)) + goto len_err_out; + if (get_meta(hdr, hdr_len, streamh->stream_no, &asp_ratio)) { + sh_video->aspect = asp_ratio * sh_video->bih->biWidth / + sh_video->bih->biHeight; + } + sh_video->i_bps = asf->bps; + + if( mp_msg_test(MSGT_DEMUX,MSGL_V) ) print_video_header(sh_video->bih, MSGL_V); + //asf_video_id=streamh.stream_no & 0x7F; + //if(demuxer->video->id==-1) demuxer->video->id=streamh.stream_no & 0x7F; + break; + } + } + // stream-specific data: + // stream_read(demuxer->stream,(char*) buffer,streamh.stream_size); + } + + // find file header + pos = find_asf_guid(hdr, asf_file_header_guid, 0, hdr_len); + if (pos >= 0) { + ASF_file_header_t *fileh = (ASF_file_header_t *)&hdr[pos]; + pos += sizeof(ASF_file_header_t); + if (pos > hdr_len) goto len_err_out; + le2me_ASF_file_header_t(fileh); + mp_msg(MSGT_HEADER, MSGL_V, "ASF: packets: %d flags: %d " + "max_packet_size: %d min_packet_size: %d max_bitrate: %d " + "preroll: %d\n", + (int)fileh->num_packets, (int)fileh->flags, + (int)fileh->min_packet_size, (int)fileh->max_packet_size, + (int)fileh->max_bitrate, (int)fileh->preroll); + asf->packetsize=fileh->max_packet_size; + asf->packet=malloc(asf->packetsize); // !!! + asf->packetrate=fileh->max_bitrate/8.0/(double)asf->packetsize; + asf->movielength=FFMAX(0.0, (fileh->play_duration / 10000.0 - fileh->preroll) / 1000.0); + } + + // find content header + pos = find_asf_guid(hdr, asf_content_desc_guid, 0, hdr_len); + if (pos >= 0) { + ASF_content_description_t *contenth = (ASF_content_description_t *)&hdr[pos]; + char *string=NULL; + uint16_t* wstring = NULL; + uint16_t len; + pos += sizeof(ASF_content_description_t); + if (pos > hdr_len) goto len_err_out; + le2me_ASF_content_description_t(contenth); + mp_msg(MSGT_HEADER,MSGL_V,"\n"); + // extract the title + if((len = contenth->title_size) != 0) { + wstring = (uint16_t*)&hdr[pos]; + pos += len; + if (pos > hdr_len) goto len_err_out; + if ((string = get_ucs2str(wstring, len))) { + mp_msg(MSGT_HEADER,MSGL_V," Title: %s\n", string); + demux_info_add(demuxer, "title", string); + free(string); + } + } + // extract the author + if((len = contenth->author_size) != 0) { + wstring = (uint16_t*)&hdr[pos]; + pos += len; + if (pos > hdr_len) goto len_err_out; + if ((string = get_ucs2str(wstring, len))) { + mp_msg(MSGT_HEADER,MSGL_V," Author: %s\n", string); + demux_info_add(demuxer, "author", string); + free(string); + } + } + // extract the copyright + if((len = contenth->copyright_size) != 0) { + wstring = (uint16_t*)&hdr[pos]; + pos += len; + if (pos > hdr_len) goto len_err_out; + if ((string = get_ucs2str(wstring, len))) { + mp_msg(MSGT_HEADER,MSGL_V," Copyright: %s\n", string); + demux_info_add(demuxer, "copyright", string); + free(string); + } + } + // extract the comment + if((len = contenth->comment_size) != 0) { + wstring = (uint16_t*)&hdr[pos]; + pos += len; + if (pos > hdr_len) goto len_err_out; + if ((string = get_ucs2str(wstring, len))) { + mp_msg(MSGT_HEADER,MSGL_V," Comment: %s\n", string); + demux_info_add(demuxer, "comments", string); + free(string); + } + } + // extract the rating + if((len = contenth->rating_size) != 0) { + wstring = (uint16_t*)&hdr[pos]; + pos += len; + if (pos > hdr_len) goto len_err_out; + if ((string = get_ucs2str(wstring, len))) { + mp_msg(MSGT_HEADER,MSGL_V," Rating: %s\n", string); + free(string); + } + } + mp_msg(MSGT_HEADER,MSGL_V,"\n"); + } + + // find content header + pos = find_asf_guid(hdr, asf_stream_group_guid, 0, hdr_len); + if (pos >= 0) { + int max_streams = (hdr_len - pos - 2) / 6; + uint16_t stream_id, i; + uint32_t max_bitrate; + char *ptr = &hdr[pos]; + mp_msg(MSGT_HEADER,MSGL_V,"============ ASF Stream group == START ===\n"); + if(max_streams <= 0) goto len_err_out; + stream_count = AV_RL16(ptr); + ptr += sizeof(uint16_t); + if(stream_count > max_streams) stream_count = max_streams; + if(stream_count > 0) + streams = malloc(2*stream_count*sizeof(uint32_t)); + mp_msg(MSGT_HEADER,MSGL_V," stream count=[0x%x][%u]\n", stream_count, stream_count ); + for( i=0 ; i<stream_count ; i++ ) { + stream_id = AV_RL16(ptr); + ptr += sizeof(uint16_t); + max_bitrate = AV_RL32(ptr); + ptr += sizeof(uint32_t); + mp_msg(MSGT_HEADER,MSGL_V," stream id=[0x%x][%u]\n", stream_id, stream_id ); + mp_msg(MSGT_HEADER,MSGL_V," max bitrate=[0x%x][%u]\n", max_bitrate, max_bitrate ); + streams[2*i] = stream_id; + streams[2*i+1] = max_bitrate; + } + mp_msg(MSGT_HEADER,MSGL_V,"============ ASF Stream group == END ===\n"); + } + free(hdr); + hdr = NULL; + start = stream_tell(demuxer->stream); // start of first data chunk + stream_read(demuxer->stream, guid_buffer, 16); + if (memcmp(guid_buffer, asf_data_chunk_guid, 16) != 0) { + mp_tmsg(MSGT_HEADER, MSGL_FATAL, "No data chunk following header!\n"); + free(streams); + streams = NULL; + return 0; + } + // read length of chunk + data_len = stream_read_qword_le(demuxer->stream); + demuxer->movi_start = stream_tell(demuxer->stream) + 26; + demuxer->movi_end = start + data_len; + mp_msg(MSGT_HEADER, MSGL_V, "Found movie at 0x%X - 0x%X\n", + (int)demuxer->movi_start, (int)demuxer->movi_end); + +if(streams) { + // stream selection is done in the network code, it shouldn't be done here + // as the servers often do not care about what we requested. +#if 0 + uint32_t vr = 0, ar = 0,i; +#ifdef CONFIG_NETWORKING + if( demuxer->stream->streaming_ctrl!=NULL ) { + if( demuxer->stream->streaming_ctrl->bandwidth!=0 && demuxer->stream->streaming_ctrl->data!=NULL ) { + best_audio = ((asf_http_streaming_ctrl_t*)demuxer->stream->streaming_ctrl->data)->audio_id; + best_video = ((asf_http_streaming_ctrl_t*)demuxer->stream->streaming_ctrl->data)->video_id; + } + } else +#endif + for(i = 0; i < stream_count; i++) { + uint32_t id = streams[2*i]; + uint32_t rate = streams[2*i+1]; + if(demuxer->v_streams[id] && rate > vr) { + vr = rate; + best_video = id; + } else if(demuxer->a_streams[id] && rate > ar) { + ar = rate; + best_audio = id; + } + } +#endif + free(streams); + streams = NULL; +} + +mp_msg(MSGT_HEADER,MSGL_V,"ASF: %d audio and %d video streams found\n",audio_streams,video_streams); +if(!audio_streams) demuxer->audio->id=-2; // nosound +else if(best_audio > 0 && demuxer->audio->id == -1) demuxer->audio->id=best_audio; +if(!video_streams){ + if(!audio_streams){ + mp_tmsg(MSGT_HEADER,MSGL_ERR,"ASF: no audio or video headers found - broken file?\n"); + return 0; + } + demuxer->video->id=-2; // audio-only +} else if (best_video > 0 && demuxer->video->id == -1) demuxer->video->id = best_video; + +#if 0 +if( mp_msg_test(MSGT_HEADER,MSGL_V) ){ + printf("ASF duration: %d\n",(int)fileh.duration); + printf("ASF start pts: %d\n",(int)fileh.start_timestamp); + printf("ASF end pts: %d\n",(int)fileh.end_timestamp); +} +#endif + +return 1; + +len_err_out: + mp_tmsg(MSGT_HEADER, MSGL_FATAL, "Invalid length in ASF header!\n"); +err_out: + free(hdr); + free(streams); + return 0; +} diff --git a/demux/asfheader.h b/demux/asfheader.h new file mode 100644 index 0000000000..9c239f7987 --- /dev/null +++ b/demux/asfheader.h @@ -0,0 +1,28 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_ASFHEADER_H +#define MPLAYER_ASFHEADER_H + +#include "asf.h" +#include "demuxer.h" + +int asf_check_header(demuxer_t *demuxer); +int read_asf_header(demuxer_t *demuxer, struct asf_priv *asf); + +#endif /* MPLAYER_ASFHEADER_H */ diff --git a/demux/aviheader.c b/demux/aviheader.c new file mode 100644 index 0000000000..0c47386dc5 --- /dev/null +++ b/demux/aviheader.c @@ -0,0 +1,674 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <libavutil/common.h> +#include <libavutil/intreadwrite.h> + +#include "config.h" +#include "mp_msg.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "aviprint.h" +#include "aviheader.h" + +static MainAVIHeader avih; + +static int odml_get_vstream_id(int id, unsigned char res[]) +{ + if ((char)(id >> 16) == 'd') { + if (res) { + res[0] = id; + res[1] = id >> 8; + } + return 1; + } + return 0; +} + +static int avi_idx_cmp(const void *elem1, const void *elem2) +{ + register off_t a = AVI_IDX_OFFSET((AVIINDEXENTRY *)elem1); + register off_t b = AVI_IDX_OFFSET((AVIINDEXENTRY *)elem2); + return (a > b) - (b > a); +} + +void read_avi_header(demuxer_t *demuxer,int index_mode){ +sh_audio_t *sh_audio=NULL; +sh_video_t *sh_video=NULL; +int stream_id=-1; +int idxfix_videostream=0; +int idxfix_divx=0; +avi_priv_t* priv=demuxer->priv; +off_t list_end=0; + +//---- AVI header: +priv->idx_size=0; +priv->audio_streams=0; +while(1){ + int id=stream_read_dword_le(demuxer->stream); + unsigned chunksize,size2; + static int last_fccType=0; + static int last_fccHandler=0; + char* hdr=NULL; + // + if(stream_eof(demuxer->stream)) break; + // + if(id==mmioFOURCC('L','I','S','T')){ + unsigned len=stream_read_dword_le(demuxer->stream); // list size + id=stream_read_dword_le(demuxer->stream); // list type + mp_msg(MSGT_HEADER,MSGL_DBG2,"LIST %.4s len=%u\n",(char *) &id,len); + if(len >= 4) { + len -= 4; + list_end=stream_tell(demuxer->stream)+((len+1)&(~1)); + } else { + mp_tmsg(MSGT_HEADER,MSGL_WARN,"** empty list?!\n"); + list_end = 0; + } + mp_msg(MSGT_HEADER,MSGL_V,"list_end=0x%X\n",(int)list_end); + if(id==listtypeAVIMOVIE){ + // found MOVI header + if(!demuxer->movi_start) demuxer->movi_start=stream_tell(demuxer->stream); + demuxer->movi_end=stream_tell(demuxer->stream)+len; + mp_tmsg(MSGT_HEADER,MSGL_V,"Found movie at 0x%X - 0x%X\n",(int)demuxer->movi_start,(int)demuxer->movi_end); + if(demuxer->stream->end_pos>demuxer->movi_end) demuxer->movi_end=demuxer->stream->end_pos; + if(index_mode==-2 || index_mode==2 || index_mode==0) + break; // reading from non-seekable source (stdin) or forced index or no index forced + if(list_end>0) stream_seek(demuxer->stream,list_end); // skip movi + list_end=0; + } + continue; + } + size2=stream_read_dword_le(demuxer->stream); + mp_msg(MSGT_HEADER,MSGL_DBG2,"CHUNK %.4s len=%u\n",(char *) &id,size2); + chunksize=(size2+1)&(~1); + switch(id){ + + // Indicates where the subject of the file is archived + case mmioFOURCC('I','A','R','L'): hdr="Archival Location";break; + // Lists the artist of the original subject of the file; + // for example, "Michaelangelo." + case mmioFOURCC('I','A','R','T'): hdr="Artist";break; + // Lists the name of the person or organization that commissioned + // the subject of the file; for example "Pope Julian II." + case mmioFOURCC('I','C','M','S'): hdr="Commissioned";break; + // Provides general comments about the file or the subject + // of the file. If the comment is several sentences long, end each + // sentence with a period. Do not include new-line characters. + case mmioFOURCC('I','C','M','T'): hdr="Comments";break; + // Records the copyright information for the file; for example, + // "Copyright Encyclopedia International 1991." If there are multiple + // copyrights, separate them by semicolon followed by a space. + case mmioFOURCC('I','C','O','P'): hdr="Copyright";break; + // Describes whether an image has been cropped and, if so, how it + // was cropped; for example, "lower-right corner." + case mmioFOURCC('I','C','R','D'): hdr="Creation Date";break; + // Describes whether an image has been cropped and, if so, how it + // was cropped; for example, "lower-right corner." + case mmioFOURCC('I','C','R','P'): hdr="Cropped";break; + // Specifies the size of the original subject of the file; for + // example, "8.5 in h, 11 in w." + case mmioFOURCC('I','D','I','M'): hdr="Dimensions";break; + // Stores dots per inch setting of the digitizer used to + // produce the file, such as "300." + case mmioFOURCC('I','D','P','I'): hdr="Dots Per Inch";break; + // Stores the of the engineer who worked on the file. If there are + // multiple engineers, separate the names by a semicolon and a blank; + // for example, "Smith, John; Adams, Joe." + case mmioFOURCC('I','E','N','G'): hdr="Engineer";break; + // Describes the original work, such as "landscape,", "portrait," + // "still liefe," etc. + case mmioFOURCC('I','G','N','R'): hdr="Genre";break; + // Provides a list of keywords that refer to the file or subject of the + // file. Separate multiple keywords with a semicolon and a blank; + // for example, "Seattle, aerial view; scenery." + case mmioFOURCC('I','K','E','Y'): hdr="Keywords";break; + // ILGT - Describes the changes in the lightness settings on the digitizer + // required to produce the file. Note that the format of this information + // depends on the hardware used. + case mmioFOURCC('I','L','G','T'): hdr="Lightness";break; + // IMED - Decribes the original subject of the file, such as + // "computer image," "drawing," "lithograph," and so on. + case mmioFOURCC('I','M','E','D'): hdr="Medium";break; + // INAM - Stores the title of the subject of the file, such as + // "Seattle from Above." + case mmioFOURCC('I','N','A','M'): hdr="Title";break; + // IPLT - Specifies the number of colors requested when digitizing + // an image, such as "256." + case mmioFOURCC('I','P','L','T'): hdr="Palette Setting";break; + // IPRD - Specifies the name of title the file was originally intended + // for, such as "Encyclopedia of Pacific Northwest Geography." + case mmioFOURCC('I','P','R','D'): hdr="Product";break; + // ISBJ - Decsribes the contents of the file, such as + // "Aerial view of Seattle." + case mmioFOURCC('I','S','B','J'): hdr="Subject";break; + // ISFT - Identifies the name of the software packages used to create the + // file, such as "Microsoft WaveEdit" + case mmioFOURCC('I','S','F','T'): hdr="Software";break; + // ISHP - Identifies the change in sharpness for the digitizer + // required to produce the file (the format depends on the hardware used). + case mmioFOURCC('I','S','H','P'): hdr="Sharpness";break; + // ISRC - Identifies the name of the person or organization who + // supplied the original subject of the file; for example, "Try Research." + case mmioFOURCC('I','S','R','C'): hdr="Source";break; + // ISRF - Identifies the original form of the material that was digitized, + // such as "slide," "paper," "map," and so on. This is not necessarily + // the same as IMED + case mmioFOURCC('I','S','R','F'): hdr="Source Form";break; + // ITCH - Identifies the technician who digitized the subject file; + // for example, "Smith, John." + case mmioFOURCC('I','T','C','H'): hdr="Technician";break; + case mmioFOURCC('I','S','M','P'): hdr="Time Code";break; + case mmioFOURCC('I','D','I','T'): hdr="Digitization Time";break; + + case ckidAVIMAINHDR: // read 'avih' + stream_read(demuxer->stream,(char*) &avih,FFMIN(size2,sizeof(avih))); + le2me_MainAVIHeader(&avih); // swap to machine endian + chunksize-=FFMIN(size2,sizeof(avih)); + if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_avih(&avih,MSGL_V); // else print_avih_flags(&avih,MSGL_V); + break; + case ckidSTREAMHEADER: { // read 'strh' + AVIStreamHeader h; + stream_read(demuxer->stream,(char*) &h,FFMIN(size2,sizeof(h))); + le2me_AVIStreamHeader(&h); // swap to machine endian + chunksize-=FFMIN(size2,sizeof(h)); + ++stream_id; + if(h.fccType==streamtypeVIDEO){ + sh_video=new_sh_video(demuxer,stream_id); + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "[%s] Video stream found, -vid %d\n", "aviheader", stream_id); + memcpy(&sh_video->video,&h,sizeof(h)); + sh_video->stream_delay = (float)sh_video->video.dwStart * sh_video->video.dwScale/sh_video->video.dwRate; + } else + if(h.fccType==streamtypeAUDIO){ + sh_audio=new_sh_audio(demuxer,stream_id); + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "[%s] Audio stream found, -aid %d\n", "aviheader", stream_id); + memcpy(&sh_audio->audio,&h,sizeof(h)); + sh_audio->stream_delay = (float)sh_audio->audio.dwStart * sh_audio->audio.dwScale/sh_audio->audio.dwRate; + sh_audio->needs_parsing = 1; + } + last_fccType=h.fccType; + last_fccHandler=h.fccHandler; + if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_strh(&h,MSGL_V); + break; } + case mmioFOURCC('i', 'n', 'd', 'x'): { + uint32_t i; + avisuperindex_chunk *s; + + if(!index_mode) break; + + if(chunksize<=24){ + break; + } + priv->suidx_size++; + priv->suidx = realloc_struct(priv->suidx, priv->suidx_size, sizeof (avisuperindex_chunk)); + s = &priv->suidx[priv->suidx_size-1]; + + chunksize-=24; + memcpy(s->fcc, "indx", 4); + s->dwSize = size2; + s->wLongsPerEntry = stream_read_word_le(demuxer->stream); + s->bIndexSubType = stream_read_char(demuxer->stream); + s->bIndexType = stream_read_char(demuxer->stream); + s->nEntriesInUse = stream_read_dword_le(demuxer->stream); + AV_WN32(s->dwChunkId, stream_read_dword_le(demuxer->stream)); + stream_read(demuxer->stream, (char *)s->dwReserved, 3*4); + memset(s->dwReserved, 0, 3*4); + + print_avisuperindex_chunk(s,MSGL_V); + + // Check and fix this useless crap + if(s->wLongsPerEntry != sizeof (avisuperindex_entry)/4) { + mp_msg (MSGT_HEADER, MSGL_WARN, "Broken super index chunk size: %u\n",s->wLongsPerEntry); + s->wLongsPerEntry = sizeof(avisuperindex_entry)/4; + } + if( ((chunksize/4)/s->wLongsPerEntry) < s->nEntriesInUse){ + mp_msg (MSGT_HEADER, MSGL_WARN, "Broken super index chunk\n"); + s->nEntriesInUse = (chunksize/4)/s->wLongsPerEntry; + } + + s->aIndex = calloc(s->nEntriesInUse, sizeof (avisuperindex_entry)); + s->stdidx = calloc(s->nEntriesInUse, sizeof (avistdindex_chunk)); + + // now the real index of indices + for (i=0; i<s->nEntriesInUse; i++) { + chunksize-=16; + s->aIndex[i].qwOffset = stream_read_qword_le(demuxer->stream); + s->aIndex[i].dwSize = stream_read_dword_le(demuxer->stream); + s->aIndex[i].dwDuration = stream_read_dword_le(demuxer->stream); + mp_msg (MSGT_HEADER, MSGL_V, "ODML (%.4s): [%d] 0x%016"PRIx64" 0x%04x %u\n", + s->dwChunkId, i, + (uint64_t)s->aIndex[i].qwOffset, s->aIndex[i].dwSize, s->aIndex[i].dwDuration); + } + + break; } + case ckidSTREAMFORMAT: { // read 'strf' + if(last_fccType==streamtypeVIDEO){ + sh_video->bih=calloc(FFMAX(chunksize, sizeof(*sh_video->bih)), 1); +// sh_video->bih=malloc(chunksize); memset(sh_video->bih,0,chunksize); + mp_tmsg(MSGT_HEADER,MSGL_V,"Found 'bih', %u bytes of %zu\n",chunksize,sizeof(*sh_video->bih)); + stream_read(demuxer->stream,(char*) sh_video->bih,chunksize); + le2me_BITMAPINFOHEADER(sh_video->bih); // swap to machine endian + if (sh_video->bih->biSize > chunksize && sh_video->bih->biSize > sizeof(*sh_video->bih)) + sh_video->bih->biSize = chunksize; + // fixup MS-RLE header (seems to be broken for <256 color files) + if(sh_video->bih->biCompression<=1 && sh_video->bih->biSize==40) + sh_video->bih->biSize=chunksize; + if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_video_header(sh_video->bih,MSGL_V); + chunksize=0; + sh_video->fps=(float)sh_video->video.dwRate/(float)sh_video->video.dwScale; + sh_video->frametime=(float)sh_video->video.dwScale/(float)sh_video->video.dwRate; + sh_video->format = sh_video->bih->biCompression; +// if(demuxer->video->id==-1) demuxer->video->id=stream_id; + // IdxFix: + idxfix_videostream=stream_id; + switch(sh_video->bih->biCompression){ + case mmioFOURCC('M', 'P', 'G', '4'): + case mmioFOURCC('m', 'p', 'g', '4'): + case mmioFOURCC('D', 'I', 'V', '1'): + idxfix_divx=3; // set index recovery mpeg4 flavour: msmpeg4v1 + mp_tmsg(MSGT_HEADER,MSGL_V,"Regenerating keyframe table for M$ mpg4v1 video.\n"); + break; + case mmioFOURCC('D', 'I', 'V', '3'): + case mmioFOURCC('d', 'i', 'v', '3'): + case mmioFOURCC('D', 'I', 'V', '4'): + case mmioFOURCC('d', 'i', 'v', '4'): + case mmioFOURCC('D', 'I', 'V', '5'): + case mmioFOURCC('d', 'i', 'v', '5'): + case mmioFOURCC('D', 'I', 'V', '6'): + case mmioFOURCC('d', 'i', 'v', '6'): + case mmioFOURCC('M', 'P', '4', '3'): + case mmioFOURCC('m', 'p', '4', '3'): + case mmioFOURCC('M', 'P', '4', '2'): + case mmioFOURCC('m', 'p', '4', '2'): + case mmioFOURCC('D', 'I', 'V', '2'): + case mmioFOURCC('A', 'P', '4', '1'): + idxfix_divx=1; // set index recovery mpeg4 flavour: msmpeg4v3 + mp_tmsg(MSGT_HEADER,MSGL_V,"Regenerating keyframe table for DIVX3 video.\n"); + break; + case mmioFOURCC('D', 'I', 'V', 'X'): + case mmioFOURCC('d', 'i', 'v', 'x'): + case mmioFOURCC('D', 'X', '5', '0'): + case mmioFOURCC('X', 'V', 'I', 'D'): + case mmioFOURCC('x', 'v', 'i', 'd'): + case mmioFOURCC('F', 'M', 'P', '4'): + case mmioFOURCC('f', 'm', 'p', '4'): + idxfix_divx=2; // set index recovery mpeg4 flavour: generic mpeg4 + mp_tmsg(MSGT_HEADER,MSGL_V,"Regenerating keyframe table for MPEG-4 video.\n"); + break; + } + } else + if(last_fccType==streamtypeAUDIO){ + unsigned wf_size = chunksize<sizeof(*sh_audio->wf)?sizeof(*sh_audio->wf):chunksize; + sh_audio->wf=calloc(wf_size,1); +// sh_audio->wf=malloc(chunksize); memset(sh_audio->wf,0,chunksize); + mp_tmsg(MSGT_HEADER,MSGL_V,"Found 'wf', %d bytes of %zu\n",chunksize,sizeof(*sh_audio->wf)); + stream_read(demuxer->stream,(char*) sh_audio->wf,chunksize); + le2me_WAVEFORMATEX(sh_audio->wf); + if (sh_audio->wf->cbSize != 0 && + wf_size < sizeof(*sh_audio->wf)+sh_audio->wf->cbSize) { + sh_audio->wf=realloc(sh_audio->wf, sizeof(*sh_audio->wf)+sh_audio->wf->cbSize); + } + sh_audio->format=sh_audio->wf->wFormatTag; + if (sh_audio->wf->wFormatTag == 0xfffe && sh_audio->wf->cbSize >= 22) + sh_audio->format = le2me_16(((WAVEFORMATEXTENSIBLE *)sh_audio->wf)->SubFormat); + if (sh_audio->format == 1 && + last_fccHandler == mmioFOURCC('A', 'x', 'a', 'n')) + sh_audio->format = last_fccHandler; + sh_audio->i_bps=sh_audio->wf->nAvgBytesPerSec; + chunksize=0; + if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_wave_header(sh_audio->wf,MSGL_V); + ++priv->audio_streams; +// if(demuxer->audio->id==-1) demuxer->audio->id=stream_id; + } + break; + } + case mmioFOURCC('v', 'p', 'r', 'p'): { + VideoPropHeader *vprp = malloc(chunksize); + unsigned int i; + stream_read(demuxer->stream, (void*)vprp, chunksize); + le2me_VideoPropHeader(vprp); + chunksize -= sizeof(*vprp)-sizeof(vprp->FieldInfo); + chunksize /= sizeof(VIDEO_FIELD_DESC); + if (vprp->nbFieldPerFrame > chunksize) { + vprp->nbFieldPerFrame = chunksize; + } + chunksize = 0; + for (i=0; i<vprp->nbFieldPerFrame; i++) { + le2me_VIDEO_FIELD_DESC(&vprp->FieldInfo[i]); + } + if (sh_video) { + sh_video->aspect = GET_AVI_ASPECT(vprp->dwFrameAspectRatio); + } + if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_vprp(vprp,MSGL_V); + free(vprp); + break; + } + case mmioFOURCC('d', 'm', 'l', 'h'): { + // dmlh 00 00 00 04 frms + unsigned int total_frames = stream_read_dword_le(demuxer->stream); + mp_tmsg(MSGT_HEADER,MSGL_V,"AVI: dmlh found (size=%d) (total_frames=%d)\n", chunksize, total_frames); + stream_skip(demuxer->stream, chunksize-4); + chunksize = 0; + } + break; + case ckidAVINEWINDEX: + if(demuxer->movi_end>stream_tell(demuxer->stream)) + demuxer->movi_end=stream_tell(demuxer->stream); // fixup movi-end + if(index_mode && !priv->isodml){ + int read; + int i; + priv->idx_size=size2>>4; + mp_tmsg(MSGT_HEADER,MSGL_V,"Reading INDEX block, %d chunks for %d frames (fpos=%"PRId64").\n", + priv->idx_size,avih.dwTotalFrames, (int64_t)stream_tell(demuxer->stream)); + priv->idx=malloc(priv->idx_size<<4); +// printf("\nindex to %p !!!!! (priv=%p)\n",priv->idx,priv); + read = stream_read(demuxer->stream,(char*)priv->idx,priv->idx_size<<4); + priv->idx_size = FFMAX(read, 0) >> 4; + for (i = 0; i < priv->idx_size; i++) { // swap index to machine endian + AVIINDEXENTRY *entry=(AVIINDEXENTRY*)priv->idx + i; + le2me_AVIINDEXENTRY(entry); + /* + * We (ab)use the upper word for bits 32-47 of the offset, so + * we'll clear them here. + * FIXME: AFAIK no codec uses them, but if one does it will break + */ + entry->dwFlags&=0xffff; + } + chunksize-=priv->idx_size<<4; + if( mp_msg_test(MSGT_HEADER,MSGL_DBG2) ) print_index(priv->idx,priv->idx_size,MSGL_DBG2); + } + break; + /* added May 2002 */ + case mmioFOURCC('R','I','F','F'): { + char riff_type[4]; + + mp_tmsg(MSGT_HEADER, MSGL_V, "Additional RIFF header...\n"); + stream_read(demuxer->stream, riff_type, sizeof riff_type); + if (strncmp(riff_type, "AVIX", sizeof riff_type)) + mp_tmsg(MSGT_HEADER, MSGL_WARN, "** Warning: this is no extended AVI header..\n"); + else { + /* + * We got an extended AVI header, so we need to switch to + * ODML to get seeking to work, provided we got indx chunks + * in the header (suidx_size > 0). + */ + if (priv->suidx_size > 0) + priv->isodml = 1; + } + chunksize = 0; + list_end = 0; /* a new list will follow */ + break; } + case ckidAVIPADDING: + stream_skip(demuxer->stream, chunksize); + chunksize = 0; + break; + } + if(hdr){ + mp_msg(MSGT_HEADER,MSGL_V,"hdr=%s size=%u\n",hdr,size2); + if(size2==3) + chunksize=1; // empty + else { + char buf[256]; + int len=(size2<250)?size2:250; + stream_read(demuxer->stream,buf,len); + chunksize-=len; + buf[len]=0; + mp_msg(MSGT_HEADER,MSGL_V,"%-10s: %s\n",hdr,buf); + demux_info_add(demuxer, hdr, buf); + } + } + mp_msg(MSGT_HEADER,MSGL_DBG2,"list_end=0x%"PRIX64" pos=0x%"PRIX64" chunksize=0x%"PRIX64" next=0x%"PRIX64"\n", + (int64_t)list_end, (int64_t)stream_tell(demuxer->stream), + (int64_t)chunksize, (int64_t)chunksize+(int64_t)stream_tell(demuxer->stream)); + if(list_end>0 && + chunksize+stream_tell(demuxer->stream) == list_end) list_end=0; + if(list_end>0 && chunksize+stream_tell(demuxer->stream)>list_end){ + mp_tmsg(MSGT_HEADER,MSGL_V,"Broken chunk? chunksize=%d (id=%.4s)\n",chunksize,(char *) &id); + stream_seek(demuxer->stream,list_end); + list_end=0; + } else + if(chunksize>0) stream_skip(demuxer->stream,chunksize); else + if((int)chunksize<0) mp_msg(MSGT_HEADER,MSGL_WARN,"chunksize=%u (id=%.4s)\n",chunksize,(char *) &id); + +} + +if (priv->suidx_size > 0 && priv->idx_size == 0) { + /* + * No NEWAVIINDEX, but we got an OpenDML index. + */ + priv->isodml = 1; +} + +if (priv->isodml && (index_mode==-1 || index_mode==0 || index_mode==1)) { + int i, j, k; + + avisuperindex_chunk *cx; + AVIINDEXENTRY *idx; + + + if (priv->idx_size) free(priv->idx); + priv->idx_size = 0; + priv->idx_offset = 0; + priv->idx = NULL; + + mp_tmsg(MSGT_HEADER, MSGL_INFO, "AVI: ODML: Building ODML index (%d superindexchunks).\n", priv->suidx_size); + + // read the standard indices + for (cx = &priv->suidx[0], i=0; i<priv->suidx_size; cx++, i++) { + stream_reset(demuxer->stream); + for (j=0; j<cx->nEntriesInUse; j++) { + int ret1, ret2; + memset(&cx->stdidx[j], 0, 32); + ret1 = stream_seek(demuxer->stream, (off_t)cx->aIndex[j].qwOffset); + ret2 = stream_read(demuxer->stream, (char *)&cx->stdidx[j], 32); + if (ret1 != 1 || ret2 != 32 || cx->stdidx[j].nEntriesInUse==0) { + // this is a broken file (probably incomplete) let the standard + // gen_index routine handle this + priv->isodml = 0; + priv->idx_size = 0; + mp_tmsg(MSGT_HEADER, MSGL_WARN, "AVI: ODML: Broken (incomplete?) file detected. Will use traditional index.\n"); + goto freeout; + } + + le2me_AVISTDIDXCHUNK(&cx->stdidx[j]); + print_avistdindex_chunk(&cx->stdidx[j],MSGL_V); + priv->idx_size += cx->stdidx[j].nEntriesInUse; + cx->stdidx[j].aIndex = malloc(cx->stdidx[j].nEntriesInUse*sizeof(avistdindex_entry)); + stream_read(demuxer->stream, (char *)cx->stdidx[j].aIndex, + cx->stdidx[j].nEntriesInUse*sizeof(avistdindex_entry)); + for (k=0;k<cx->stdidx[j].nEntriesInUse; k++) + le2me_AVISTDIDXENTRY(&cx->stdidx[j].aIndex[k]); + + cx->stdidx[j].dwReserved3 = 0; + + } + } + + /* + * We convert the index by translating all entries into AVIINDEXENTRYs + * and sorting them by offset. The result should be the same index + * we would get with -forceidx. + */ + + idx = priv->idx = malloc(priv->idx_size * sizeof (AVIINDEXENTRY)); + + for (cx = priv->suidx; cx != &priv->suidx[priv->suidx_size]; cx++) { + avistdindex_chunk *sic; + for (sic = cx->stdidx; sic != &cx->stdidx[cx->nEntriesInUse]; sic++) { + avistdindex_entry *sie; + for (sie = sic->aIndex; sie != &sic->aIndex[sic->nEntriesInUse]; sie++) { + uint64_t off = sic->qwBaseOffset + sie->dwOffset - 8; + memcpy(&idx->ckid, sic->dwChunkId, 4); + idx->dwChunkOffset = off; + idx->dwFlags = (off >> 32) << 16; + idx->dwChunkLength = sie->dwSize & 0x7fffffff; + idx->dwFlags |= (sie->dwSize&0x80000000)?0x0:AVIIF_KEYFRAME; // bit 31 denotes !keyframe + idx++; + } + } + } + qsort(priv->idx, priv->idx_size, sizeof(AVIINDEXENTRY), avi_idx_cmp); + + /* + Hack to work around a "wrong" index in some divx odml files + (processor_burning.avi as an example) + They have ##dc on non keyframes but the ix00 tells us they are ##db. + Read the fcc of a non-keyframe vid frame and check it. + */ + + { + uint32_t id; + uint32_t db = 0; + stream_reset (demuxer->stream); + + // find out the video stream id. I have seen files with 01db. + for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){ + unsigned char res[2]; + if (odml_get_vstream_id(idx->ckid, res)) { + db = mmioFOURCC(res[0], res[1], 'd', 'b'); + break; + } + } + + // find first non keyframe + for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){ + if (!(idx->dwFlags & AVIIF_KEYFRAME) && idx->ckid == db) break; + } + if (i<priv->idx_size && db) { + stream_seek(demuxer->stream, AVI_IDX_OFFSET(idx)); + id = stream_read_dword_le(demuxer->stream); + if (id && id != db) // index fcc and real fcc differ? fix it. + for (idx = &((AVIINDEXENTRY *)priv->idx)[0], i=0; i<priv->idx_size; i++, idx++){ + if (!(idx->dwFlags & AVIIF_KEYFRAME) && idx->ckid == db) + idx->ckid = id; + } + } + } + + if ( mp_msg_test(MSGT_HEADER,MSGL_DBG2) ) print_index(priv->idx, priv->idx_size,MSGL_DBG2); + + demuxer->movi_end=demuxer->stream->end_pos; + +freeout: + + // free unneeded stuff + cx = &priv->suidx[0]; + do { + for (j=0;j<cx->nEntriesInUse;j++) + if (cx->stdidx[j].nEntriesInUse) free(cx->stdidx[j].aIndex); + free(cx->stdidx); + + } while (cx++ != &priv->suidx[priv->suidx_size-1]); + free(priv->suidx); + +} + +if(index_mode>=2 || (priv->idx_size==0 && index_mode==1)){ + int idx_pos = 0; + // build index for file: + stream_reset(demuxer->stream); + stream_seek(demuxer->stream,demuxer->movi_start); + + priv->idx_size=0; + priv->idx=NULL; + + while(1){ + int id; + unsigned len; + off_t skip; + AVIINDEXENTRY* idx; + unsigned int c; + demuxer->filepos=stream_tell(demuxer->stream); + if(demuxer->filepos>=demuxer->movi_end && demuxer->movi_start<demuxer->movi_end) break; + id=stream_read_dword_le(demuxer->stream); + len=stream_read_dword_le(demuxer->stream); + if(id==mmioFOURCC('L','I','S','T') || id==mmioFOURCC('R', 'I', 'F', 'F')){ + id=stream_read_dword_le(demuxer->stream); // list or RIFF type + continue; + } + if(stream_eof(demuxer->stream)) break; + if(!id || avi_stream_id(id)==100) goto skip_chunk; // bad ID (or padding?) + + if(idx_pos>=priv->idx_size){ +// priv->idx_size+=32; + priv->idx_size+=1024; // +16kB + priv->idx=realloc(priv->idx,priv->idx_size*sizeof(AVIINDEXENTRY)); + if(!priv->idx){idx_pos=0; break;} // error! + } + idx=&((AVIINDEXENTRY *)priv->idx)[idx_pos++]; + idx->ckid=id; + idx->dwFlags=AVIIF_KEYFRAME; // FIXME + idx->dwFlags|=(demuxer->filepos>>16)&0xffff0000U; + idx->dwChunkOffset=(unsigned long)demuxer->filepos; + idx->dwChunkLength=len; + + c=stream_read_dword(demuxer->stream); + + if(!len) idx->dwFlags&=~AVIIF_KEYFRAME; + + // Fix keyframes for DivX files: + if(idxfix_divx) + if(avi_stream_id(id)==idxfix_videostream){ + switch(idxfix_divx){ + case 3: c=stream_read_dword(demuxer->stream)<<5; //skip 32+5 bits for m$mpeg4v1 + case 1: if(c&0x40000000) idx->dwFlags&=~AVIIF_KEYFRAME;break; // divx 3 + case 2: if(c==0x1B6) idx->dwFlags&=~AVIIF_KEYFRAME;break; // divx 4 + } + } + + // update status line: + { static off_t lastpos; + off_t pos; + off_t len=demuxer->movi_end-demuxer->movi_start; + if(len){ + pos=100*(demuxer->filepos-demuxer->movi_start)/len; // % + } else { + pos=(demuxer->filepos-demuxer->movi_start)>>20; // MB + } + if(pos!=lastpos){ + lastpos=pos; + mp_tmsg(MSGT_HEADER,MSGL_STATUS, "Generating Index: %3lu %s \r", + (unsigned long)pos, len?"%":"MB"); + } + } + mp_dbg(MSGT_HEADER,MSGL_DBG2,"%08X %08X %.4s %08X %X\n",(unsigned int)demuxer->filepos,id,(char *) &id,(int)c,(unsigned int) idx->dwFlags); +#if 0 + { unsigned char tmp[64]; + int i; + stream_read(demuxer->stream,tmp,64); + printf("%.4s",&id); + for(i=0;i<64;i++) printf(" %02X",tmp[i]); + printf("\n"); + } +#endif +skip_chunk: + skip=(len+1)&(~1UL); // total bytes in this chunk + stream_seek(demuxer->stream,8+demuxer->filepos+skip); + } + priv->idx_size=idx_pos; + mp_tmsg(MSGT_HEADER,MSGL_INFO,"AVI: Generated index table for %d chunks!\n",priv->idx_size); + if( mp_msg_test(MSGT_HEADER,MSGL_DBG2) ) print_index(priv->idx,priv->idx_size,MSGL_DBG2); + +} +} diff --git a/demux/aviheader.h b/demux/aviheader.h new file mode 100644 index 0000000000..1629a33b57 --- /dev/null +++ b/demux/aviheader.h @@ -0,0 +1,382 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AVIHEADER_H +#define MPLAYER_AVIHEADER_H + +#include <sys/types.h> +#include <stdint.h> +#include "config.h" +#include "libavutil/common.h" +#include "mpbswap.h" + +#ifndef mmioFOURCC +#define mmioFOURCC( ch0, ch1, ch2, ch3 ) \ + ( (uint32_t)(uint8_t)(ch0) | ( (uint32_t)(uint8_t)(ch1) << 8 ) | \ + ( (uint32_t)(uint8_t)(ch2) << 16 ) | ( (uint32_t)(uint8_t)(ch3) << 24 ) ) +#endif + +/* Macro to make a TWOCC out of two characters */ +#ifndef aviTWOCC +#define aviTWOCC(ch0, ch1) ((uint16_t)(uint8_t)(ch0) | ((uint16_t)(uint8_t)(ch1) << 8)) +#endif + +//typedef uint16_t TWOCC; +//typedef uint32_t FOURCC; + +/* form types, list types, and chunk types */ +#define formtypeAVI mmioFOURCC('A', 'V', 'I', ' ') +#define listtypeAVIHEADER mmioFOURCC('h', 'd', 'r', 'l') +#define ckidAVIMAINHDR mmioFOURCC('a', 'v', 'i', 'h') +#define listtypeSTREAMHEADER mmioFOURCC('s', 't', 'r', 'l') +#define ckidSTREAMHEADER mmioFOURCC('s', 't', 'r', 'h') +#define ckidSTREAMFORMAT mmioFOURCC('s', 't', 'r', 'f') +#define ckidSTREAMHANDLERDATA mmioFOURCC('s', 't', 'r', 'd') +#define ckidSTREAMNAME mmioFOURCC('s', 't', 'r', 'n') + +#define listtypeAVIMOVIE mmioFOURCC('m', 'o', 'v', 'i') +#define listtypeAVIRECORD mmioFOURCC('r', 'e', 'c', ' ') + +#define ckidAVINEWINDEX mmioFOURCC('i', 'd', 'x', '1') + +/* +** Stream types for the <fccType> field of the stream header. +*/ +#define streamtypeVIDEO mmioFOURCC('v', 'i', 'd', 's') +#define streamtypeAUDIO mmioFOURCC('a', 'u', 'd', 's') +#define streamtypeMIDI mmioFOURCC('m', 'i', 'd', 's') +#define streamtypeTEXT mmioFOURCC('t', 'x', 't', 's') + +/* Basic chunk types */ +#define cktypeDIBbits aviTWOCC('d', 'b') +#define cktypeDIBcompressed aviTWOCC('d', 'c') +#define cktypePALchange aviTWOCC('p', 'c') +#define cktypeWAVEbytes aviTWOCC('w', 'b') + +/* Chunk id to use for extra chunks for padding. */ +#define ckidAVIPADDING mmioFOURCC('J', 'U', 'N', 'K') + +/* flags for use in <dwFlags> in AVIFileHdr */ +#define AVIF_HASINDEX 0x00000010 // Index at end of file? +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames? +#define AVIF_WASCAPTUREFILE 0x00010000 +#define AVIF_COPYRIGHTED 0x00020000 + +typedef struct +{ + uint32_t dwMicroSecPerFrame; // frame display rate (or 0L) + uint32_t dwMaxBytesPerSec; // max. transfer rate + uint32_t dwPaddingGranularity; // pad to multiples of this + // size; normally 2K. + uint32_t dwFlags; // the ever-present flags + uint32_t dwTotalFrames; // # frames in file + uint32_t dwInitialFrames; + uint32_t dwStreams; + uint32_t dwSuggestedBufferSize; + + uint32_t dwWidth; + uint32_t dwHeight; + + uint32_t dwReserved[4]; +} MainAVIHeader; + +typedef struct rectangle_t { + short left; + short top; + short right; + short bottom; +} rectangle_t; + +typedef struct { + uint32_t fccType; + uint32_t fccHandler; + uint32_t dwFlags; /* Contains AVITF_* flags */ + uint16_t wPriority; + uint16_t wLanguage; + uint32_t dwInitialFrames; + uint32_t dwScale; + uint32_t dwRate; /* dwRate / dwScale == samples/second */ + uint32_t dwStart; + uint32_t dwLength; /* In units above... */ + uint32_t dwSuggestedBufferSize; + uint32_t dwQuality; + uint32_t dwSampleSize; + rectangle_t rcFrame; +} AVIStreamHeader; + +/* Flags for index */ +#define AVIIF_LIST 0x00000001L // chunk is a 'LIST' +#define AVIIF_KEYFRAME 0x00000010L // this frame is a key frame. + +#define AVIIF_NOTIME 0x00000100L // this frame doesn't take any time +#define AVIIF_COMPUSE 0x0FFF0000L // these bits are for compressor use + +#ifndef FOURCC_RIFF +#define FOURCC_RIFF mmioFOURCC('R', 'I', 'F', 'F') +#define FOURCC_LIST mmioFOURCC('L', 'I', 'S', 'T') +#endif + +typedef struct +{ + uint32_t ckid; + uint32_t dwFlags; + uint32_t dwChunkOffset; // Position of chunk + uint32_t dwChunkLength; // Length of chunk +} AVIINDEXENTRY; + + +typedef struct avisuperindex_entry { + uint64_t qwOffset; // absolute file offset + uint32_t dwSize; // size of index chunk at this offset + uint32_t dwDuration; // time span in stream ticks +} avisuperindex_entry; + +typedef struct avistdindex_entry { + uint32_t dwOffset; // qwBaseOffset + this is absolute file offset + uint32_t dwSize; // bit 31 is set if this is NOT a keyframe +} avistdindex_entry; + +// Standard index +typedef struct __attribute__((packed)) avistdindex_chunk { + char fcc[4]; // ix## + uint32_t dwSize; // size of this chunk + uint16_t wLongsPerEntry; // must be sizeof(aIndex[0])/sizeof(DWORD) + uint8_t bIndexSubType; // must be 0 + uint8_t bIndexType; // must be AVI_INDEX_OF_CHUNKS + uint32_t nEntriesInUse; // first unused entry + char dwChunkId[4]; // '##dc' or '##db' or '##wb' etc.. + uint64_t qwBaseOffset; // all dwOffsets in aIndex array are relative to this + uint32_t dwReserved3; // must be 0 + avistdindex_entry *aIndex; // the actual frames +} avistdindex_chunk; + + +// Base Index Form 'indx' +typedef struct avisuperindex_chunk { + char fcc[4]; + uint32_t dwSize; // size of this chunk + uint16_t wLongsPerEntry; // size of each entry in aIndex array (must be 4*4 for us) + uint8_t bIndexSubType; // future use. must be 0 + uint8_t bIndexType; // one of AVI_INDEX_* codes + uint32_t nEntriesInUse; // index of first unused member in aIndex array + char dwChunkId[4]; // fcc of what is indexed + uint32_t dwReserved[3]; // meaning differs for each index type/subtype. + // 0 if unused + avisuperindex_entry *aIndex; // position of ix## chunks + avistdindex_chunk *stdidx; // the actual std indices +} avisuperindex_chunk; + +typedef struct { + uint32_t CompressedBMHeight; + uint32_t CompressedBMWidth; + uint32_t ValidBMHeight; + uint32_t ValidBMWidth; + uint32_t ValidBMXOffset; + uint32_t ValidBMYOffset; + uint32_t VideoXOffsetInT; + uint32_t VideoYValidStartLine; +} VIDEO_FIELD_DESC; + +typedef struct { + uint32_t VideoFormatToken; + uint32_t VideoStandard; + uint32_t dwVerticalRefreshRate; + uint32_t dwHTotalInT; + uint32_t dwVTotalInLines; + uint32_t dwFrameAspectRatio; + uint32_t dwFrameWidthInPixels; + uint32_t dwFrameHeightInLines; + uint32_t nbFieldPerFrame; + VIDEO_FIELD_DESC FieldInfo[2]; +} VideoPropHeader; + +typedef enum { + FORMAT_UNKNOWN, + FORMAT_PAL_SQUARE, + FORMAT_PAL_CCIR_601, + FORMAT_NTSC_SQUARE, + FORMAT_NTSC_CCIR_601, +} VIDEO_FORMAT; + +typedef enum { + STANDARD_UNKNOWN, + STANDARD_PAL, + STANDARD_NTSC, + STANDARD_SECAM +} VIDEO_STANDARD; + +#define MAKE_AVI_ASPECT(a, b) (((a)<<16)|(b)) +#define GET_AVI_ASPECT(a) ((float)((a)>>16)/(float)((a)&0xffff)) + +/* + * Some macros to swap little endian structures read from an AVI file + * into machine endian format + */ +#if BYTE_ORDER == BIG_ENDIAN +#define le2me_MainAVIHeader(h) { \ + (h)->dwMicroSecPerFrame = le2me_32((h)->dwMicroSecPerFrame); \ + (h)->dwMaxBytesPerSec = le2me_32((h)->dwMaxBytesPerSec); \ + (h)->dwPaddingGranularity = le2me_32((h)->dwPaddingGranularity); \ + (h)->dwFlags = le2me_32((h)->dwFlags); \ + (h)->dwTotalFrames = le2me_32((h)->dwTotalFrames); \ + (h)->dwInitialFrames = le2me_32((h)->dwInitialFrames); \ + (h)->dwStreams = le2me_32((h)->dwStreams); \ + (h)->dwSuggestedBufferSize = le2me_32((h)->dwSuggestedBufferSize); \ + (h)->dwWidth = le2me_32((h)->dwWidth); \ + (h)->dwHeight = le2me_32((h)->dwHeight); \ +} + +#define le2me_AVIStreamHeader(h) { \ + (h)->fccType = le2me_32((h)->fccType); \ + (h)->fccHandler = le2me_32((h)->fccHandler); \ + (h)->dwFlags = le2me_32((h)->dwFlags); \ + (h)->wPriority = le2me_16((h)->wPriority); \ + (h)->wLanguage = le2me_16((h)->wLanguage); \ + (h)->dwInitialFrames = le2me_32((h)->dwInitialFrames); \ + (h)->dwScale = le2me_32((h)->dwScale); \ + (h)->dwRate = le2me_32((h)->dwRate); \ + (h)->dwStart = le2me_32((h)->dwStart); \ + (h)->dwLength = le2me_32((h)->dwLength); \ + (h)->dwSuggestedBufferSize = le2me_32((h)->dwSuggestedBufferSize); \ + (h)->dwQuality = le2me_32((h)->dwQuality); \ + (h)->dwSampleSize = le2me_32((h)->dwSampleSize); \ + le2me_RECT(&(h)->rcFrame); \ +} +#define le2me_RECT(h) { \ + (h)->left = le2me_16((h)->left); \ + (h)->top = le2me_16((h)->top); \ + (h)->right = le2me_16((h)->right); \ + (h)->bottom = le2me_16((h)->bottom); \ +} +#define le2me_BITMAPINFOHEADER(h) { \ + (h)->biSize = le2me_32((h)->biSize); \ + (h)->biWidth = le2me_32((h)->biWidth); \ + (h)->biHeight = le2me_32((h)->biHeight); \ + (h)->biPlanes = le2me_16((h)->biPlanes); \ + (h)->biBitCount = le2me_16((h)->biBitCount); \ + (h)->biCompression = le2me_32((h)->biCompression); \ + (h)->biSizeImage = le2me_32((h)->biSizeImage); \ + (h)->biXPelsPerMeter = le2me_32((h)->biXPelsPerMeter); \ + (h)->biYPelsPerMeter = le2me_32((h)->biYPelsPerMeter); \ + (h)->biClrUsed = le2me_32((h)->biClrUsed); \ + (h)->biClrImportant = le2me_32((h)->biClrImportant); \ +} +#define le2me_WAVEFORMATEX(h) { \ + (h)->wFormatTag = le2me_16((h)->wFormatTag); \ + (h)->nChannels = le2me_16((h)->nChannels); \ + (h)->nSamplesPerSec = le2me_32((h)->nSamplesPerSec); \ + (h)->nAvgBytesPerSec = le2me_32((h)->nAvgBytesPerSec); \ + (h)->nBlockAlign = le2me_16((h)->nBlockAlign); \ + (h)->wBitsPerSample = le2me_16((h)->wBitsPerSample); \ + (h)->cbSize = le2me_16((h)->cbSize); \ +} +#define le2me_AVIINDEXENTRY(h) { \ + (h)->ckid = le2me_32((h)->ckid); \ + (h)->dwFlags = le2me_32((h)->dwFlags); \ + (h)->dwChunkOffset = le2me_32((h)->dwChunkOffset); \ + (h)->dwChunkLength = le2me_32((h)->dwChunkLength); \ +} +#define le2me_AVISTDIDXCHUNK(h) {\ + char c; \ + c = (h)->fcc[0]; (h)->fcc[0] = (h)->fcc[3]; (h)->fcc[3] = c; \ + c = (h)->fcc[1]; (h)->fcc[1] = (h)->fcc[2]; (h)->fcc[2] = c; \ + (h)->dwSize = le2me_32((h)->dwSize); \ + (h)->wLongsPerEntry = le2me_16((h)->wLongsPerEntry); \ + (h)->nEntriesInUse = le2me_32((h)->nEntriesInUse); \ + c = (h)->dwChunkId[0]; (h)->dwChunkId[0] = (h)->dwChunkId[3]; (h)->dwChunkId[3] = c; \ + c = (h)->dwChunkId[1]; (h)->dwChunkId[1] = (h)->dwChunkId[2]; (h)->dwChunkId[2] = c; \ + (h)->qwBaseOffset = le2me_64((h)->qwBaseOffset); \ + (h)->dwReserved3 = le2me_32((h)->dwReserved3); \ +} +#define le2me_AVISTDIDXENTRY(h) {\ + (h)->dwOffset = le2me_32((h)->dwOffset); \ + (h)->dwSize = le2me_32((h)->dwSize); \ +} +#define le2me_VideoPropHeader(h) { \ + (h)->VideoFormatToken = le2me_32((h)->VideoFormatToken); \ + (h)->VideoStandard = le2me_32((h)->VideoStandard); \ + (h)->dwVerticalRefreshRate = le2me_32((h)->dwVerticalRefreshRate); \ + (h)->dwHTotalInT = le2me_32((h)->dwHTotalInT); \ + (h)->dwVTotalInLines = le2me_32((h)->dwVTotalInLines); \ + (h)->dwFrameAspectRatio = le2me_32((h)->dwFrameAspectRatio); \ + (h)->dwFrameWidthInPixels = le2me_32((h)->dwFrameWidthInPixels); \ + (h)->dwFrameHeightInLines = le2me_32((h)->dwFrameHeightInLines); \ + (h)->nbFieldPerFrame = le2me_32((h)->nbFieldPerFrame); \ +} +#define le2me_VIDEO_FIELD_DESC(h) { \ + (h)->CompressedBMHeight = le2me_32((h)->CompressedBMHeight); \ + (h)->CompressedBMWidth = le2me_32((h)->CompressedBMWidth); \ + (h)->ValidBMHeight = le2me_32((h)->ValidBMHeight); \ + (h)->ValidBMWidth = le2me_32((h)->ValidBMWidth); \ + (h)->ValidBMXOffset = le2me_32((h)->ValidBMXOffset); \ + (h)->ValidBMYOffset = le2me_32((h)->ValidBMYOffset); \ + (h)->VideoXOffsetInT = le2me_32((h)->VideoXOffsetInT); \ + (h)->VideoYValidStartLine = le2me_32((h)->VideoYValidStartLine); \ +} + +#else +#define le2me_MainAVIHeader(h) /**/ +#define le2me_AVIStreamHeader(h) /**/ +#define le2me_RECT(h) /**/ +#define le2me_BITMAPINFOHEADER(h) /**/ +#define le2me_WAVEFORMATEX(h) /**/ +#define le2me_AVIINDEXENTRY(h) /**/ +#define le2me_AVISTDIDXCHUNK(h) /**/ +#define le2me_AVISTDIDXENTRY(h) /**/ +#define le2me_VideoPropHeader(h) /**/ +#define le2me_VIDEO_FIELD_DESC(h) /**/ +#endif + +typedef struct { + // index stuff: + void* idx; + int idx_size; + off_t idx_pos; + off_t idx_pos_a; + off_t idx_pos_v; + off_t idx_offset; // ennyit kell hozzaadni az index offset ertekekhez + // bps-based PTS stuff: + int video_pack_no; + int audio_block_size; + off_t audio_block_no; + // interleaved PTS stuff: + int skip_video_frames; + int audio_streams; + float avi_audio_pts; + float avi_video_pts; + float pts_correction; + unsigned int pts_corr_bytes; + unsigned char pts_corrected; + unsigned char pts_has_video; + unsigned int numberofframes; + avisuperindex_chunk *suidx; + int suidx_size; + int isodml; + int warned_unaligned; +} avi_priv_t; + +#define AVI_PRIV ((avi_priv_t*)(demuxer->priv)) + +#define AVI_IDX_OFFSET(x) ((((uint64_t)(x)->dwFlags&0xffff0000)<<16)+(x)->dwChunkOffset) + +struct demuxer; +void read_avi_header(struct demuxer *demuxer, int index_mode); + +#endif /* MPLAYER_AVIHEADER_H */ diff --git a/demux/aviprint.c b/demux/aviprint.c new file mode 100644 index 0000000000..e7520a42c5 --- /dev/null +++ b/demux/aviprint.c @@ -0,0 +1,197 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include "config.h" + +// for avi_stream_id(): +#include "stream/stream.h" +#include "demuxer.h" + +#include "aviheader.h" +#include "ms_hdr.h" +#include "aviprint.h" + +//#include "codec-cfg.h" +//#include "stheader.h" + +void print_avih_flags(MainAVIHeader *h, int verbose_level){ + mp_msg(MSGT_HEADER, verbose_level, "MainAVIHeader.dwFlags: (%"PRId32")%s%s%s%s%s%s\n",h->dwFlags, + (h->dwFlags&AVIF_HASINDEX)?" HAS_INDEX":"", + (h->dwFlags&AVIF_MUSTUSEINDEX)?" MUST_USE_INDEX":"", + (h->dwFlags&AVIF_ISINTERLEAVED)?" IS_INTERLEAVED":"", + (h->dwFlags&AVIF_TRUSTCKTYPE)?" TRUST_CKTYPE":"", + (h->dwFlags&AVIF_WASCAPTUREFILE)?" WAS_CAPTUREFILE":"", + (h->dwFlags&AVIF_COPYRIGHTED)?" COPYRIGHTED":"" + ); +} + +void print_avih(MainAVIHeader *h, int verbose_level){ + mp_msg(MSGT_HEADER, verbose_level, "======= AVI Header =======\n"); + mp_msg(MSGT_HEADER, verbose_level, "us/frame: %"PRId32" (fps=%5.3f)\n",h->dwMicroSecPerFrame,1000000.0f/(float)h->dwMicroSecPerFrame); + mp_msg(MSGT_HEADER, verbose_level, "max bytes/sec: %"PRId32"\n",h->dwMaxBytesPerSec); + mp_msg(MSGT_HEADER, verbose_level, "padding: %"PRId32"\n",h->dwPaddingGranularity); + print_avih_flags(h, verbose_level); + mp_msg(MSGT_HEADER, verbose_level, "frames total: %"PRId32" initial: %"PRId32"\n",h->dwTotalFrames,h->dwInitialFrames); + mp_msg(MSGT_HEADER, verbose_level, "streams: %"PRId32"\n",h->dwStreams); + mp_msg(MSGT_HEADER, verbose_level, "Suggested BufferSize: %"PRId32"\n",h->dwSuggestedBufferSize); + mp_msg(MSGT_HEADER, verbose_level, "Size: %"PRId32" x %"PRId32"\n",h->dwWidth,h->dwHeight); + mp_msg(MSGT_HEADER, verbose_level, "==========================\n"); +} + +void print_strh(AVIStreamHeader *h, int verbose_level){ + mp_msg(MSGT_HEADER, verbose_level, "====== STREAM Header =====\n"); + mp_msg(MSGT_HEADER, verbose_level, "Type: %.4s FCC: %.4s (%X)\n",(char *)&h->fccType,(char *)&h->fccHandler,(unsigned int)h->fccHandler); + mp_msg(MSGT_HEADER, verbose_level, "Flags: %"PRId32"\n",h->dwFlags); + mp_msg(MSGT_HEADER, verbose_level, "Priority: %d Language: %d\n",h->wPriority,h->wLanguage); + mp_msg(MSGT_HEADER, verbose_level, "InitialFrames: %"PRId32"\n",h->dwInitialFrames); + mp_msg(MSGT_HEADER, verbose_level, "Rate: %"PRId32"/%"PRId32" = %5.3f\n",h->dwRate,h->dwScale,(float)h->dwRate/(float)h->dwScale); + mp_msg(MSGT_HEADER, verbose_level, "Start: %"PRId32" Len: %"PRId32"\n",h->dwStart,h->dwLength); + mp_msg(MSGT_HEADER, verbose_level, "Suggested BufferSize: %"PRId32"\n",h->dwSuggestedBufferSize); + mp_msg(MSGT_HEADER, verbose_level, "Quality %"PRId32"\n",h->dwQuality); + mp_msg(MSGT_HEADER, verbose_level, "Sample size: %"PRId32"\n",h->dwSampleSize); + mp_msg(MSGT_HEADER, verbose_level, "==========================\n"); +} + +void print_wave_header(WAVEFORMATEX *h, int verbose_level){ + mp_msg(MSGT_HEADER, verbose_level, "======= WAVE Format =======\n"); + mp_msg(MSGT_HEADER, verbose_level, "Format Tag: %d (0x%X)\n",h->wFormatTag,h->wFormatTag); + mp_msg(MSGT_HEADER, verbose_level, "Channels: %d\n",h->nChannels); + mp_msg(MSGT_HEADER, verbose_level, "Samplerate: %"PRId32"\n",h->nSamplesPerSec); + mp_msg(MSGT_HEADER, verbose_level, "avg byte/sec: %"PRId32"\n",h->nAvgBytesPerSec); + mp_msg(MSGT_HEADER, verbose_level, "Block align: %d\n",h->nBlockAlign); + mp_msg(MSGT_HEADER, verbose_level, "bits/sample: %d\n",h->wBitsPerSample); + mp_msg(MSGT_HEADER, verbose_level, "cbSize: %d\n",h->cbSize); + if(h->wFormatTag==0x55 && h->cbSize>=12){ + MPEGLAYER3WAVEFORMAT* h2=(MPEGLAYER3WAVEFORMAT *)h; + mp_msg(MSGT_HEADER, verbose_level, "mp3.wID=%d\n",h2->wID); + mp_msg(MSGT_HEADER, verbose_level, "mp3.fdwFlags=0x%"PRIX32"\n",h2->fdwFlags); + mp_msg(MSGT_HEADER, verbose_level, "mp3.nBlockSize=%d\n",h2->nBlockSize); + mp_msg(MSGT_HEADER, verbose_level, "mp3.nFramesPerBlock=%d\n",h2->nFramesPerBlock); + mp_msg(MSGT_HEADER, verbose_level, "mp3.nCodecDelay=%d\n",h2->nCodecDelay); + } + else if (h->wFormatTag == 0xfffe && h->cbSize >= 22) { + WAVEFORMATEXTENSIBLE *h2 = (WAVEFORMATEXTENSIBLE *)h; + mp_msg(MSGT_HEADER, verbose_level, "ex.wValidBitsPerSample=%d\n", h2->wValidBitsPerSample); + mp_msg(MSGT_HEADER, verbose_level, "ex.dwChannelMask=0x%X\n", h2->dwChannelMask); + mp_msg(MSGT_HEADER, verbose_level, "ex.SubFormat=%d (0x%X)\n", h2->SubFormat, h2->SubFormat); + } + else if (h->cbSize > 0) + { + int i; + uint8_t* p = (uint8_t*)(h + 1); + mp_msg(MSGT_HEADER, verbose_level, "Unknown extra header dump: "); + for (i = 0; i < h->cbSize; i++) + mp_msg(MSGT_HEADER, verbose_level, "[%x] ", p[i]); + mp_msg(MSGT_HEADER, verbose_level, "\n"); + } + mp_msg(MSGT_HEADER, verbose_level, "==========================================================================\n"); +} + + +void print_video_header(BITMAPINFOHEADER *h, int verbose_level){ + mp_msg(MSGT_HEADER, verbose_level, "======= VIDEO Format ======\n"); + mp_msg(MSGT_HEADER, verbose_level, " biSize %d\n", h->biSize); + mp_msg(MSGT_HEADER, verbose_level, " biWidth %d\n", h->biWidth); + mp_msg(MSGT_HEADER, verbose_level, " biHeight %d\n", h->biHeight); + mp_msg(MSGT_HEADER, verbose_level, " biPlanes %d\n", h->biPlanes); + mp_msg(MSGT_HEADER, verbose_level, " biBitCount %d\n", h->biBitCount); + mp_msg(MSGT_HEADER, verbose_level, " biCompression %d='%.4s'\n", h->biCompression, (char *)&h->biCompression); + mp_msg(MSGT_HEADER, verbose_level, " biSizeImage %d\n", h->biSizeImage); + if (h->biSize > sizeof(*h)) + { + int i; + uint8_t* p = (uint8_t*)(h + 1); + mp_msg(MSGT_HEADER, verbose_level, "Unknown extra header dump: "); + for (i = 0; i < h->biSize-sizeof(*h); i++) + mp_msg(MSGT_HEADER, verbose_level, "[%x] ", *(p+i)); + mp_msg(MSGT_HEADER, verbose_level, "\n"); + } + mp_msg(MSGT_HEADER, verbose_level, "===========================\n"); +} + +void print_vprp(VideoPropHeader *vprp, int verbose_level){ + int i; + mp_msg(MSGT_HEADER, verbose_level, "======= Video Properties Header =======\n"); + mp_msg(MSGT_HEADER, verbose_level, "Format: %d VideoStandard: %d\n", + vprp->VideoFormatToken,vprp->VideoStandard); + mp_msg(MSGT_HEADER, verbose_level, "VRefresh: %d HTotal: %d VTotal: %d\n", + vprp->dwVerticalRefreshRate, vprp->dwHTotalInT, vprp->dwVTotalInLines); + mp_msg(MSGT_HEADER, verbose_level, "FrameAspect: %d:%d Framewidth: %d Frameheight: %d\n", + vprp->dwFrameAspectRatio >> 16, vprp->dwFrameAspectRatio & 0xffff, + vprp->dwFrameWidthInPixels, vprp->dwFrameHeightInLines); + mp_msg(MSGT_HEADER, verbose_level, "Fields: %d\n", vprp->nbFieldPerFrame); + for (i=0; i<vprp->nbFieldPerFrame; i++) { + VIDEO_FIELD_DESC *vfd = &vprp->FieldInfo[i]; + mp_msg(MSGT_HEADER, verbose_level, " == Field %d description ==\n", i); + mp_msg(MSGT_HEADER, verbose_level, " CompressedBMHeight: %d CompressedBMWidth: %d\n", + vfd->CompressedBMHeight, vfd->CompressedBMWidth); + mp_msg(MSGT_HEADER, verbose_level, " ValidBMHeight: %d ValidBMWidth: %d\n", + vfd->ValidBMHeight, vfd->ValidBMWidth); + mp_msg(MSGT_HEADER, verbose_level, " ValidBMXOffset: %d ValidBMYOffset: %d\n", + vfd->ValidBMXOffset, vfd->ValidBMYOffset); + mp_msg(MSGT_HEADER, verbose_level, " VideoXOffsetInT: %d VideoYValidStartLine: %d\n", + vfd->VideoXOffsetInT, vfd->VideoYValidStartLine); + } + mp_msg(MSGT_HEADER, verbose_level, "=======================================\n"); +} + +void print_index(AVIINDEXENTRY *idx, int idx_size, int verbose_level){ + int i; + unsigned int pos[256]; + unsigned int num[256]; + memset(pos, 0, sizeof(pos)); + memset(num, 0, sizeof(num)); + for(i=0;i<idx_size;i++){ + int id=avi_stream_id(idx[i].ckid); + if(id<0 || id>255) id=255; + mp_msg(MSGT_HEADER, verbose_level, "%5d: %.4s %4X %016"PRIX64" len:%6"PRId32" pos:%7d->%7.3f %7d->%7.3f\n",i, + (char *)&idx[i].ckid, + (unsigned int)idx[i].dwFlags&0xffff, + (uint64_t)AVI_IDX_OFFSET(&idx[i]), +// idx[i].dwChunkOffset+demuxer->movi_start, + idx[i].dwChunkLength, + pos[id],(float)pos[id]/18747.0f, + num[id],(float)num[id]/23.976f + ); + pos[id]+=idx[i].dwChunkLength; + ++num[id]; + } +} + +void print_avistdindex_chunk(avistdindex_chunk *h, int verbose_level){ + mp_msg (MSGT_HEADER, verbose_level, "====== AVI Standard Index Header ========\n"); + mp_msg (MSGT_HEADER, verbose_level, " FCC (%.4s) dwSize (%d) wLongsPerEntry(%d)\n", h->fcc, h->dwSize, h->wLongsPerEntry); + mp_msg (MSGT_HEADER, verbose_level, " bIndexSubType (%d) bIndexType (%d)\n", h->bIndexSubType, h->bIndexType); + mp_msg (MSGT_HEADER, verbose_level, " nEntriesInUse (%d) dwChunkId (%.4s)\n", h->nEntriesInUse, h->dwChunkId); + mp_msg (MSGT_HEADER, verbose_level, " qwBaseOffset (0x%"PRIX64") dwReserved3 (%d)\n", h->qwBaseOffset, h->dwReserved3); + mp_msg (MSGT_HEADER, verbose_level, "===========================\n"); +} +void print_avisuperindex_chunk(avisuperindex_chunk *h, int verbose_level){ + mp_msg (MSGT_HEADER, verbose_level, "====== AVI Super Index Header ========\n"); + mp_msg (MSGT_HEADER, verbose_level, " FCC (%.4s) dwSize (%d) wLongsPerEntry(%d)\n", h->fcc, h->dwSize, h->wLongsPerEntry); + mp_msg (MSGT_HEADER, verbose_level, " bIndexSubType (%d) bIndexType (%d)\n", h->bIndexSubType, h->bIndexType); + mp_msg (MSGT_HEADER, verbose_level, " nEntriesInUse (%d) dwChunkId (%.4s)\n", h->nEntriesInUse, h->dwChunkId); + mp_msg (MSGT_HEADER, verbose_level, " dwReserved[0] (%d) dwReserved[1] (%d) dwReserved[2] (%d)\n", + h->dwReserved[0], h->dwReserved[1], h->dwReserved[2]); + mp_msg (MSGT_HEADER, verbose_level, "===========================\n"); +} diff --git a/demux/aviprint.h b/demux/aviprint.h new file mode 100644 index 0000000000..86123b7725 --- /dev/null +++ b/demux/aviprint.h @@ -0,0 +1,35 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AVIPRINT_H +#define MPLAYER_AVIPRINT_H + +#include "ms_hdr.h" +#include "aviheader.h" + +void print_avih_flags(MainAVIHeader *h, int verbose_level); +void print_avih(MainAVIHeader *h, int verbose_level); +void print_strh(AVIStreamHeader *h, int verbose_level); +void print_wave_header(WAVEFORMATEX *h, int verbose_level); +void print_video_header(BITMAPINFOHEADER *h, int verbose_level); +void print_vprp(VideoPropHeader *vprp, int verbose_level); +void print_index(AVIINDEXENTRY *idx, int idx_size, int verbose_level); +void print_avistdindex_chunk(avistdindex_chunk *h, int verbose_level); +void print_avisuperindex_chunk(avisuperindex_chunk *h, int verbose_level); + +#endif /* MPLAYER_AVIPRINT_H */ diff --git a/demux/demux.c b/demux/demux.c new file mode 100644 index 0000000000..3673fd06e3 --- /dev/null +++ b/demux/demux.c @@ -0,0 +1,1435 @@ +/* + * DEMUXER v2.5 + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "config.h" +#include "options.h" +#include "talloc.h" +#include "mp_msg.h" +#include "m_config.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "mf.h" + +#include "libaf/format.h" + +#include "libavcodec/avcodec.h" +#if MP_INPUT_BUFFER_PADDING_SIZE < FF_INPUT_BUFFER_PADDING_SIZE +#error MP_INPUT_BUFFER_PADDING_SIZE is too small! +#endif + +static void clear_parser(sh_common_t *sh); + +// Demuxer list +extern const struct demuxer_desc demuxer_desc_edl; +extern const struct demuxer_desc demuxer_desc_cue; +extern const demuxer_desc_t demuxer_desc_rawaudio; +extern const demuxer_desc_t demuxer_desc_rawvideo; +extern const demuxer_desc_t demuxer_desc_tv; +extern const demuxer_desc_t demuxer_desc_mf; +extern const demuxer_desc_t demuxer_desc_avi; +extern const demuxer_desc_t demuxer_desc_asf; +extern const demuxer_desc_t demuxer_desc_matroska; +extern const demuxer_desc_t demuxer_desc_gif; +extern const demuxer_desc_t demuxer_desc_lavf; +extern const demuxer_desc_t demuxer_desc_lavf_preferred; +extern const demuxer_desc_t demuxer_desc_mng; +extern const demuxer_desc_t demuxer_desc_mpeg_ps; +extern const demuxer_desc_t demuxer_desc_mpeg_pes; +extern const demuxer_desc_t demuxer_desc_mpeg_gxf; +extern const demuxer_desc_t demuxer_desc_mpeg_es; +extern const demuxer_desc_t demuxer_desc_mpeg4_es; +extern const demuxer_desc_t demuxer_desc_h264_es; +extern const demuxer_desc_t demuxer_desc_mpeg_ts; + +/* Please do not add any new demuxers here. If you want to implement a new + * demuxer, add it to libavformat, except for wrappers around external + * libraries and demuxers requiring binary support. */ + +const demuxer_desc_t *const demuxer_list[] = { + &demuxer_desc_edl, + &demuxer_desc_cue, + &demuxer_desc_rawaudio, + &demuxer_desc_rawvideo, +#ifdef CONFIG_TV + &demuxer_desc_tv, +#endif + &demuxer_desc_mf, + &demuxer_desc_lavf_preferred, + &demuxer_desc_avi, + &demuxer_desc_asf, + &demuxer_desc_matroska, +#ifdef CONFIG_GIF + &demuxer_desc_gif, +#endif + &demuxer_desc_lavf, +#ifdef CONFIG_MNG + &demuxer_desc_mng, +#endif + &demuxer_desc_mpeg_ps, + &demuxer_desc_mpeg_pes, + &demuxer_desc_mpeg_gxf, + &demuxer_desc_mpeg_es, + &demuxer_desc_mpeg4_es, + &demuxer_desc_h264_es, + &demuxer_desc_mpeg_ts, + /* Please do not add any new demuxers here. If you want to implement a new + * demuxer, add it to libavformat, except for wrappers around external + * libraries and demuxers requiring binary support. */ + NULL +}; + +static struct demux_packet *create_packet(size_t len) +{ + if (len > 1000000000) { + mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Attempt to allocate demux packet " + "over 1 GB!\n"); + abort(); + } + struct demux_packet *dp = malloc(sizeof(struct demux_packet)); + dp->len = len; + dp->next = NULL; + dp->pts = MP_NOPTS_VALUE; + dp->duration = -1; + dp->stream_pts = MP_NOPTS_VALUE; + dp->pos = 0; + dp->keyframe = false; + dp->refcount = 1; + dp->master = NULL; + dp->buffer = NULL; + dp->avpacket = NULL; + return dp; +} + +struct demux_packet *new_demux_packet(size_t len) +{ + struct demux_packet *dp = create_packet(len); + dp->buffer = malloc(len + MP_INPUT_BUFFER_PADDING_SIZE); + if (!dp->buffer) { + mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Memory allocation failure!\n"); + abort(); + } + memset(dp->buffer + len, 0, 8); + return dp; +} + +// data must already have suitable padding +struct demux_packet *new_demux_packet_fromdata(void *data, size_t len) +{ + struct demux_packet *dp = create_packet(len); + dp->buffer = data; + return dp; +} + +void resize_demux_packet(struct demux_packet *dp, size_t len) +{ + if (len > 1000000000) { + mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Attempt to realloc demux packet " + "over 1 GB!\n"); + abort(); + } + dp->buffer = realloc(dp->buffer, len + MP_INPUT_BUFFER_PADDING_SIZE); + if (!dp->buffer) { + mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Memory allocation failure!\n"); + abort(); + } + memset(dp->buffer + len, 0, 8); + dp->len = len; +} + +struct demux_packet *clone_demux_packet(struct demux_packet *pack) +{ + struct demux_packet *dp = malloc(sizeof(struct demux_packet)); + while (pack->master) + pack = pack->master; // find the master + memcpy(dp, pack, sizeof(struct demux_packet)); + dp->next = NULL; + dp->refcount = 0; + dp->master = pack; + pack->refcount++; + return dp; +} + +void free_demux_packet(struct demux_packet *dp) +{ + if (dp->master == NULL) { //dp is a master packet + dp->refcount--; + if (dp->refcount == 0) { + if (dp->avpacket) + talloc_free(dp->avpacket); + else + free(dp->buffer); + free(dp); + } + return; + } + // dp is a clone: + free_demux_packet(dp->master); + free(dp); +} + +static void free_demuxer_stream(struct demux_stream *ds) +{ + ds_free_packs(ds); + free(ds); +} + +static struct demux_stream *new_demuxer_stream(struct demuxer *demuxer, + enum stream_type type, int id) +{ + demux_stream_t *ds = malloc(sizeof(demux_stream_t)); + *ds = (demux_stream_t) { + .stream_type = type, + .id = id, + .demuxer = demuxer, + .asf_seq = -1, + }; + return ds; +} + +struct sh_stream *ds_gsh(struct demux_stream *ds) +{ + // Ideally ds would have a gsh field, but since all the old demuxers set + // ds->sh themselves and we don't want to change them, enjoy this hack. + if (!ds->sh) + return NULL; + switch (ds->stream_type) { + case STREAM_VIDEO: return ((struct sh_video *)ds->sh)->gsh; + case STREAM_AUDIO: return ((struct sh_audio *)ds->sh)->gsh; + case STREAM_SUB: return ((struct sh_sub *)ds->sh)->gsh; + } + assert(false); +} + +/** + * Get demuxer description structure for a given demuxer type + * + * @param file_format type of the demuxer + * @return structure for the demuxer, NULL if not found + */ +static const demuxer_desc_t *get_demuxer_desc_from_type(int file_format) +{ + int i; + + for (i = 0; demuxer_list[i]; i++) + if (file_format == demuxer_list[i]->type) + return demuxer_list[i]; + + return NULL; +} + + +demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type, + int a_id, int v_id, int s_id, char *filename) +{ + struct demuxer *d = talloc_zero(NULL, struct demuxer); + d->stream = stream; + d->stream_pts = MP_NOPTS_VALUE; + d->reference_clock = MP_NOPTS_VALUE; + d->movi_start = stream->start_pos; + d->movi_end = stream->end_pos; + d->seekable = 1; + d->synced = 0; + d->filepos = -1; + d->audio = new_demuxer_stream(d, STREAM_VIDEO, a_id); + d->video = new_demuxer_stream(d, STREAM_AUDIO, v_id); + d->sub = new_demuxer_stream(d, STREAM_SUB, s_id); + d->ds[STREAM_VIDEO] = d->video; + d->ds[STREAM_AUDIO] = d->audio; + d->ds[STREAM_SUB] = d->sub; + d->type = type; + d->opts = opts; + if (type) + if (!(d->desc = get_demuxer_desc_from_type(type))) + mp_msg(MSGT_DEMUXER, MSGL_ERR, + "BUG! Invalid demuxer type in new_demuxer(), " + "big troubles ahead.\n"); + if (filename) // Filename hack for avs_check_file + d->filename = strdup(filename); + stream_seek(stream, stream->start_pos); + return d; +} + +const char *sh_sub_type2str(int type) +{ + switch (type) { + case 't': return "text"; + case 'm': return "movtext"; + case 'a': return "ass"; + case 'v': return "vobsub"; + case 'x': return "xsub"; + case 'b': return "dvb"; + case 'd': return "dvb-teletext"; + case 'p': return "hdmv pgs"; + } + return "unknown"; +} + +static struct sh_stream *new_sh_stream(demuxer_t *demuxer, + enum stream_type type, + int stream_index, + int tid) +{ + struct sh_stream *sh = talloc_struct(demuxer, struct sh_stream, { + .type = type, + .demuxer = demuxer, + .index = demuxer->num_streams, + .demuxer_id = tid, // may be overwritten by demuxer + .tid = tid, + .stream_index = stream_index, + .opts = demuxer->opts, + }); + MP_TARRAY_APPEND(demuxer, demuxer->streams, demuxer->num_streams, sh); + switch (sh->type) { + case STREAM_VIDEO: { + struct sh_video *sht = talloc_zero(demuxer, struct sh_video); + sht->vid = sh->tid; + sht->ds = demuxer->video; + sh->video = sht; + sh->common_header = (struct sh_common *) sht; + demuxer->v_streams[sh->stream_index] = sht; + break; + } + case STREAM_AUDIO: { + struct sh_audio *sht = talloc_zero(demuxer, struct sh_audio); + sht->aid = tid; + sht->ds = demuxer->audio; + sht->samplesize = 2; + sht->sample_format = AF_FORMAT_S16_NE; + sh->audio = sht; + sh->common_header = (struct sh_common *) sht; + demuxer->a_streams[sh->stream_index] = sht; + break; + } + case STREAM_SUB: { + struct sh_sub *sht = talloc_zero(demuxer, struct sh_sub); + sht->sid = tid; + sht->ds = demuxer->sub; + sh->sub = sht; + sh->common_header = (struct sh_common *) sht; + demuxer->s_streams[sh->stream_index] = sht; + break; + } + default: assert(false); + } + sh->common_header->opts = sh->opts; + sh->common_header->gsh = sh; + return sh; +} + +sh_sub_t *new_sh_sub_sid(demuxer_t *demuxer, int id, int sid) +{ + if (id > MAX_S_STREAMS - 1 || id < 0) { + mp_msg(MSGT_DEMUXER, MSGL_WARN, + "Requested sub stream id overflow (%d > %d)\n", id, + MAX_S_STREAMS); + return NULL; + } + if (demuxer->s_streams[id]) + mp_msg(MSGT_DEMUXER, MSGL_WARN, "Sub stream %i redefined\n", id); + else { + new_sh_stream(demuxer, STREAM_SUB, id, sid); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SUBTITLE_ID=%d\n", sid); + } + return demuxer->s_streams[id]; +} + +struct sh_sub *new_sh_sub_sid_lang(struct demuxer *demuxer, int id, int sid, + const char *lang) +{ + struct sh_sub *sh = new_sh_sub_sid(demuxer, id, sid); + if (lang && lang[0] && strcmp(lang, "und")) { + sh->lang = talloc_strdup(sh, lang); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_LANG=%s\n", sid, lang); + } + return sh; +} + +static void free_sh_sub(sh_sub_t *sh) +{ + mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_sub at %p\n", sh); + free(sh->extradata); + clear_parser((sh_common_t *)sh); + talloc_free(sh); +} + +sh_audio_t *new_sh_audio_aid(demuxer_t *demuxer, int id, int aid) +{ + if (id > MAX_A_STREAMS - 1 || id < 0) { + mp_msg(MSGT_DEMUXER, MSGL_WARN, + "Requested audio stream id overflow (%d > %d)\n", id, + MAX_A_STREAMS); + return NULL; + } + if (demuxer->a_streams[id]) { + mp_tmsg(MSGT_DEMUXER, MSGL_WARN, "WARNING: Audio stream header %d redefined.\n", id); + } else { + mp_tmsg(MSGT_DEMUXER, MSGL_V, "==> Found audio stream: %d\n", id); + new_sh_stream(demuxer, STREAM_AUDIO, id, aid); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_ID=%d\n", aid); + } + return demuxer->a_streams[id]; +} + +static void free_sh_audio(demuxer_t *demuxer, int id) +{ + sh_audio_t *sh = demuxer->a_streams[id]; + demuxer->a_streams[id] = NULL; + mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_audio at %p\n", sh); + free(sh->wf); + free(sh->codecdata); + clear_parser((sh_common_t *)sh); + talloc_free(sh); +} + +sh_video_t *new_sh_video_vid(demuxer_t *demuxer, int id, int vid) +{ + if (id > MAX_V_STREAMS - 1 || id < 0) { + mp_msg(MSGT_DEMUXER, MSGL_WARN, + "Requested video stream id overflow (%d > %d)\n", id, + MAX_V_STREAMS); + return NULL; + } + if (demuxer->v_streams[id]) + mp_tmsg(MSGT_DEMUXER, MSGL_WARN, "WARNING: Video stream header %d redefined.\n", id); + else { + mp_tmsg(MSGT_DEMUXER, MSGL_V, "==> Found video stream: %d\n", id); + new_sh_stream(demuxer, STREAM_VIDEO, id, vid); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_ID=%d\n", vid); + } + return demuxer->v_streams[id]; +} + +static void free_sh_video(sh_video_t *sh) +{ + mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_video at %p\n", sh); + free(sh->bih); + clear_parser((sh_common_t *)sh); + talloc_free(sh); +} + +void free_demuxer(demuxer_t *demuxer) +{ + int i; + mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing %s demuxer at %p\n", + demuxer->desc->shortdesc, demuxer); + if (demuxer->desc->close) + demuxer->desc->close(demuxer); + // free streams: + for (i = 0; i < MAX_A_STREAMS; i++) + if (demuxer->a_streams[i]) + free_sh_audio(demuxer, i); + for (i = 0; i < MAX_V_STREAMS; i++) + if (demuxer->v_streams[i]) + free_sh_video(demuxer->v_streams[i]); + for (i = 0; i < MAX_S_STREAMS; i++) + if (demuxer->s_streams[i]) + free_sh_sub(demuxer->s_streams[i]); + // free demuxers: + free_demuxer_stream(demuxer->audio); + free_demuxer_stream(demuxer->video); + free_demuxer_stream(demuxer->sub); + free(demuxer->filename); + talloc_free(demuxer); +} + + +void ds_add_packet(demux_stream_t *ds, demux_packet_t *dp) +{ + // append packet to DS stream: + ++ds->packs; + ds->bytes += dp->len; + if (ds->last) { + // next packet in stream + ds->last->next = dp; + ds->last = dp; + } else { + // first packet in stream + ds->first = ds->last = dp; + } + mp_dbg(MSGT_DEMUXER, MSGL_DBG2, + "DEMUX: Append packet to %s, len=%d pts=%5.3f pos=%u [packs: A=%d V=%d]\n", + (ds == ds->demuxer->audio) ? "d_audio" : "d_video", dp->len, + dp->pts, (unsigned int) dp->pos, ds->demuxer->audio->packs, + ds->demuxer->video->packs); +} + +static void allocate_parser(AVCodecContext **avctx, AVCodecParserContext **parser, unsigned format) +{ + enum CodecID codec_id = CODEC_ID_NONE; + + switch (format) { + case MKTAG('M', 'P', '4', 'L'): + codec_id = CODEC_ID_AAC_LATM; + break; + case 0x2000: + case 0x332D6361: + case 0x332D4341: + case 0x20736D: + case MKTAG('s', 'a', 'c', '3'): + codec_id = CODEC_ID_AC3; + break; + case MKTAG('d', 'n', 'e', 't'): + // DNET/byte-swapped AC-3 - there is no parser for that yet + //codec_id = CODEC_ID_DNET; + break; + case MKTAG('E', 'A', 'C', '3'): + codec_id = CODEC_ID_EAC3; + break; + case 0x2001: + case 0x86: + codec_id = CODEC_ID_DTS; + break; + case MKTAG('f', 'L', 'a', 'C'): + codec_id = CODEC_ID_FLAC; + break; + case MKTAG('M', 'L', 'P', ' '): + codec_id = CODEC_ID_MLP; + break; + case 0x55: + case 0x5500736d: + case 0x55005354: + case MKTAG('.', 'm', 'p', '3'): + case MKTAG('M', 'P', '3', ' '): + case MKTAG('L', 'A', 'M', 'E'): + codec_id = CODEC_ID_MP3; + break; + case 0x50: + case 0x5000736d: + case MKTAG('.', 'm', 'p', '2'): + case MKTAG('.', 'm', 'p', '1'): + codec_id = CODEC_ID_MP2; + break; + case MKTAG('T', 'R', 'H', 'D'): + codec_id = CODEC_ID_TRUEHD; + break; + } + if (codec_id != CODEC_ID_NONE) { + *avctx = avcodec_alloc_context3(NULL); + if (!*avctx) + return; + *parser = av_parser_init(codec_id); + if (!*parser) + av_freep(avctx); + } +} + +static void get_parser(sh_common_t *sh, AVCodecContext **avctx, AVCodecParserContext **parser) +{ + *avctx = NULL; + *parser = NULL; + + if (!sh || !sh->needs_parsing) + return; + + *avctx = sh->avctx; + *parser = sh->parser; + if (*parser) + return; + + allocate_parser(avctx, parser, sh->format); + sh->avctx = *avctx; + sh->parser = *parser; +} + +int ds_parse(demux_stream_t *ds, uint8_t **buffer, int *len, double pts, off_t pos) +{ + AVCodecContext *avctx; + AVCodecParserContext *parser; + get_parser(ds->sh, &avctx, &parser); + if (!parser) + return *len; + return av_parser_parse2(parser, avctx, buffer, len, *buffer, *len, pts, pts, pos); +} + +static void clear_parser(sh_common_t *sh) +{ + av_parser_close(sh->parser); + sh->parser = NULL; + av_freep(&sh->avctx); +} + +void ds_clear_parser(demux_stream_t *ds) +{ + if (!ds->sh) + return; + clear_parser(ds->sh); +} + +void ds_read_packet(demux_stream_t *ds, stream_t *stream, int len, + double pts, off_t pos, bool keyframe) +{ + demux_packet_t *dp = new_demux_packet(len); + len = stream_read(stream, dp->buffer, len); + resize_demux_packet(dp, len); + dp->pts = pts; + dp->pos = pos; + dp->keyframe = keyframe; + // append packet to DS stream: + ds_add_packet(ds, dp); +} + +// return value: +// 0 = EOF or no stream found or invalid type +// 1 = successfully read a packet + +int demux_fill_buffer(demuxer_t *demux, demux_stream_t *ds) +{ + // Note: parameter 'ds' can be NULL! + return demux->desc->fill_buffer(demux, ds); +} + +// return value: +// 0 = EOF +// 1 = successful +int ds_fill_buffer(demux_stream_t *ds) +{ + demuxer_t *demux = ds->demuxer; + if (ds->current) + free_demux_packet(ds->current); + ds->current = NULL; + mp_dbg(MSGT_DEMUXER, MSGL_DBG3, "ds_fill_buffer (%s) called\n", + ds == demux->audio ? "d_audio" : ds == demux->video ? "d_video" : + ds == demux->sub ? "d_sub" : "unknown"); + while (1) { + if (ds->packs) { + demux_packet_t *p = ds->first; + // copy useful data: + ds->buffer = p->buffer; + ds->buffer_pos = 0; + ds->buffer_size = p->len; + ds->pos = p->pos; + ds->dpos += p->len; // !!! + ++ds->pack_no; + if (p->pts != MP_NOPTS_VALUE) { + ds->pts = p->pts; + ds->pts_bytes = 0; + } + ds->pts_bytes += p->len; // !!! + if (p->stream_pts != MP_NOPTS_VALUE) + demux->stream_pts = p->stream_pts; + ds->keyframe = p->keyframe; + // unlink packet: + ds->bytes -= p->len; + ds->current = p; + ds->first = p->next; + if (!ds->first) + ds->last = NULL; + --ds->packs; + /* The code below can set ds->eof to 1 when another stream runs + * out of buffer space. That makes sense because in that situation + * the calling code should not count on being able to demux more + * packets from this stream. + * If however the situation improves and we're called again + * despite the eof flag then it's better to clear it to avoid + * weird behavior. */ + ds->eof = 0; + return 1; + } + +#define MaybeNI _("Maybe you are playing a non-interleaved stream/file or the codec failed?\n" \ + "For AVI files, try to force non-interleaved mode with the --demuxer=avi --avi-ni options.\n") + + if (demux->audio->packs >= MAX_PACKS + || demux->audio->bytes >= MAX_PACK_BYTES) { + mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many audio packets in the buffer: (%d in %d bytes).\n", + demux->audio->packs, demux->audio->bytes); + mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI); + break; + } + if (demux->video->packs >= MAX_PACKS + || demux->video->bytes >= MAX_PACK_BYTES) { + mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many video packets in the buffer: (%d in %d bytes).\n", + demux->video->packs, demux->video->bytes); + mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI); + break; + } + if (!demux_fill_buffer(demux, ds)) { + mp_dbg(MSGT_DEMUXER, MSGL_DBG2, + "ds_fill_buffer()->demux_fill_buffer() failed\n"); + break; // EOF + } + } + ds->buffer_pos = ds->buffer_size = 0; + ds->buffer = NULL; + mp_msg(MSGT_DEMUXER, MSGL_V, + "ds_fill_buffer: EOF reached (stream: %s) \n", + ds == demux->audio ? "audio" : "video"); + ds->eof = 1; + return 0; +} + +int demux_read_data(demux_stream_t *ds, unsigned char *mem, int len) +{ + int x; + int bytes = 0; + while (len > 0) { + x = ds->buffer_size - ds->buffer_pos; + if (x == 0) { + if (!ds_fill_buffer(ds)) + return bytes; + } else { + if (x > len) + x = len; + if (mem) + memcpy(mem + bytes, &ds->buffer[ds->buffer_pos], x); + bytes += x; + len -= x; + ds->buffer_pos += x; + } + } + return bytes; +} + +/** + * \brief read data until the given 3-byte pattern is encountered, up to maxlen + * \param mem memory to read data into, may be NULL to discard data + * \param maxlen maximum number of bytes to read + * \param read number of bytes actually read + * \param pattern pattern to search for (lowest 8 bits are ignored) + * \return whether pattern was found + */ +int demux_pattern_3(demux_stream_t *ds, unsigned char *mem, int maxlen, + int *read, uint32_t pattern) +{ + register uint32_t head = 0xffffff00; + register uint32_t pat = pattern & 0xffffff00; + int total_len = 0; + do { + register unsigned char *ds_buf = &ds->buffer[ds->buffer_size]; + int len = ds->buffer_size - ds->buffer_pos; + register long pos = -len; + if (unlikely(pos >= 0)) { // buffer is empty + ds_fill_buffer(ds); + continue; + } + do { + head |= ds_buf[pos]; + head <<= 8; + } while (++pos && head != pat); + len += pos; + if (total_len + len > maxlen) + len = maxlen - total_len; + len = demux_read_data(ds, mem ? &mem[total_len] : NULL, len); + total_len += len; + } while ((head != pat || total_len < 3) && total_len < maxlen && !ds->eof); + if (read) + *read = total_len; + return total_len >= 3 && head == pat; +} + +void ds_free_packs(demux_stream_t *ds) +{ + demux_packet_t *dp = ds->first; + while (dp) { + demux_packet_t *dn = dp->next; + free_demux_packet(dp); + dp = dn; + } + if (ds->asf_packet) { + // free unfinished .asf fragments: + free(ds->asf_packet->buffer); + free(ds->asf_packet); + ds->asf_packet = NULL; + } + ds->first = ds->last = NULL; + ds->packs = 0; // !!!!! + ds->bytes = 0; + if (ds->current) + free_demux_packet(ds->current); + ds->current = NULL; + ds->buffer = NULL; + ds->buffer_pos = ds->buffer_size; + ds->pts = MP_NOPTS_VALUE; + ds->pts_bytes = 0; +} + +int ds_get_packet(demux_stream_t *ds, unsigned char **start) +{ + int len; + if (ds->buffer_pos >= ds->buffer_size) { + if (!ds_fill_buffer(ds)) { + // EOF + *start = NULL; + return -1; + } + } + len = ds->buffer_size - ds->buffer_pos; + *start = &ds->buffer[ds->buffer_pos]; + ds->buffer_pos += len; + return len; +} + +int ds_get_packet_pts(demux_stream_t *ds, unsigned char **start, double *pts) +{ + int len; + *pts = MP_NOPTS_VALUE; + len = ds_get_packet(ds, start); + if (len < 0) + return len; + // Return pts unless this read starts from the middle of a packet + if (len == ds->buffer_pos) + *pts = ds->current->pts; + return len; +} + +int ds_get_packet_sub(demux_stream_t *ds, unsigned char **start) +{ + int len; + if (ds->buffer_pos >= ds->buffer_size) { + *start = NULL; + if (!ds->packs) + return -1; // no sub + if (!ds_fill_buffer(ds)) + return -1; // EOF + } + len = ds->buffer_size - ds->buffer_pos; + *start = &ds->buffer[ds->buffer_pos]; + ds->buffer_pos += len; + return len; +} + +struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last) +{ + // This shouldn't get used together with partial reads + assert(ds->buffer_pos == 0 || ds->buffer_pos >= ds->buffer_size); + if (!repeat_last) + ds_fill_buffer(ds); + ds->buffer_pos = ds->buffer_size; + return ds->current; +} + +double ds_get_next_pts(demux_stream_t *ds) +{ + demuxer_t *demux = ds->demuxer; + // if we have not read from the "current" packet, consider it + // as the next, otherwise we never get the pts for the first packet. + while (!ds->first && (!ds->current || ds->buffer_pos)) { + if (demux->audio->packs >= MAX_PACKS + || demux->audio->bytes >= MAX_PACK_BYTES) { + mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many audio packets in the buffer: (%d in %d bytes).\n", + demux->audio->packs, demux->audio->bytes); + mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI); + return MP_NOPTS_VALUE; + } + if (demux->video->packs >= MAX_PACKS + || demux->video->bytes >= MAX_PACK_BYTES) { + mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many video packets in the buffer: (%d in %d bytes).\n", + demux->video->packs, demux->video->bytes); + mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI); + return MP_NOPTS_VALUE; + } + if (!demux_fill_buffer(demux, ds)) + return MP_NOPTS_VALUE; + } + // take pts from "current" if we never read from it. + if (ds->current && !ds->buffer_pos) + return ds->current->pts; + return ds->first->pts; +} + +// ==================================================================== + +void demuxer_help(void) +{ + int i; + + mp_msg(MSGT_DEMUXER, MSGL_INFO, "Available demuxers:\n"); + mp_msg(MSGT_DEMUXER, MSGL_INFO, " demuxer: info: (comment)\n"); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DEMUXERS\n"); + for (i = 0; demuxer_list[i]; i++) { + if (demuxer_list[i]->type >= DEMUXER_TYPE_END) // internal type + continue; + if (demuxer_list[i]->comment && strlen(demuxer_list[i]->comment)) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "%10s %s (%s)\n", + demuxer_list[i]->name, demuxer_list[i]->info, + demuxer_list[i]->comment); + else + mp_msg(MSGT_DEMUXER, MSGL_INFO, "%10s %s\n", + demuxer_list[i]->name, demuxer_list[i]->info); + } +} + + +/** + * Get demuxer type for a given demuxer name + * + * @param demuxer_name string with demuxer name of demuxer number + * @param force will be set if demuxer should be forced. + * May be NULL. + * @return DEMUXER_TYPE_xxx, -1 if error or not found + */ +static int get_demuxer_type_from_name(char *demuxer_name, int *force) +{ + if (!demuxer_name || !demuxer_name[0]) + return DEMUXER_TYPE_UNKNOWN; + if (force) + *force = demuxer_name[0] == '+'; + if (demuxer_name[0] == '+') + demuxer_name = &demuxer_name[1]; + for (int i = 0; demuxer_list[i]; i++) { + if (demuxer_list[i]->type >= DEMUXER_TYPE_END) + // Can't select special demuxers from commandline + continue; + if (strcmp(demuxer_name, demuxer_list[i]->name) == 0) + return demuxer_list[i]->type; + } + + return -1; +} + +static struct demuxer *open_given_type(struct MPOpts *opts, + const struct demuxer_desc *desc, + struct stream *stream, bool force, + int audio_id, int video_id, int sub_id, + char *filename, + struct demuxer_params *params) +{ + struct demuxer *demuxer; + int fformat; + demuxer = new_demuxer(opts, stream, desc->type, audio_id, + video_id, sub_id, filename); + demuxer->params = params; + if (desc->check_file) + fformat = desc->check_file(demuxer); + else + fformat = desc->type; + if (force) + fformat = desc->type; + if (fformat == 0) + goto fail; + if (fformat == desc->type) { + if (demuxer->filetype) + mp_tmsg(MSGT_DEMUXER, MSGL_INFO, "Detected file format: %s (%s)\n", + demuxer->filetype, desc->shortdesc); + else + mp_tmsg(MSGT_DEMUXER, MSGL_INFO, "Detected file format: %s\n", + desc->shortdesc); + if (demuxer->desc->open) { + struct demuxer *demux2 = demuxer->desc->open(demuxer); + if (!demux2) { + mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "Opening as detected format " + "\"%s\" failed.\n", desc->shortdesc); + goto fail; + } + /* At least demux_mov can return a demux_demuxers instance + * from open() instead of the original fed in. */ + demuxer = demux2; + } + demuxer->file_format = fformat; + opts->correct_pts = opts->user_correct_pts; + if (opts->correct_pts < 0) + opts->correct_pts = + demux_control(demuxer, DEMUXER_CTRL_CORRECT_PTS, + NULL) == DEMUXER_CTRL_OK; + return demuxer; + } else { + // demux_mov can return playlist instead of mov + if (fformat == DEMUXER_TYPE_PLAYLIST) + return demuxer; // handled in mplayer.c + /* Internal MPEG PS demuxer check can return other MPEG subtypes + * which don't have their own checks; recurse to try opening as + * the returned type instead. */ + free_demuxer(demuxer); + desc = get_demuxer_desc_from_type(fformat); + if (!desc) { + mp_msg(MSGT_DEMUXER, MSGL_ERR, + "BUG: recursion to nonexistent file format\n"); + return NULL; + } + return open_given_type(opts, desc, stream, false, audio_id, + video_id, sub_id, filename, params); + } + fail: + free_demuxer(demuxer); + return NULL; +} + +struct demuxer *demux_open_withparams(struct MPOpts *opts, + struct stream *stream, int file_format, + char *force_format, int audio_id, + int video_id, int sub_id, char *filename, + struct demuxer_params *params) +{ + struct demuxer *demuxer = NULL; + const struct demuxer_desc *desc; + + int force = 0; + int demuxer_type; + if ((demuxer_type = get_demuxer_type_from_name(force_format, &force)) < 0) { + mp_msg(MSGT_DEMUXER, MSGL_ERR, "Demuxer %s does not exist.\n", + force_format); + return NULL; + } + if (demuxer_type) + file_format = demuxer_type; + + // Some code (e.g. dvd stuff, network code, or extension.c) explicitly + // request certain file formats. The list of formats are always handled by + // libavformat. + // Maybe attempts should be made to convert the mplayer format to the libav + // format, instead of reyling on libav to auto-detect the stream's format + // correctly. + switch (file_format) { + //case DEMUXER_TYPE_MPEG_PS: + //case DEMUXER_TYPE_MPEG_TS: + case DEMUXER_TYPE_Y4M: + case DEMUXER_TYPE_NSV: + case DEMUXER_TYPE_AAC: + case DEMUXER_TYPE_MPC: + file_format = DEMUXER_TYPE_LAVF; + } + + // If somebody requested a demuxer check it + if (file_format) { + desc = get_demuxer_desc_from_type(file_format); + if (!desc) + // should only happen with obsolete -demuxer 99 numeric format + return NULL; + return open_given_type(opts, desc, stream, force, audio_id, + video_id, sub_id, filename, params); + } + + // Test demuxers with safe file checks + for (int i = 0; (desc = demuxer_list[i]); i++) { + if (desc->safe_check) { + demuxer = open_given_type(opts, desc, stream, false, audio_id, + video_id, sub_id, filename, params); + if (demuxer) + return demuxer; + } + } + + // Ok. We're over the stable detectable fileformats, the next ones are + // a bit fuzzy. So by default (extension_parsing==1) try extension-based + // detection first: + if (filename && opts->extension_parsing == 1) { + desc = get_demuxer_desc_from_type(demuxer_type_by_filename(filename)); + if (desc) + demuxer = open_given_type(opts, desc, stream, false, audio_id, + video_id, sub_id, filename, params); + if (demuxer) + return demuxer; + } + + // Finally try detection for demuxers with unsafe checks + for (int i = 0; (desc = demuxer_list[i]); i++) { + if (!desc->safe_check && desc->check_file) { + demuxer = open_given_type(opts, desc, stream, false, audio_id, + video_id, sub_id, filename, params); + if (demuxer) + return demuxer; + } + } + + return NULL; +} + +struct demuxer *demux_open(struct MPOpts *opts, stream_t *vs, int file_format, + int audio_id, int video_id, int sub_id, + char *filename) +{ + return demux_open_withparams(opts, vs, file_format, opts->demuxer_name, + audio_id, video_id, sub_id, filename, NULL); +} + +void demux_flush(demuxer_t *demuxer) +{ + ds_free_packs(demuxer->video); + ds_free_packs(demuxer->audio); + ds_free_packs(demuxer->sub); +} + +int demux_seek(demuxer_t *demuxer, float rel_seek_secs, float audio_delay, + int flags) +{ + if (!demuxer->seekable) { + if (demuxer->file_format == DEMUXER_TYPE_AVI) + mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in raw AVI streams. (Index required, try with the -idx switch.)\n"); +#ifdef CONFIG_TV + else if (demuxer->file_format == DEMUXER_TYPE_TV) + mp_tmsg(MSGT_SEEK, MSGL_WARN, "TV input is not seekable! (Seeking will probably be for changing channels ;)\n"); +#endif + else + mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in this file.\n"); + return 0; + } + // clear demux buffers: + demux_flush(demuxer); + demuxer->video->eof = 0; + demuxer->audio->eof = 0; + demuxer->sub->eof = 0; + + /* HACK: assume any demuxer used with these streams can cope with + * the stream layer suddenly seeking to a different position under it + * (nothing actually implements DEMUXER_CTRL_RESYNC now). + */ + struct stream *stream = demuxer->stream; + if (stream->type == STREAMTYPE_DVD) { + double pts; + + if (flags & SEEK_ABSOLUTE) + pts = 0.0f; + else { + if (demuxer->stream_pts == MP_NOPTS_VALUE) + goto dmx_seek; + pts = demuxer->stream_pts; + } + + if (flags & SEEK_FACTOR) { + double tmp = 0; + if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, + &tmp) == STREAM_UNSUPPORTED) + goto dmx_seek; + pts += tmp * rel_seek_secs; + } else + pts += rel_seek_secs; + + if (stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, &pts) + != STREAM_UNSUPPORTED) { + demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); + return 1; + } + } + + dmx_seek: + if (demuxer->desc->seek) + demuxer->desc->seek(demuxer, rel_seek_secs, audio_delay, flags); + + return 1; +} + +int demux_info_add(demuxer_t *demuxer, const char *opt, const char *param) +{ + return demux_info_add_bstr(demuxer, bstr0(opt), bstr0(param)); +} + +int demux_info_add_bstr(demuxer_t *demuxer, struct bstr opt, struct bstr param) +{ + char **info = demuxer->info; + int n = 0; + + + for (n = 0; info && info[2 * n] != NULL; n++) { + if (!bstrcasecmp(opt, bstr0(info[2*n]))) { + if (!bstrcmp(param, bstr0(info[2*n + 1]))) { + mp_msg(MSGT_DEMUX, MSGL_V, "Demuxer info %.*s set to unchanged value %.*s\n", + BSTR_P(opt), BSTR_P(param)); + return 0; + } + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "Demuxer info %.*s changed to %.*s\n", + BSTR_P(opt), BSTR_P(param)); + talloc_free(info[2*n + 1]); + info[2*n + 1] = talloc_strndup(demuxer->info, param.start, param.len); + return 0; + } + } + + info = demuxer->info = talloc_realloc(demuxer, info, char *, 2 * (n + 2)); + info[2*n] = talloc_strndup(demuxer->info, opt.start, opt.len); + info[2*n + 1] = talloc_strndup(demuxer->info, param.start, param.len); + memset(&info[2 * (n + 1)], 0, 2 * sizeof(char *)); + + return 1; +} + +int demux_info_print(demuxer_t *demuxer) +{ + char **info = demuxer->info; + int n; + + if (!info) + return 0; + + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "Clip info:\n"); + for (n = 0; info[2 * n] != NULL; n++) { + mp_msg(MSGT_DEMUX, MSGL_INFO, " %s: %s\n", info[2 * n], + info[2 * n + 1]); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CLIP_INFO_NAME%d=%s\n", n, + info[2 * n]); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CLIP_INFO_VALUE%d=%s\n", n, + info[2 * n + 1]); + } + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CLIP_INFO_N=%d\n", n); + + return 0; +} + +char *demux_info_get(demuxer_t *demuxer, const char *opt) +{ + int i; + char **info = demuxer->info; + + for (i = 0; info && info[2 * i] != NULL; i++) { + if (!strcasecmp(opt, info[2 * i])) + return info[2 * i + 1]; + } + + return NULL; +} + +int demux_control(demuxer_t *demuxer, int cmd, void *arg) +{ + + if (demuxer->desc->control) + return demuxer->desc->control(demuxer, cmd, arg); + + return DEMUXER_CTRL_NOTIMPL; +} + +struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d, + enum stream_type t, int id) +{ + for (int n = 0; n < d->num_streams; n++) { + struct sh_stream *s = d->streams[n]; + if (s->type == t && s->demuxer_id == id) + return d->streams[n]; + } + return NULL; +} + +void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type, + struct sh_stream *stream) +{ + assert(!stream || stream->type == type); + int index = stream ? stream->tid : -2; + if (type == STREAM_AUDIO) { + if (demux_control(demuxer, DEMUXER_CTRL_SWITCH_AUDIO, &index) + == DEMUXER_CTRL_NOTIMPL) + demuxer->audio->id = index; + } else if (type == STREAM_VIDEO) { + if (demux_control(demuxer, DEMUXER_CTRL_SWITCH_VIDEO, &index) + == DEMUXER_CTRL_NOTIMPL) + demuxer->video->id = index; + } else if (type == STREAM_SUB) { + int index2 = stream ? stream->stream_index : -2; + if (demuxer->ds[type]->id != index2) + ds_free_packs(demuxer->ds[type]); + demuxer->ds[type]->id = index2; + } + int new_id = demuxer->ds[type]->id; + void *new = NULL; + if (new_id >= 0) { + switch (type) { + case STREAM_VIDEO: new = demuxer->v_streams[new_id]; break; + case STREAM_AUDIO: new = demuxer->a_streams[new_id]; break; + case STREAM_SUB: new = demuxer->s_streams[new_id]; break; + } + } + demuxer->ds[type]->sh = new; +} + +int demuxer_add_attachment(demuxer_t *demuxer, struct bstr name, + struct bstr type, struct bstr data) +{ + if (!(demuxer->num_attachments % 32)) + demuxer->attachments = talloc_realloc(demuxer, demuxer->attachments, + struct demux_attachment, + demuxer->num_attachments + 32); + + struct demux_attachment *att = + demuxer->attachments + demuxer->num_attachments; + att->name = talloc_strndup(demuxer->attachments, name.start, name.len); + att->type = talloc_strndup(demuxer->attachments, type.start, type.len); + att->data = talloc_size(demuxer->attachments, data.len); + memcpy(att->data, data.start, data.len); + att->data_size = data.len; + + return demuxer->num_attachments++; +} + +static int chapter_compare(const void *p1, const void *p2) +{ + struct demux_chapter *c1 = (void *)p1; + struct demux_chapter *c2 = (void *)p2; + + if (c1->start > c2->start) + return 1; + else if (c1->start < c2->start) + return -1; + return 0; +} + +static void demuxer_sort_chapters(demuxer_t *demuxer) +{ + qsort(demuxer->chapters, demuxer->num_chapters, + sizeof(struct demux_chapter), chapter_compare); +} + +int demuxer_add_chapter(demuxer_t *demuxer, struct bstr name, + uint64_t start, uint64_t end) +{ + if (!(demuxer->num_chapters % 32)) + demuxer->chapters = talloc_realloc(demuxer, demuxer->chapters, + struct demux_chapter, + demuxer->num_chapters + 32); + + demuxer->chapters[demuxer->num_chapters].start = start; + demuxer->chapters[demuxer->num_chapters].end = end; + demuxer->chapters[demuxer->num_chapters].name = name.len ? + talloc_strndup(demuxer->chapters, name.start, name.len) : + talloc_strdup(demuxer->chapters, mp_gtext("unknown")); + + demuxer->num_chapters++; + + if (demuxer->num_chapters > 1 + && demuxer->chapters[demuxer->num_chapters - 2].start + < demuxer->chapters[demuxer->num_chapters - 1].start) + demuxer_sort_chapters(demuxer); + + return 0; +} + +/** + * \brief demuxer_seek_chapter() seeks to a chapter in two possible ways: + * either using the demuxer->chapters structure set by the demuxer + * or asking help to the stream layer (e.g. dvd) + * \param chapter - chapter number wished - 0-based + * \param seek_pts set by the function to the pts to seek to (if demuxer->chapters is set) + * \return -1 on error, current chapter if successful + */ + +int demuxer_seek_chapter(demuxer_t *demuxer, int chapter, double *seek_pts) +{ + int ris; + + if (!demuxer->num_chapters || !demuxer->chapters) { + demux_flush(demuxer); + + ris = stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_CHAPTER, + &chapter); + if (ris != STREAM_UNSUPPORTED) + demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); + + // exit status may be ok, but main() doesn't have to seek itself + // (because e.g. dvds depend on sectors, not on pts) + *seek_pts = -1.0; + + return ris != STREAM_UNSUPPORTED ? chapter : -1; + } else { // chapters structure is set in the demuxer + if (chapter >= demuxer->num_chapters) + return -1; + if (chapter < 0) + chapter = 0; + + *seek_pts = demuxer->chapters[chapter].start / 1e9; + + return chapter; + } +} + +int demuxer_get_current_chapter(demuxer_t *demuxer, double time_now) +{ + int chapter = -2; + if (!demuxer->num_chapters || !demuxer->chapters) { + if (stream_control(demuxer->stream, STREAM_CTRL_GET_CURRENT_CHAPTER, + &chapter) == STREAM_UNSUPPORTED) + chapter = -2; + } else { + uint64_t now = time_now * 1e9 + 0.5; + for (chapter = demuxer->num_chapters - 1; chapter >= 0; --chapter) { + if (demuxer->chapters[chapter].start <= now) + break; + } + } + return chapter; +} + +char *demuxer_chapter_name(demuxer_t *demuxer, int chapter) +{ + if (demuxer->num_chapters && demuxer->chapters) { + if (chapter >= 0 && chapter < demuxer->num_chapters + && demuxer->chapters[chapter].name) + return talloc_strdup(NULL, demuxer->chapters[chapter].name); + } + return NULL; +} + +float demuxer_chapter_time(demuxer_t *demuxer, int chapter, float *end) +{ + if (demuxer->num_chapters && demuxer->chapters && chapter >= 0 + && chapter < demuxer->num_chapters) { + if (end) + *end = demuxer->chapters[chapter].end / 1e9; + return demuxer->chapters[chapter].start / 1e9; + } + return -1.0; +} + +int demuxer_chapter_count(demuxer_t *demuxer) +{ + if (!demuxer->num_chapters || !demuxer->chapters) { + int num_chapters = 0; + if (stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS, + &num_chapters) == STREAM_UNSUPPORTED) + num_chapters = 0; + return num_chapters; + } else + return demuxer->num_chapters; +} + +int demuxer_angles_count(demuxer_t *demuxer) +{ + int ris, angles = -1; + + ris = stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_ANGLES, &angles); + if (ris == STREAM_UNSUPPORTED) + return -1; + return angles; +} + +int demuxer_get_current_angle(demuxer_t *demuxer) +{ + int ris, curr_angle = -1; + ris = stream_control(demuxer->stream, STREAM_CTRL_GET_ANGLE, &curr_angle); + if (ris == STREAM_UNSUPPORTED) + return -1; + return curr_angle; +} + + +int demuxer_set_angle(demuxer_t *demuxer, int angle) +{ + int ris, angles = -1; + + angles = demuxer_angles_count(demuxer); + if ((angles < 1) || (angle > angles)) + return -1; + + demux_flush(demuxer); + + ris = stream_control(demuxer->stream, STREAM_CTRL_SET_ANGLE, &angle); + if (ris == STREAM_UNSUPPORTED) + return -1; + + demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); + + return angle; +} diff --git a/demux/demux.h b/demux/demux.h new file mode 100644 index 0000000000..f44c728c1e --- /dev/null +++ b/demux/demux.h @@ -0,0 +1,414 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_DEMUXER_H +#define MPLAYER_DEMUXER_H + +#include <sys/types.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include "bstr.h" +#include "mpcommon.h" +#include "demux_packet.h" +#include "stheader.h" + +struct MPOpts; + +#ifdef HAVE_BUILTIN_EXPECT +#define likely(x) __builtin_expect((x) != 0, 1) +#define unlikely(x) __builtin_expect((x) != 0, 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#define MAX_PACKS 4096 +#define MAX_PACK_BYTES 0x8000000 // 128 MiB + +enum demuxer_type { + DEMUXER_TYPE_UNKNOWN = 0, + DEMUXER_TYPE_MPEG_PS, + DEMUXER_TYPE_AVI, + DEMUXER_TYPE_AVI_NI, + DEMUXER_TYPE_AVI_NINI, + DEMUXER_TYPE_ASF, + DEMUXER_TYPE_TV, + DEMUXER_TYPE_Y4M, + DEMUXER_TYPE_MF, + DEMUXER_TYPE_RAWAUDIO, + DEMUXER_TYPE_RAWVIDEO, + DEMUXER_TYPE_MPEG_ES, + DEMUXER_TYPE_MPEG4_ES, + DEMUXER_TYPE_H264_ES, + DEMUXER_TYPE_MPEG_PES, + DEMUXER_TYPE_MPEG_GXF, + DEMUXER_TYPE_GIF, + DEMUXER_TYPE_MPEG_TS, + DEMUXER_TYPE_MATROSKA, + DEMUXER_TYPE_LAVF, + DEMUXER_TYPE_NSV, + DEMUXER_TYPE_AVS, + DEMUXER_TYPE_AAC, + DEMUXER_TYPE_MPC, + DEMUXER_TYPE_LAVF_PREFERRED, + DEMUXER_TYPE_MNG, + DEMUXER_TYPE_EDL, + DEMUXER_TYPE_CUE, + + /* Values after this are for internal use and can not be selected + * as demuxer type by the user (-demuxer option). */ + DEMUXER_TYPE_END, + + DEMUXER_TYPE_PLAYLIST, +}; + +enum timestamp_type { + TIMESTAMP_TYPE_PTS, + TIMESTAMP_TYPE_SORT, +}; + + +// DEMUXER control commands/answers +#define DEMUXER_CTRL_NOTIMPL -1 +#define DEMUXER_CTRL_DONTKNOW 0 +#define DEMUXER_CTRL_OK 1 +#define DEMUXER_CTRL_GUESS 2 +#define DEMUXER_CTRL_GET_TIME_LENGTH 10 +#define DEMUXER_CTRL_GET_PERCENT_POS 11 +#define DEMUXER_CTRL_SWITCH_AUDIO 12 +#define DEMUXER_CTRL_RESYNC 13 +#define DEMUXER_CTRL_SWITCH_VIDEO 14 +#define DEMUXER_CTRL_IDENTIFY_PROGRAM 15 +#define DEMUXER_CTRL_CORRECT_PTS 16 +#define DEMUXER_CTRL_AUTOSELECT_SUBTITLE 17 + +#define SEEK_ABSOLUTE (1 << 0) +#define SEEK_FACTOR (1 << 1) +#define SEEK_FORWARD (1 << 2) +#define SEEK_BACKWARD (1 << 3) + +// demux_lavf can pass lavf buffers using FF_INPUT_BUFFER_PADDING_SIZE instead +#define MP_INPUT_BUFFER_PADDING_SIZE 16 + +typedef struct demux_stream { + enum stream_type stream_type; + int buffer_pos; // current buffer position + int buffer_size; // current buffer size + unsigned char *buffer; // current buffer, never free() it, always use free_demux_packet(buffer_ref); + double pts; // current buffer's pts + int pts_bytes; // number of bytes read after last pts stamp + int eof; // end of demuxed stream? (true if all buffer empty) + off_t pos; // position in the input stream (file) + off_t dpos; // position in the demuxed stream + int pack_no; // serial number of packet + bool keyframe; // keyframe flag of current packet +//--------------- + int packs; // number of packets in buffer + int bytes; // total bytes of packets in buffer + demux_packet_t *first; // read to current buffer from here + demux_packet_t *last; // append new packets from input stream to here + demux_packet_t *current; // needed for refcounting of the buffer + int id; // stream ID (for multiple audio/video streams) + struct demuxer *demuxer; // parent demuxer structure (stream handler) +// ---- asf ----- + struct demux_packet *asf_packet; // read asf fragments here + int asf_seq; +// ---- mov ----- + unsigned int ss_mul, ss_div; +// ---- stream header ---- + void *sh; // points to sh_audio or sh_video +} demux_stream_t; + +typedef struct demuxer_info { + char *name; + char *author; + char *encoder; + char *comments; + char *copyright; +} demuxer_info_t; + +#define MAX_SH_STREAMS 256 +#define MAX_A_STREAMS MAX_SH_STREAMS +#define MAX_V_STREAMS MAX_SH_STREAMS +#define MAX_S_STREAMS MAX_SH_STREAMS + +struct demuxer; + +/** + * Demuxer description structure + */ +typedef struct demuxer_desc { + const char *info; // What is it (long name and/or description) + const char *name; // Demuxer name, used with -demuxer switch + const char *shortdesc; // Description printed at demuxer detection + const char *author; // Demuxer author(s) + const char *comment; // Comment, printed with -demuxer help + + enum demuxer_type type; + // If 1 detection is safe and fast, do it before file extension check + int safe_check; + + // Check if can demux the file, return DEMUXER_TYPE_xxx on success + // Mandatory if safe_check == 1, else optional + int (*check_file)(struct demuxer *demuxer); + /// Get packets from file, return 0 on eof. Mandatory + int (*fill_buffer)(struct demuxer *demuxer, struct demux_stream *ds); + /// Open the demuxer, return demuxer on success, NULL on failure + struct demuxer *(*open)(struct demuxer *demuxer); // Optional + /// Close the demuxer + void (*close)(struct demuxer *demuxer); // Optional + // Seek. Optional + void (*seek)(struct demuxer *demuxer, float rel_seek_secs, + float audio_delay, int flags); + // Various control functions. Optional + int (*control)(struct demuxer *demuxer, int cmd, void *arg); +} demuxer_desc_t; + +typedef struct demux_chapter +{ + uint64_t start, end; + char *name; +} demux_chapter_t; + +struct matroska_data { + unsigned char segment_uid[16]; + // Ordered chapter information if any + struct matroska_chapter { + uint64_t start; + uint64_t end; + bool has_segment_uid; + unsigned char segment_uid[16]; + char *name; + } *ordered_chapters; + int num_ordered_chapters; +}; + +typedef struct demux_attachment +{ + char *name; + char *type; + void *data; + unsigned int data_size; +} demux_attachment_t; + +struct demuxer_params { + unsigned char (*matroska_wanted_uids)[16]; +}; + +typedef struct demuxer { + const demuxer_desc_t *desc; ///< Demuxer description structure + const char *filetype; // format name when not identified by demuxer (libavformat) + off_t filepos; // input stream current pos. + off_t movi_start; + off_t movi_end; + struct stream *stream; + double stream_pts; // current stream pts, if applicable (e.g. dvd) + double reference_clock; + char *filename; // Needed by avs_check_file + int synced; // stream synced (used by mpeg) + enum demuxer_type type; + /* Normally the file_format field is just a copy of the type field above. + * There are 2 exceptions I noticed. Internal demux_avi may force + * ->type to DEMUXER_TYPE_AVI_[NI|NINI] while leaving ->file_format at + * DEMUXER_TYPE_AVI. Internal demux_mov may set ->type to + * DEMUXER_TYPE_PLAYLIST and also return that from the check function + * or not (looks potentially buggy). */ + enum demuxer_type file_format; + int seekable; // flag + /* Set if using absolute seeks for small movements is OK (no pts resets + * that would make pts ambigious, preferably supports back/forward flags */ + bool accurate_seek; + enum timestamp_type timestamp_type; + + struct demux_stream *ds[STREAM_TYPE_COUNT]; // video/audio/sub buffers + + // These correspond to ds[], e.g.: audio == ds[STREAM_AUDIO] + struct demux_stream *audio; // audio buffer/demuxer + struct demux_stream *video; // video buffer/demuxer + struct demux_stream *sub; // dvd subtitle buffer/demuxer + + // stream headers: + struct sh_audio *a_streams[MAX_SH_STREAMS]; + struct sh_video *v_streams[MAX_SH_STREAMS]; + struct sh_sub *s_streams[MAX_SH_STREAMS]; + + struct sh_stream **streams; + int num_streams; + + int num_editions; + int edition; + + struct demux_chapter *chapters; + int num_chapters; + + struct demux_attachment *attachments; + int num_attachments; + + struct matroska_data matroska_data; + // for trivial demuxers which just read the whole file for codec to use + struct bstr file_contents; + + void *priv; // demuxer-specific internal data + char **info; // metadata + struct MPOpts *opts; + struct demuxer_params *params; +} demuxer_t; + +typedef struct { + int progid; //program id + int aid, vid, sid; //audio, video and subtitle id +} demux_program_t; + +struct demux_packet *new_demux_packet(size_t len); +// data must already have suitable padding +struct demux_packet *new_demux_packet_fromdata(void *data, size_t len); +void resize_demux_packet(struct demux_packet *dp, size_t len); +struct demux_packet *clone_demux_packet(struct demux_packet *pack); +void free_demux_packet(struct demux_packet *dp); + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif + +static inline void *realloc_struct(void *ptr, size_t nmemb, size_t size) +{ + if (nmemb > SIZE_MAX / size) { + free(ptr); + return NULL; + } + return realloc(ptr, nmemb * size); +} + +struct demuxer *new_demuxer(struct MPOpts *opts, struct stream *stream, + int type, int a_id, int v_id, int s_id, + char *filename); +void free_demuxer(struct demuxer *demuxer); + +struct sh_stream *ds_gsh(struct demux_stream *ds); + +void ds_add_packet(struct demux_stream *ds, struct demux_packet *dp); +void ds_read_packet(struct demux_stream *ds, struct stream *stream, int len, + double pts, off_t pos, bool keyframe); + +int demux_fill_buffer(struct demuxer *demux, struct demux_stream *ds); +int ds_fill_buffer(struct demux_stream *ds); + +static inline off_t ds_tell(struct demux_stream *ds) +{ + return (ds->dpos - ds->buffer_size) + ds->buffer_pos; +} + +static inline int ds_tell_pts(struct demux_stream *ds) +{ + return (ds->pts_bytes - ds->buffer_size) + ds->buffer_pos; +} + +int demux_read_data(struct demux_stream *ds, unsigned char *mem, int len); +int demux_pattern_3(struct demux_stream *ds, unsigned char *mem, int maxlen, + int *read, uint32_t pattern); + +#define demux_peekc(ds) ( \ + (likely(ds->buffer_pos<ds->buffer_size)) ? ds->buffer[ds->buffer_pos] \ + : ((unlikely(!ds_fill_buffer(ds))) ? (-1) : ds->buffer[ds->buffer_pos])) +#define demux_getc(ds) ( \ + (likely(ds->buffer_pos<ds->buffer_size)) ? ds->buffer[ds->buffer_pos++] \ + : ((unlikely(!ds_fill_buffer(ds))) ? (-1) : ds->buffer[ds->buffer_pos++])) + +void ds_free_packs(struct demux_stream *ds); +int ds_get_packet(struct demux_stream *ds, unsigned char **start); +int ds_get_packet_pts(struct demux_stream *ds, unsigned char **start, + double *pts); +int ds_get_packet_sub(struct demux_stream *ds, unsigned char **start); +struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last); +double ds_get_next_pts(struct demux_stream *ds); +int ds_parse(struct demux_stream *sh, uint8_t **buffer, int *len, double pts, + off_t pos); +void ds_clear_parser(struct demux_stream *sh); + +static inline int avi_stream_id(unsigned int id) +{ + unsigned char a, b; + a = id - '0'; + b = (id >> 8) - '0'; + if (a>9 || b>9) + return 100; // invalid ID + return a * 10 + b; +} + +struct demuxer *demux_open(struct MPOpts *opts, struct stream *stream, + int file_format, int aid, int vid, int sid, + char *filename); + +struct demuxer *demux_open_withparams(struct MPOpts *opts, + struct stream *stream, int file_format, + char *force_format, int audio_id, + int video_id, int sub_id, char *filename, + struct demuxer_params *params); + +void demux_flush(struct demuxer *demuxer); +int demux_seek(struct demuxer *demuxer, float rel_seek_secs, float audio_delay, + int flags); + +// AVI demuxer params: +extern int index_mode; // -1=untouched 0=don't use index 1=use (generate) index +extern int force_ni; +extern int pts_from_bps; + +int demux_info_add(struct demuxer *demuxer, const char *opt, const char *param); +int demux_info_add_bstr(struct demuxer *demuxer, struct bstr opt, + struct bstr param); +char *demux_info_get(struct demuxer *demuxer, const char *opt); +int demux_info_print(struct demuxer *demuxer); +int demux_control(struct demuxer *demuxer, int cmd, void *arg); + +void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type, + struct sh_stream *stream); + +int demuxer_type_by_filename(char *filename); + +void demuxer_help(void); + +int demuxer_add_attachment(struct demuxer *demuxer, struct bstr name, + struct bstr type, struct bstr data); +int demuxer_add_chapter(struct demuxer *demuxer, struct bstr name, + uint64_t start, uint64_t end); +int demuxer_seek_chapter(struct demuxer *demuxer, int chapter, + double *seek_pts); + +/// Get current chapter index if available. +int demuxer_get_current_chapter(struct demuxer *demuxer, double time_now); +/// Get chapter name by index if available. +char *demuxer_chapter_name(struct demuxer *demuxer, int chapter); +/// Get chapter start time and end time by index if available. +float demuxer_chapter_time(struct demuxer *demuxer, int chapter, float *end); +/// Get total chapter number. +int demuxer_chapter_count(struct demuxer *demuxer); +/// Get current angle index. +int demuxer_get_current_angle(struct demuxer *demuxer); +/// Set angle. +int demuxer_set_angle(struct demuxer *demuxer, int angle); +/// Get number of angles. +int demuxer_angles_count(struct demuxer *demuxer); + +struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d, + enum stream_type t, int id); + +#endif /* MPLAYER_DEMUXER_H */ diff --git a/demux/demux_asf.c b/demux/demux_asf.c new file mode 100644 index 0000000000..9d189ef095 --- /dev/null +++ b/demux/demux_asf.c @@ -0,0 +1,697 @@ +/* + * ASF file parser for DEMUXER v0.3 + * copyright (c) 2001 A'rpi/ESP-team + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#include <libavutil/intreadwrite.h> + +#include "config.h" +#include "mp_msg.h" + +#include "stream/stream.h" +#include "asf.h" +#include "asfheader.h" +#include "demuxer.h" +#include "libmpcodecs/dec_audio.h" + +// based on asf file-format doc by Eugene [http://divx.euro.ru] + +/** + * \brief reads int stored in number of bytes given by len + * \param ptr pointer to read from, is incremented appropriately + * \param len lowest 2 bits indicate number of bytes to read + * \param def default value to return if len is invalid + */ +static inline unsigned read_varlen(uint8_t **ptr, int len, int def) { + const uint8_t *p = *ptr; + len &= 3; + switch (len) { + case 1: *ptr += 1; return *p; + case 2: *ptr += 2; return AV_RL16(p); + case 3: *ptr += 4; return AV_RL32(p); + } + return def; +} + +/** + * \brief checks if there is enough data to read the bytes given by len + * \param ptr pointer to read from + * \param endptr pointer to the end of the buffer + * \param len lowest 2 bits indicate number of bytes to read + */ +static inline int check_varlen(uint8_t *ptr, uint8_t *endptr, int len) { + return len&3 ? ptr + (1<<((len&3) - 1)) <= endptr : 1; +} + +static void asf_descrambling(unsigned char **src,unsigned len, struct asf_priv* asf){ + unsigned char *dst; + unsigned char *s2=*src; + unsigned i=0,x,y; + if (len > UINT_MAX - MP_INPUT_BUFFER_PADDING_SIZE) + return; + dst = malloc(len + MP_INPUT_BUFFER_PADDING_SIZE); + while(len>=asf->scrambling_h*asf->scrambling_w*asf->scrambling_b+i){ +// mp_msg(MSGT_DEMUX,MSGL_DBG4,"descrambling! (w=%d b=%d)\n",w,asf_scrambling_b); + //i+=asf_scrambling_h*asf_scrambling_w; + for(x=0;x<asf->scrambling_w;x++) + for(y=0;y<asf->scrambling_h;y++){ + memcpy(dst+i,s2+(y*asf->scrambling_w+x)*asf->scrambling_b,asf->scrambling_b); + i+=asf->scrambling_b; + } + s2+=asf->scrambling_h*asf->scrambling_w*asf->scrambling_b; + } + //if(i<len) memcpy(dst+i,src+i,len-i); + free(*src); + *src = dst; +} + +/***************************************************************** + * \brief initializes asf private data + * + */ +static void init_priv (struct asf_priv* asf){ + asf->last_vid_seq=-1; + asf->vid_ext_timing_index=-1; + asf->aud_ext_timing_index=-1; + asf->vid_ext_frame_index=-1; +} + +static void demux_asf_append_to_packet(demux_packet_t* dp,unsigned char *data,int len,int offs) +{ + if(dp->len!=offs && offs!=-1) mp_msg(MSGT_DEMUX,MSGL_V,"warning! fragment.len=%d BUT next fragment offset=%d \n",dp->len,offs); + dp->buffer=realloc(dp->buffer,dp->len+len+MP_INPUT_BUFFER_PADDING_SIZE); + memcpy(dp->buffer+dp->len,data,len); + memset(dp->buffer+dp->len+len, 0, MP_INPUT_BUFFER_PADDING_SIZE); + mp_dbg(MSGT_DEMUX,MSGL_DBG4,"data appended! %d+%d\n",dp->len,len); + dp->len+=len; +} + +static int demux_asf_read_packet(demuxer_t *demux,unsigned char *data,int len,int id,int seq,uint64_t time,unsigned short dur,int offs,int keyframe){ + struct asf_priv* asf = demux->priv; + demux_stream_t *ds=NULL; + int close_seg=0; + + mp_dbg(MSGT_DEMUX,MSGL_DBG4,"demux_asf.read_packet: id=%d seq=%d len=%d\n",id,seq,len); + + if(demux->video->id==-1) + if(demux->v_streams[id]) + demux->video->id=id; + + if(demux->audio->id==-1) + if(demux->a_streams[id]) + demux->audio->id=id; + + if(id==demux->audio->id){ + // audio + ds=demux->audio; + if(!ds->sh){ + ds->sh=demux->a_streams[id]; + mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected ASF audio ID = %d\n",ds->id); + } + } else + if(id==demux->video->id){ + // video + ds=demux->video; + if(!ds->sh){ + ds->sh=demux->v_streams[id]; + mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected ASF video ID = %d\n",ds->id); + } + } + + if(ds){ + if(ds->asf_packet){ + demux_packet_t* dp=ds->asf_packet; + + if (ds==demux->video && asf->asf_is_dvr_ms) { + if (asf->new_vid_frame_seg) { + dp->pos=demux->filepos; + close_seg = 1; + } else seq = ds->asf_seq; + } else close_seg = ds->asf_seq!=seq; + + if(close_seg){ + // closed segment, finalize packet: + if(ds==demux->audio) + if(asf->scrambling_h>1 && asf->scrambling_w>1 && asf->scrambling_b>0) + asf_descrambling(&ds->asf_packet->buffer,ds->asf_packet->len,asf); + ds_add_packet(ds,ds->asf_packet); + ds->asf_packet=NULL; + } else { + // append data to it! + demux_asf_append_to_packet(dp,data,len,offs); + // we are ready now. + return 1; + } + } + // create new packet: + { demux_packet_t* dp; + if(offs>0){ + mp_msg(MSGT_DEMUX,MSGL_V,"warning! broken fragment, %d bytes missing \n",offs); + return 0; + } + dp=new_demux_packet(len); + memcpy(dp->buffer,data,len); + if (asf->asf_is_dvr_ms) + dp->pts=time*0.0000001; + else + dp->pts=time*0.001; + dp->keyframe = keyframe; +// if(ds==demux->video) printf("ASF time: %8d dur: %5d \n",time,dur); + dp->pos=demux->filepos; + ds->asf_packet=dp; + ds->asf_seq=seq; + // we are ready now. + return 1; + } + } + + return 0; +} + +/***************************************************************** + * \brief read the replicated data associated with each segment + * \parameter pp reference to replicated data + * \parameter id stream number + * \parameter seq media object number + * \parameter keyframe key frame indicator - set to zero if keyframe, non-zero otherwise + * \parameter seg_time set to payload time when valid, if audio or new video frame payload, zero otherwise + * + */ +static void get_payload_extension_data(demuxer_t *demux, unsigned char** pp, unsigned char id, unsigned int seq, int *keyframe, uint64_t *seg_time){ + struct asf_priv* asf = demux->priv; + uint64_t payload_time = -1; //100ns units + int i, ext_max, ext_timing_index; + uint8_t *pi = *pp+4; + + if(demux->video->id==-1) + if(demux->v_streams[id]) + demux->video->id=id; + + if(demux->audio->id==-1) + if(demux->a_streams[id]) + demux->audio->id=id; + + if (id!=demux->video->id && id!=demux->audio->id) return; + + if (id==demux->video->id) { + ext_max = asf->vid_repdata_count; + ext_timing_index = asf->vid_ext_timing_index; + } else { + ext_max = asf->aud_repdata_count; + ext_timing_index = asf->aud_ext_timing_index; + } + + *seg_time=0.0; + asf->new_vid_frame_seg = 0; + + for (i=0; i<ext_max; i++) { + uint16_t payextsize; + uint8_t segment_marker; + + if (id==demux->video->id) + payextsize = asf->vid_repdata_sizes[i]; + else + payextsize = asf->aud_repdata_sizes[i]; + + if (payextsize == 65535) { + payextsize = AV_RL16(pi); + pi+=2; + } + + // if this is the timing info extension then read the payload time + if (i == ext_timing_index) + payload_time = AV_RL64(pi+8); + + // if this is the video frame info extension then + // set the keyframe indicator, the 'new frame segment' indicator + // and (initially) the 'frame time' + if (i == asf->vid_ext_frame_index && id==demux->video->id) { + segment_marker = pi[0]; + // Known video stream segment_marker values that + // contain useful information: + // + // NTSC/ATSC (29.97fps): 0X4A 01001010 + // 0X4B 01001011 + // 0X49 01001001 + // + // PAL/ATSC (25fps): 0X3A 00111010 + // 0X3B 00111011 + // 0X39 00111001 + // + // ATSC progressive (29.97fps): 0X7A 01111010 + // 0X7B 01111011 + // 0X79 01111001 + // 11111111 + // ^ this is new video frame marker + // + // ^^^^ these bits indicate the framerate + // 0X4 is 29.97i, 0X3 is 25i, 0X7 is 29.97p, ???=25p + // + // ^^^ these bits indicate the frame type: + // 001 means I-frame + // 010 and 011 probably mean P and B + + asf->new_vid_frame_seg = (0X08 & segment_marker) && seq != asf->last_vid_seq; + + if (asf->new_vid_frame_seg) asf->last_vid_seq = seq; + + if (asf->avg_vid_frame_time == 0) { + // set the average frame time initially (in 100ns units). + // This is based on what works for known samples. + // It can be extended if more samples of different types can be obtained. + if (((segment_marker & 0XF0) >> 4) == 4) { + asf->avg_vid_frame_time = (uint64_t)((1.001 / 30.0) * 10000000.0); + asf->know_frame_time=1; + } else if (((segment_marker & 0XF0) >> 4) == 3) { + asf->avg_vid_frame_time = (uint64_t)(0.04 * 10000000.0); + asf->know_frame_time=1; + } else if (((segment_marker & 0XF0) >> 4) == 6) { + asf->avg_vid_frame_time = (uint64_t)(0.02 * 10000000.0); + asf->know_frame_time=1; + } else if (((segment_marker & 0XF0) >> 4) == 7) { + asf->avg_vid_frame_time = (uint64_t)((1.001 / 60.0) * 10000000.0); + asf->know_frame_time=1; + } else { + // we dont know the frame time initially so + // make a guess and then recalculate as we go. + asf->avg_vid_frame_time = (uint64_t)((1.001 / 60.0) * 10000000.0); + asf->know_frame_time=0; + } + } + *keyframe = (asf->new_vid_frame_seg && (segment_marker & 0X07) == 1); + } + pi +=payextsize; + } + + if (id==demux->video->id && asf->new_vid_frame_seg) { + asf->vid_frame_ct++; + // Some samples only have timings on key frames and + // the rest contain non-cronological timestamps. Interpolating + // the values between key frames works for all samples. + if (*keyframe) { + asf->found_first_key_frame=1; + if (!asf->know_frame_time && asf->last_key_payload_time > 0) { + // We dont know average frametime so recalculate. + // Giving precedence to the 'weight' of the existing + // average limits damage done to new value when there is + // a sudden time jump which happens occasionally. + asf->avg_vid_frame_time = + (0.9 * asf->avg_vid_frame_time) + + (0.1 * ((payload_time - asf->last_key_payload_time) / asf->vid_frame_ct)); + } + asf->last_key_payload_time = payload_time; + asf->vid_frame_ct = 1; + *seg_time = payload_time; + } else + *seg_time = (asf->last_key_payload_time + (asf->avg_vid_frame_time * (asf->vid_frame_ct-1))); + } + + if (id==demux->audio->id) { + if (payload_time != -1) + asf->last_aud_diff = payload_time - asf->last_aud_pts; + asf->last_aud_pts += asf->last_aud_diff; + *seg_time = asf->last_aud_pts; + } +} +//static int num_elementary_packets100=0; +//static int num_elementary_packets101=0; + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +static int demux_asf_fill_buffer(demuxer_t *demux, demux_stream_t *ds){ + struct asf_priv* asf = demux->priv; + + demux->filepos=stream_tell(demux->stream); + // Brodcast stream have movi_start==movi_end + // Better test ? + if((demux->movi_start < demux->movi_end) && (demux->filepos>=demux->movi_end)){ + demux->stream->eof=1; + return 0; + } + + stream_read(demux->stream,asf->packet,asf->packetsize); + if(demux->stream->eof) return 0; // EOF + if(asf->packetsize < 2) return 0; // Packet too short + + { + unsigned char* p=asf->packet; + unsigned char* p_end=asf->packet+asf->packetsize; + unsigned char flags=p[0]; + unsigned char segtype=p[1]; + unsigned padding; + unsigned plen; + unsigned sequence av_unused; + unsigned long time av_unused = 0; + unsigned short duration=0; + + int segs=1; + unsigned char segsizetype=0x80; + int seg=-1; + + if( mp_msg_test(MSGT_DEMUX,MSGL_DBG2) ){ + int i; + for(i=0;i<FFMIN(16, asf->packetsize);i++) printf(" %02X",asf->packet[i]); + printf("\n"); + } + + // skip ECC data if present by testing bit 7 of flags + // 1xxxbbbb -> ecc data present, skip bbbb byte(s) + // 0xxxxxxx -> payload parsing info starts + if (flags & 0x80) + { + p += (flags & 0x0f)+1; + if (p+1 >= p_end) return 0; // Packet too short + flags = p[0]; + segtype = p[1]; + } + + //if(segtype!=0x5d) printf("Warning! packet[4] != 0x5d \n"); + + p+=2; // skip flags & segtype + + // Read packet size (plen): + if(!check_varlen(p, p_end, flags>> 5)) return 0; // Not enough data + plen = read_varlen(&p, flags >> 5, 0); + + // Read sequence: + if(!check_varlen(p, p_end, flags>> 1)) return 0; // Not enough data + sequence = read_varlen(&p, flags >> 1, 0); + + // Read padding size (padding): + if(!check_varlen(p, p_end, flags>> 3)) return 0; // Not enough data + padding = read_varlen(&p, flags >> 3, 0); + + if(((flags>>5)&3)!=0){ + // Explicit (absoulte) packet size + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"Explicit packet size specified: %d \n",plen); + if(plen>asf->packetsize) mp_msg(MSGT_DEMUX,MSGL_V,"Warning! plen>packetsize! (%d>%d) \n",plen,asf->packetsize); + } else { + // Padding (relative) size + plen=asf->packetsize-padding; + } + + // Read time & duration: + if (p+5 >= p_end) return 0; // Packet too short + time = AV_RL32(p); p+=4; + duration = AV_RL16(p); p+=2; + + // Read payload flags: + if(flags&1){ + // multiple sub-packets + if (p >= p_end) return 0; // Packet too short + segsizetype=p[0]>>6; + segs=p[0] & 0x3F; + ++p; + } + mp_dbg(MSGT_DEMUX,MSGL_DBG4,"%08"PRIu64": flag=%02X segs=%d seq=%u plen=%u pad=%u time=%ld dur=%d\n", + (uint64_t)demux->filepos,flags,segs,sequence,plen,padding,time,duration); + + for(seg=0;seg<segs;seg++){ + //ASF_segmhdr_t* sh; + unsigned char streamno; + unsigned int seq; + unsigned int x; // offset or timestamp + unsigned int rlen; + // + int len; + uint64_t time2=0; + int keyframe=0; + + if(p>=p_end) { + mp_msg(MSGT_DEMUX,MSGL_V,"Warning! invalid packet 1, aborting parsing...\n"); + break; + } + + if( mp_msg_test(MSGT_DEMUX,MSGL_DBG2) ){ + int i; + printf("seg %d:",seg); + for(i=0;i<FFMIN(16, p_end - p);i++) printf(" %02X",p[i]); + printf("\n"); + } + + streamno=p[0]&0x7F; + if(p[0]&0x80) keyframe=1; + p++; + + // Read media object number (seq): + if(!check_varlen(p, p_end, segtype >> 4)) break; // Not enough data + seq = read_varlen(&p, segtype >> 4, 0); + + // Read offset or timestamp: + if(!check_varlen(p, p_end, segtype >> 2)) break; // Not enough data + x = read_varlen(&p, segtype >> 2, 0); + + // Read replic.data len: + if(!check_varlen(p, p_end, segtype)) break; // Not enough data + rlen = read_varlen(&p, segtype, 0); + +// printf("### rlen=%d \n",rlen); + if (rlen > p_end - p) { + mp_msg(MSGT_DEMUX, MSGL_V, "invalid rlen=%u\n", rlen); + break; + } + + switch(rlen){ + case 0x01: // 1 = special, means grouping + //printf("grouping: %02X \n",p[0]); + ++p; // skip PTS delta + break; + default: + if(rlen>=8){ + p+=4; // skip object size + if (p+3 >= p_end) break; // Packet too short + time2=AV_RL32(p); // read PTS + if (asf->asf_is_dvr_ms) + get_payload_extension_data(demux, &p, streamno, seq, &keyframe, &time2); + p+=rlen-4; + } else { + mp_msg(MSGT_DEMUX,MSGL_V,"unknown segment type (rlen): 0x%02X \n",rlen); + time2=0; // unknown + p+=rlen; + } + } + + if(flags&1){ + // multiple segments + if(!check_varlen(p, p_end, segsizetype)) break; // Not enough data + len = read_varlen(&p, segsizetype, plen-(p-asf->packet)); + } else { + // single segment + len=plen-(p-asf->packet); + } + if(len<0 || (p+len)>p_end){ + mp_msg(MSGT_DEMUX,MSGL_V,"ASF_parser: warning! segment len=%d\n",len); + len = p_end - p; + } + mp_dbg(MSGT_DEMUX,MSGL_DBG4," seg #%d: streamno=%d seq=%d type=%02X len=%d\n",seg,streamno,seq,rlen,len); + + switch(rlen){ + case 0x01: + // GROUPING: + //printf("ASF_parser: warning! grouping (flag=1) not yet supported!\n",len); + //printf(" total: %d \n",len); + while(len>0){ + int len2=p[0]; + p++; + //printf(" group part: %d bytes\n",len2); + if(len2 > len - 1 || len2 < 0) break; // Not enough data + len2 = FFMIN(len2, asf->packetsize); + demux_asf_read_packet(demux,p,len2,streamno,seq,x,duration,-1,keyframe); + p+=len2; + len-=len2+1; + ++seq; + } + if(len!=0){ + mp_msg(MSGT_DEMUX,MSGL_V,"ASF_parser: warning! groups total != len\n"); + } + break; + default: + // NO GROUPING: + //printf("fragment offset: %d \n",sh->x); + if (len <= 0) break; + if (!asf->asf_is_dvr_ms || asf->found_first_key_frame) { + len = FFMIN(len, asf->packetsize); + demux_asf_read_packet(demux,p,len,streamno,seq,time2,duration,x,keyframe); + } + p+=len; + break; + } + + } // for segs + return 1; // success + } + + mp_msg(MSGT_DEMUX,MSGL_V,"%08"PRIX64": UNKNOWN TYPE %02X %02X %02X %02X %02X...\n",(int64_t)demux->filepos,asf->packet[0],asf->packet[1],asf->packet[2],asf->packet[3],asf->packet[4]); + return 0; +} + +#include "stheader.h" + +static void demux_seek_asf(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags){ + struct asf_priv* asf = demuxer->priv; + demux_stream_t *d_audio=demuxer->audio; + demux_stream_t *d_video=demuxer->video; + sh_audio_t *sh_audio=d_audio->sh; +// sh_video_t *sh_video=d_video->sh; + + //FIXME: OFF_T - didn't test ASF case yet (don't have a large asf...) + //FIXME: reports good or bad to steve@daviesfam.org please + + //================= seek in ASF ========================== + float p_rate=asf->packetrate; // packets / sec + off_t rel_seek_packs=(flags&SEEK_FACTOR)? // FIXME: int may be enough? + (rel_seek_secs*(demuxer->movi_end-demuxer->movi_start)/asf->packetsize): + (rel_seek_secs*p_rate); + off_t rel_seek_bytes=rel_seek_packs*asf->packetsize; + off_t newpos; + //printf("ASF: packs: %d duration: %d \n",(int)fileh.packets,*((int*)&fileh.duration)); +// printf("ASF_seek: %d secs -> %d packs -> %d bytes \n", +// rel_seek_secs,rel_seek_packs,rel_seek_bytes); + newpos=((flags&SEEK_ABSOLUTE)?demuxer->movi_start:demuxer->filepos)+rel_seek_bytes; + if(newpos<0 || newpos<demuxer->movi_start) newpos=demuxer->movi_start; +// printf("\r -- asf: newpos=%d -- \n",newpos); + stream_seek(demuxer->stream,newpos); + + if (asf->asf_is_dvr_ms) asf->dvr_last_vid_pts = 0.0f; + + if (d_video->id >= 0) + ds_fill_buffer(d_video); + if(sh_audio){ + ds_fill_buffer(d_audio); + } + + if (d_video->id >= 0) + while(1){ + if(sh_audio && !d_audio->eof){ + float a_pts=d_audio->pts; + a_pts+=(ds_tell_pts(d_audio)-sh_audio->a_in_buffer_len)/(float)sh_audio->i_bps; + // sync audio: + if (d_video->pts > a_pts){ + skip_audio_frame(sh_audio); +// if(!ds_fill_buffer(d_audio)) sh_audio=NULL; // skip audio. EOF? + continue; + } + } + if (d_video->keyframe) + break; + if(!ds_fill_buffer(d_video)) break; // skip frame. EOF? + } + + +} + +static int demux_asf_control(demuxer_t *demuxer,int cmd, void *arg){ + struct asf_priv* asf = demuxer->priv; +/* demux_stream_t *d_audio=demuxer->audio; + demux_stream_t *d_video=demuxer->video; + sh_audio_t *sh_audio=d_audio->sh; + sh_video_t *sh_video=d_video->sh; +*/ + switch(cmd) { + case DEMUXER_CTRL_GET_TIME_LENGTH: + *((double *)arg)=asf->movielength; + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_GET_PERCENT_POS: + return DEMUXER_CTRL_DONTKNOW; + + default: + return DEMUXER_CTRL_NOTIMPL; + } +} + + +static demuxer_t* demux_open_asf(demuxer_t* demuxer) +{ + struct asf_priv* asf = demuxer->priv; + sh_video_t *sh_video=NULL; + + //---- ASF header: + if(!asf) return NULL; + init_priv(asf); + if (!read_asf_header(demuxer,asf)) + return NULL; + stream_reset(demuxer->stream); + stream_seek(demuxer->stream,demuxer->movi_start); +// demuxer->idx_pos=0; +// demuxer->endpos=avi_header.movi_end; + if(demuxer->video->id != -2) { + if(!ds_fill_buffer(demuxer->video)){ + mp_msg(MSGT_DEMUXER, MSGL_WARN, "ASF: %s", + mp_gtext("No video stream found.\n")); + demuxer->video->sh=NULL; + //printf("ASF: missing video stream!? contact the author, it may be a bug :(\n"); + } else { + sh_video=demuxer->video->sh; + sh_video->fps=1000.0f; sh_video->frametime=0.001f; + + if (asf->asf_is_dvr_ms) { + sh_video->bih->biWidth = 0; + sh_video->bih->biHeight = 0; + } + } + } + + if(demuxer->audio->id!=-2){ + mp_tmsg(MSGT_DEMUXER,MSGL_V,"ASF: Searching for audio stream (id:%d).\n",demuxer->audio->id); + if(!ds_fill_buffer(demuxer->audio)){ + mp_msg(MSGT_DEMUXER, MSGL_INFO, "ASF: %s", + mp_gtext("No audio stream found -> no sound.\n")); + demuxer->audio->sh=NULL; + } + } + if(!demuxer->stream->seek) + demuxer->seekable=0; + + return demuxer; +} + + +static void demux_close_asf(demuxer_t *demuxer) { + struct asf_priv* asf = demuxer->priv; + + if (!asf) return; + + free(asf->aud_repdata_sizes); + free(asf->vid_repdata_sizes); + free(asf->packet); + free(asf); +} + +const demuxer_desc_t demuxer_desc_asf = { + "ASF demuxer", + "asf", + "ASF", + "A'rpi", + "ASF, WMV, WMA", + DEMUXER_TYPE_ASF, + 1, // safe autodetect + asf_check_header, + demux_asf_fill_buffer, + demux_open_asf, + demux_close_asf, + demux_seek_asf, + demux_asf_control +}; diff --git a/demux/demux_avi.c b/demux/demux_avi.c new file mode 100644 index 0000000000..887494c6cd --- /dev/null +++ b/demux/demux_avi.c @@ -0,0 +1,909 @@ +/* + * AVI file parser for DEMUXER v2.9 + * Copyright (c) 2001 A'rpi/ESP-team + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "mp_msg.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "aviheader.h" + +extern const demuxer_desc_t demuxer_desc_avi_ni; +extern const demuxer_desc_t demuxer_desc_avi_nini; + +// PTS: 0=interleaved 1=BPS-based +int pts_from_bps=1; + +static void update_audio_block_size(demuxer_t *demux) +{ + avi_priv_t *priv = demux->priv; + sh_audio_t *sh = demux->audio->sh; + if (!sh) + return; + priv->audio_block_size = sh->audio.dwSampleSize; + if (sh->wf) { + priv->audio_block_size = sh->wf->nBlockAlign; + if (!priv->audio_block_size) { + // for PCM audio we can calculate the blocksize: + if (sh->format == 1) + priv->audio_block_size = sh->wf->nChannels*(sh->wf->wBitsPerSample/8); + else + priv->audio_block_size = 1; // hope the best... + } else { + // workaround old mencoder bug: + if (sh->audio.dwSampleSize == 1 && sh->audio.dwScale == 1 && + (sh->wf->nBlockAlign == 1152 || sh->wf->nBlockAlign == 576)) { + mp_tmsg(MSGT_DEMUX,MSGL_WARN,"AVI: Working around CBR-MP3 nBlockAlign header bug!\n"); + priv->audio_block_size = 1; + } + } + } +} + +// Select ds from ID +static demux_stream_t *demux_avi_select_stream(demuxer_t *demux, + unsigned int id) +{ + int stream_id=avi_stream_id(id); + + + if(demux->video->id==-1) + if(demux->v_streams[stream_id]) + demux->video->id=stream_id; + + if(demux->audio->id==-1) + if(demux->a_streams[stream_id]) + demux->audio->id=stream_id; + + if(stream_id==demux->audio->id){ + if(!demux->audio->sh){ + demux->audio->sh=demux->a_streams[stream_id]; + mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected AVI audio ID = %d\n",demux->audio->id); + update_audio_block_size(demux); + } + return demux->audio; + } + if(stream_id==demux->video->id){ + if(!demux->video->sh){ + demux->video->sh=demux->v_streams[stream_id]; + mp_msg(MSGT_DEMUX,MSGL_V,"Auto-selected AVI video ID = %d\n",demux->video->id); + } + return demux->video; + } + if(id!=mmioFOURCC('J','U','N','K')){ + // unknown + mp_msg(MSGT_DEMUX,MSGL_DBG2,"Unknown chunk: %.4s (%X)\n",(char *) &id,id); + //abort(); + } + return NULL; +} + +static int valid_fourcc(unsigned int id){ + static const char valid[] = "0123456789abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + unsigned char* fcc=(unsigned char*)(&id); + return strchr(valid, fcc[0]) && strchr(valid, fcc[1]) && + strchr(valid, fcc[2]) && strchr(valid, fcc[3]); +} + +static int valid_stream_id(unsigned int id) { + unsigned char* fcc=(unsigned char*)(&id); + return fcc[0] >= '0' && fcc[0] <= '9' && fcc[1] >= '0' && fcc[1] <= '9' && + ((fcc[2] == 'w' && fcc[3] == 'b') || (fcc[2] == 'd' && fcc[3] == 'c')); +} + +static int choose_chunk_len(unsigned int len1,unsigned int len2){ + // len1 has a bit more priority than len2. len1!=len2 + // Note: this is a first-idea-logic, may be wrong. comments welcomed. + + // prefer small frames rather than 0 + if(!len1) return (len2>0x80000) ? len1 : len2; + if(!len2) return (len1>0x100000) ? len2 : len1; + + // choose the smaller value: + return (len1<len2)? len1 : len2; +} + +static int demux_avi_read_packet(demuxer_t *demux,demux_stream_t *ds,unsigned int id,unsigned int len,int idxpos,int flags){ + avi_priv_t *priv=demux->priv; + int skip; + float pts=0; + + mp_dbg(MSGT_DEMUX,MSGL_DBG3,"demux_avi.read_packet: %X\n",id); + + if(ds==demux->audio){ + if(priv->pts_corrected==0){ + if(priv->pts_has_video){ + // we have video pts now + float delay=0; + if(((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec) + delay=(float)priv->pts_corr_bytes/((sh_audio_t*)(ds->sh))->wf->nAvgBytesPerSec; + mp_msg(MSGT_DEMUX,MSGL_V,"XXX initial v_pts=%5.3f a_pos=%d (%5.3f) \n",priv->avi_audio_pts,priv->pts_corr_bytes,delay); + //priv->pts_correction=-priv->avi_audio_pts+delay; + priv->pts_correction=delay-priv->avi_audio_pts; + priv->avi_audio_pts+=priv->pts_correction; + priv->pts_corrected=1; + } else + priv->pts_corr_bytes+=len; + } + if(pts_from_bps){ + pts = priv->audio_block_no * + (float)((sh_audio_t*)demux->audio->sh)->audio.dwScale / + (float)((sh_audio_t*)demux->audio->sh)->audio.dwRate; + } else + pts=priv->avi_audio_pts; //+priv->pts_correction; + priv->avi_audio_pts=0; + // update blockcount: + priv->audio_block_no+= + (len+priv->audio_block_size-1)/priv->audio_block_size; + } else + if(ds==demux->video){ + // video + if(priv->skip_video_frames>0){ + // drop frame (seeking) + --priv->skip_video_frames; + ds=NULL; + } + + pts = priv->avi_video_pts = priv->video_pack_no * + (float)((sh_video_t*)demux->video->sh)->video.dwScale / + (float)((sh_video_t*)demux->video->sh)->video.dwRate; + + priv->avi_audio_pts=priv->avi_video_pts+priv->pts_correction; + priv->pts_has_video=1; + + if(ds) ++priv->video_pack_no; + + } + + skip=(len+1)&(~1); // total bytes in this chunk + + if(ds){ + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Read %d data bytes from packet %04X\n",len,id); + ds_read_packet(ds,demux->stream,len,pts,idxpos,flags); + skip-=len; + } + skip = FFMAX(skip, 0); + if (avi_stream_id(id) > 99 && id != mmioFOURCC('J','U','N','K')) + skip = FFMIN(skip, 65536); + if(skip){ + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_AVI: Skipping %d bytes from packet %04X\n",skip,id); + stream_skip(demux->stream,skip); + } + return ds?1:0; +} + +static uint32_t avi_find_id(stream_t *stream) { + uint32_t id = stream_read_dword_le(stream); + if (!id) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "Incomplete stream? Trying resync.\n"); + do { + id = stream_read_dword_le(stream); + if (stream_eof(stream)) return 0; + } while (avi_stream_id(id) > 99); + } + return id; +} + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +static int demux_avi_fill_buffer(demuxer_t *demux, demux_stream_t *dsds){ +avi_priv_t *priv=demux->priv; +unsigned int id=0; +unsigned int len; +int ret=0; +demux_stream_t *ds; + +do{ + int flags=1; + AVIINDEXENTRY *idx=NULL; + if(priv->idx_size>0 && priv->idx_pos<priv->idx_size){ + off_t pos; + + idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++]; + + if(idx->dwFlags&AVIIF_LIST){ + if (!valid_stream_id(idx->ckid)) + // LIST + continue; + if (!priv->warned_unaligned) + mp_msg(MSGT_DEMUX, MSGL_WARN, "Looks like unaligned chunk in index, broken AVI file!\n"); + priv->warned_unaligned = 1; + } + if(!demux_avi_select_stream(demux,idx->ckid)){ + mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid); + continue; // skip this chunk + } + + pos = (off_t)priv->idx_offset+AVI_IDX_OFFSET(idx); + if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start) && (demux->stream->flags & MP_STREAM_SEEK)){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! idx=0x%"PRIX64" \n",(int64_t)pos); + continue; + } + stream_seek(demux->stream,pos); + demux->filepos=stream_tell(demux->stream); + id=stream_read_dword_le(demux->stream); + if(stream_eof(demux->stream)) return 0; // EOF! + + if(id!=idx->ckid){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid); + if(valid_fourcc(idx->ckid)) + id=idx->ckid; // use index if valid + else + if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad + } + len=stream_read_dword_le(demux->stream); + if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength); + if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :( + len=choose_chunk_len(idx->dwChunkLength,len); + } + if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0; + } else { + demux->filepos=stream_tell(demux->stream); + if(demux->filepos>=demux->movi_end && demux->movi_end>demux->movi_start && (demux->stream->flags & MP_STREAM_SEEK)){ + demux->stream->eof=1; + return 0; + } + id=avi_find_id(demux->stream); + len=stream_read_dword_le(demux->stream); + if(stream_eof(demux->stream)) return 0; // EOF! + + if(id==mmioFOURCC('L','I','S','T') || id==mmioFOURCC('R', 'I', 'F', 'F')){ + id=stream_read_dword_le(demux->stream); // list or RIFF type + continue; + } + } + + ds=demux_avi_select_stream(demux,id); + if(ds) + if(ds->packs+1>=MAX_PACKS || ds->bytes+len>=MAX_PACK_BYTES){ + // this packet will cause a buffer overflow, switch to -ni mode!!! + mp_tmsg(MSGT_DEMUX,MSGL_WARN,"\nBadly interleaved AVI file detected - switching to --avi-ni mode...\n"); + if(priv->idx_size>0){ + // has index + demux->type=DEMUXER_TYPE_AVI_NI; + demux->desc=&demuxer_desc_avi_ni; + --priv->idx_pos; // hack + } else { + // no index + demux->type=DEMUXER_TYPE_AVI_NINI; + demux->desc=&demuxer_desc_avi_nini; + priv->idx_pos=demux->filepos; // hack + } + priv->idx_pos_v=priv->idx_pos_a=priv->idx_pos; + // quit now, we can't even (no enough buffer memory) read this packet :( + return -1; + } + + ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,flags); +} while(ret!=1); + return 1; +} + + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +static int demux_avi_fill_buffer_ni(demuxer_t *demux, demux_stream_t *ds) +{ +avi_priv_t *priv=demux->priv; +unsigned int id=0; +unsigned int len; +int ret=0; + +do{ + int flags=1; + AVIINDEXENTRY *idx=NULL; + int idx_pos=0; + demux->filepos=stream_tell(demux->stream); + + if(ds==demux->video) idx_pos=priv->idx_pos_v++; else + if(ds==demux->audio) idx_pos=priv->idx_pos_a++; else + idx_pos=priv->idx_pos++; + + if(priv->idx_size>0 && idx_pos<priv->idx_size){ + off_t pos; + idx=&((AVIINDEXENTRY *)priv->idx)[idx_pos]; + + if(idx->dwFlags&AVIIF_LIST){ + if (!valid_stream_id(idx->ckid)) + // LIST + continue; + if (!priv->warned_unaligned) + mp_msg(MSGT_DEMUX, MSGL_WARN, "Looks like unaligned chunk in index, broken AVI file!\n"); + priv->warned_unaligned = 1; + } + if(ds && demux_avi_select_stream(demux,idx->ckid)!=ds){ + mp_dbg(MSGT_DEMUX,MSGL_DBG3,"Skip chunk %.4s (0x%X) \n",(char *)&idx->ckid,(unsigned int)idx->ckid); + continue; // skip this chunk + } + + pos = priv->idx_offset+AVI_IDX_OFFSET(idx); + if((pos<demux->movi_start || pos>=demux->movi_end) && (demux->movi_end>demux->movi_start)){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! current=0x%"PRIX64" idx=0x%"PRIX64" \n",(int64_t)demux->filepos,(int64_t)pos); + continue; + } + stream_seek(demux->stream,pos); + + id=stream_read_dword_le(demux->stream); + + if(stream_eof(demux->stream)) return 0; + + if(id!=idx->ckid){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkID mismatch! raw=%.4s idx=%.4s \n",(char *)&id,(char *)&idx->ckid); + if(valid_fourcc(idx->ckid)) + id=idx->ckid; // use index if valid + else + if(!valid_fourcc(id)) continue; // drop chunk if both id and idx bad + } + len=stream_read_dword_le(demux->stream); + if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%d \n",len,idx->dwChunkLength); + if(len>0x200000 && idx->dwChunkLength>0x200000) continue; // both values bad :( + len=choose_chunk_len(idx->dwChunkLength,len); + } + if(!(idx->dwFlags&AVIIF_KEYFRAME)) flags=0; + } else return 0; + ret=demux_avi_read_packet(demux,demux_avi_select_stream(demux,id),id,len,idx_pos,flags); +} while(ret!=1); + return 1; +} + + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +static int demux_avi_fill_buffer_nini(demuxer_t *demux, demux_stream_t *ds) +{ +avi_priv_t *priv=demux->priv; +unsigned int id=0; +unsigned int len; +int ret=0; +off_t *fpos=NULL; + + if(ds==demux->video) fpos=&priv->idx_pos_v; else + if(ds==demux->audio) fpos=&priv->idx_pos_a; else + return 0; + + stream_seek(demux->stream,fpos[0]); + +do{ + + demux->filepos=stream_tell(demux->stream); + if(demux->filepos>=demux->movi_end && (demux->movi_end>demux->movi_start)){ + ds->eof=1; + return 0; + } + + id=avi_find_id(demux->stream); + len=stream_read_dword_le(demux->stream); + + if(stream_eof(demux->stream)) return 0; + + if(id==mmioFOURCC('L','I','S','T')){ + id=stream_read_dword_le(demux->stream); // list type + continue; + } + + if(id==mmioFOURCC('R','I','F','F')){ + mp_msg(MSGT_DEMUX,MSGL_V,"additional RIFF header...\n"); + id=stream_read_dword_le(demux->stream); // "AVIX" + continue; + } + + if(ds==demux_avi_select_stream(demux,id)){ + // read it! + ret=demux_avi_read_packet(demux,ds,id,len,priv->idx_pos-1,0); + } else { + // skip it! + int skip=(len+1)&(~1); // total bytes in this chunk + stream_skip(demux->stream,skip); + } + +} while(ret!=1); + fpos[0]=stream_tell(demux->stream); + return 1; +} + +// AVI demuxer parameters: +int index_mode=-1; // -1=untouched 0=don't use index 1=use (generate) index +int force_ni=0; // force non-interleaved AVI parsing + +static demuxer_t* demux_open_avi(demuxer_t* demuxer){ + demux_stream_t *d_audio=demuxer->audio; + demux_stream_t *d_video=demuxer->video; + sh_audio_t *sh_audio=NULL; + sh_video_t *sh_video=NULL; + avi_priv_t* priv=calloc(1, sizeof(avi_priv_t)); + + demuxer->priv=(void*)priv; + + //---- AVI header: + read_avi_header(demuxer,(demuxer->stream->flags & MP_STREAM_SEEK_BW)?index_mode:-2); + update_audio_block_size(demuxer); + + if(demuxer->audio->id>=0 && !demuxer->a_streams[demuxer->audio->id]){ + mp_tmsg(MSGT_DEMUX,MSGL_WARN,"AVI: invalid audio stream ID: %d - ignoring (nosound)\n",demuxer->audio->id); + demuxer->audio->id=-2; // disabled + } + if(demuxer->video->id>=0 && !demuxer->v_streams[demuxer->video->id]){ + mp_tmsg(MSGT_DEMUX,MSGL_WARN,"AVI: invalid video stream ID: %d - ignoring (using default)\n",demuxer->video->id); + demuxer->video->id=-1; // autodetect + } + + stream_reset(demuxer->stream); + stream_seek(demuxer->stream,demuxer->movi_start); + if(priv->idx_size>1){ + // decide index format: +#if 1 + if((AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start || + AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[1])<demuxer->movi_start )&& !priv->isodml) + priv->idx_offset=demuxer->movi_start-4; +#else + if(AVI_IDX_OFFSET(&((AVIINDEXENTRY *)priv->idx)[0])<demuxer->movi_start) + priv->idx_offset=demuxer->movi_start-4; +#endif + mp_msg(MSGT_DEMUX,MSGL_V,"AVI index offset: 0x%X (movi=0x%X idx0=0x%X idx1=0x%X)\n", + (int)priv->idx_offset,(int)demuxer->movi_start, + (int)((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset, + (int)((AVIINDEXENTRY *)priv->idx)[1].dwChunkOffset); + } + + if(priv->idx_size>0){ + // check that file is non-interleaved: + int i; + off_t a_pos=-1; + off_t v_pos=-1; + for(i=0;i<priv->idx_size;i++){ + AVIINDEXENTRY* idx=&((AVIINDEXENTRY *)priv->idx)[i]; + demux_stream_t* ds=demux_avi_select_stream(demuxer,idx->ckid); + off_t pos = priv->idx_offset + AVI_IDX_OFFSET(idx); + if(a_pos==-1 && ds==demuxer->audio){ + a_pos=pos; + if(v_pos!=-1) break; + } + if(v_pos==-1 && ds==demuxer->video){ + v_pos=pos; + if(a_pos!=-1) break; + } + } + if(v_pos==-1){ + mp_msg(MSGT_DEMUX, MSGL_ERR, "AVI_NI: %s", + mp_gtext("No video stream found.\n")); + return NULL; + } + if(a_pos==-1){ + d_audio->sh=sh_audio=NULL; + } else { + if(force_ni || abs(a_pos-v_pos)>0x100000){ // distance > 1MB + mp_tmsg(MSGT_DEMUX,MSGL_INFO,"%s NON-INTERLEAVED AVI file format.\n",force_ni?"Forced":"Detected"); + demuxer->type=DEMUXER_TYPE_AVI_NI; // HACK!!!! + demuxer->desc=&demuxer_desc_avi_ni; // HACK!!!! + pts_from_bps=1; // force BPS sync! + } + } + } else { + // no index + if(force_ni){ + mp_tmsg(MSGT_DEMUX,MSGL_INFO,"Using NON-INTERLEAVED broken AVI file format.\n"); + demuxer->type=DEMUXER_TYPE_AVI_NINI; // HACK!!!! + demuxer->desc=&demuxer_desc_avi_nini; // HACK!!!! + priv->idx_pos_a= + priv->idx_pos_v=demuxer->movi_start; + pts_from_bps=1; // force BPS sync! + } + demuxer->seekable=0; + } + if(!ds_fill_buffer(d_video)){ + mp_msg(MSGT_DEMUX, MSGL_ERR, "AVI: %s", + mp_gtext("Missing video stream!? Contact the author, " + "it may be a bug :(\n")); + return NULL; + } + sh_video=d_video->sh;sh_video->ds=d_video; + if(d_audio->id!=-2){ + mp_msg(MSGT_DEMUX,MSGL_V,"AVI: Searching for audio stream (id:%d)\n",d_audio->id); + if(!priv->audio_streams || !ds_fill_buffer(d_audio)){ + mp_msg(MSGT_DEMUX, MSGL_INFO, "AVI: %s", + mp_gtext("No audio stream found -> no sound.\n")); + d_audio->sh=sh_audio=NULL; + } else { + sh_audio=d_audio->sh;sh_audio->ds=d_audio; + } + } + + // calculating audio/video bitrate: + if(priv->idx_size>0){ + // we have index, let's count 'em! + AVIINDEXENTRY *idx = priv->idx; + int64_t vsize=0; + int64_t asize=0; + size_t vsamples=0; + size_t asamples=0; + int i; + for(i=0;i<priv->idx_size;i++){ + int id=avi_stream_id(idx[i].ckid); + unsigned len=idx[i].dwChunkLength; + if(sh_video->ds->id == id) { + vsize+=len; + ++vsamples; + } + else if(sh_audio && sh_audio->ds->id == id) { + asize+=len; + asamples+=(len+priv->audio_block_size-1)/priv->audio_block_size; + } + } + mp_msg(MSGT_DEMUX, MSGL_V, + "AVI video size=%"PRId64" (%zu) audio size=%"PRId64" (%zu)\n", + vsize, vsamples, asize, asamples); + priv->numberofframes=vsamples; + sh_video->i_bps=((float)vsize/(float)vsamples)*(float)sh_video->video.dwRate/(float)sh_video->video.dwScale; + if(sh_audio) sh_audio->i_bps=((float)asize/(float)asamples)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale; + } else { + // guessing, results may be inaccurate: + int64_t vsize; + int64_t asize=0; + + if((priv->numberofframes=sh_video->video.dwLength)<=1) + // bad video header, try to get number of frames from audio + if(sh_audio && sh_audio->wf->nAvgBytesPerSec) priv->numberofframes=sh_video->fps*sh_audio->audio.dwLength/sh_audio->audio.dwRate*sh_audio->audio.dwScale; + if(priv->numberofframes<=1){ + mp_tmsg(MSGT_SEEK,MSGL_WARN,"Could not determine number of frames (for absolute seek).\n"); + priv->numberofframes=0; + } + + if(sh_audio){ + if(sh_audio->wf->nAvgBytesPerSec && sh_audio->audio.dwSampleSize!=1){ + asize=(float)sh_audio->wf->nAvgBytesPerSec*sh_audio->audio.dwLength*sh_audio->audio.dwScale/sh_audio->audio.dwRate; + } else { + asize=sh_audio->audio.dwLength; + sh_audio->i_bps=(float)asize/(sh_video->frametime*priv->numberofframes); + } + } + vsize=demuxer->movi_end-demuxer->movi_start-asize-8*priv->numberofframes; + mp_msg(MSGT_DEMUX,MSGL_V,"AVI video size=%"PRId64" (%u) audio size=%"PRId64"\n",vsize,priv->numberofframes,asize); + sh_video->i_bps=(float)vsize/(sh_video->frametime*priv->numberofframes); + } + + return demuxer; + +} + + +static void demux_seek_avi(demuxer_t *demuxer, float rel_seek_secs, + float audio_delay, int flags) +{ + avi_priv_t *priv=demuxer->priv; + demux_stream_t *d_audio=demuxer->audio; + demux_stream_t *d_video=demuxer->video; + sh_audio_t *sh_audio=d_audio->sh; + sh_video_t *sh_video=d_video->sh; + float skip_audio_secs=0; + + //FIXME: OFF_T - Didn't check AVI case yet (avi files can't be >2G anyway?) + //================= seek in AVI ========================== + int rel_seek_frames=rel_seek_secs*sh_video->fps; + int video_chunk_pos=d_video->pos; + int i; + + if(flags&SEEK_ABSOLUTE){ + // seek absolute + video_chunk_pos=0; + } + + if(flags&SEEK_FACTOR){ + rel_seek_frames=rel_seek_secs*priv->numberofframes; + } + + priv->skip_video_frames=0; + priv->avi_audio_pts=0; + +// ------------ STEP 1: find nearest video keyframe chunk ------------ + // find nearest video keyframe chunk pos: + if(rel_seek_frames>0){ + // seek forward + while(video_chunk_pos<priv->idx_size-1){ + int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid; + if(avi_stream_id(id)==d_video->id){ // video frame + if((--rel_seek_frames)<0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break; + } + ++video_chunk_pos; + } + } else { + // seek backward + while(video_chunk_pos>0){ + int id=((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].ckid; + if(avi_stream_id(id)==d_video->id){ // video frame + if((++rel_seek_frames)>0 && ((AVIINDEXENTRY *)priv->idx)[video_chunk_pos].dwFlags&AVIIF_KEYFRAME) break; + } + --video_chunk_pos; + } + } + priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=video_chunk_pos; + + // re-calc video pts: + d_video->pack_no=0; + for(i=0;i<video_chunk_pos;i++){ + int id=((AVIINDEXENTRY *)priv->idx)[i].ckid; + if(avi_stream_id(id)==d_video->id) ++d_video->pack_no; + } + priv->video_pack_no= + sh_video->num_frames=sh_video->num_frames_decoded=d_video->pack_no; + priv->avi_video_pts=d_video->pack_no*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate; + d_video->pos=video_chunk_pos; + + mp_msg(MSGT_SEEK,MSGL_DBG2,"V_SEEK: pack=%d pts=%5.3f chunk=%d \n",d_video->pack_no,priv->avi_video_pts,video_chunk_pos); + +// ------------ STEP 2: seek audio, find the right chunk & pos ------------ + + d_audio->pack_no=0; + priv->audio_block_no=0; + d_audio->dpos=0; + + if(sh_audio){ + int i; + int len=0; + int skip_audio_bytes=0; + int curr_audio_pos=-1; + int audio_chunk_pos=-1; + int chunk_max=(demuxer->type==DEMUXER_TYPE_AVI)?video_chunk_pos:priv->idx_size; + + if(sh_audio->audio.dwSampleSize){ + // constant rate audio stream + /* immediate seeking to audio position, including when streams are delayed */ + curr_audio_pos=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale; + curr_audio_pos*=sh_audio->audio.dwSampleSize; + + // find audio chunk pos: + for(i=0;i<chunk_max;i++){ + int id=((AVIINDEXENTRY *)priv->idx)[i].ckid; + if(avi_stream_id(id)==d_audio->id){ + len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength; + if(d_audio->dpos<=curr_audio_pos && curr_audio_pos<(d_audio->dpos+len)){ + break; + } + ++d_audio->pack_no; + priv->audio_block_no+= + (len+priv->audio_block_size-1)/priv->audio_block_size; + d_audio->dpos+=len; + } + } + audio_chunk_pos=i; + skip_audio_bytes=curr_audio_pos-d_audio->dpos; + + mp_msg(MSGT_SEEK,MSGL_V,"SEEK: i=%d (max:%d) dpos=%d (wanted:%d) \n", + i,chunk_max,(int)d_audio->dpos,curr_audio_pos); + + } else { + // VBR audio + /* immediate seeking to audio position, including when streams are delayed */ + int chunks=(priv->avi_video_pts + audio_delay)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale; + audio_chunk_pos=0; + + // find audio chunk pos: + for(i=0;i<priv->idx_size && chunks>0;i++){ + int id=((AVIINDEXENTRY *)priv->idx)[i].ckid; + if(avi_stream_id(id)==d_audio->id){ + len=((AVIINDEXENTRY *)priv->idx)[i].dwChunkLength; + if(i>chunk_max){ + skip_audio_bytes+=len; + } else { + ++d_audio->pack_no; + priv->audio_block_no+= + (len+priv->audio_block_size-1)/priv->audio_block_size; + d_audio->dpos+=len; + audio_chunk_pos=i; + } + chunks-=(len+priv->audio_block_size-1)/priv->audio_block_size; + } + } + } + + // Now we have: + // audio_chunk_pos = chunk no in index table (it's <=chunk_max) + // skip_audio_bytes = bytes to be skipped after chunk seek + // d-audio->pack_no = chunk_no in stream at audio_chunk_pos + // d_audio->dpos = bytepos in stream at audio_chunk_pos + // let's seek! + + // update stream position: + d_audio->pos=audio_chunk_pos; + + if(demuxer->type==DEMUXER_TYPE_AVI){ + // interleaved stream: + if(audio_chunk_pos<video_chunk_pos){ + // calc priv->skip_video_frames & adjust video pts counter: + for(i=audio_chunk_pos;i<video_chunk_pos;i++){ + int id=((AVIINDEXENTRY *)priv->idx)[i].ckid; + if(avi_stream_id(id)==d_video->id) ++priv->skip_video_frames; + } + // requires for correct audio pts calculation (demuxer): + priv->avi_video_pts-=priv->skip_video_frames*(float)sh_video->video.dwScale/(float)sh_video->video.dwRate; + priv->avi_audio_pts=priv->avi_video_pts; + // set index position: + priv->idx_pos_a=priv->idx_pos_v=priv->idx_pos=audio_chunk_pos; + } + } else { + // non-interleaved stream: + priv->idx_pos_a=audio_chunk_pos; + priv->idx_pos_v=video_chunk_pos; + priv->idx_pos=(audio_chunk_pos<video_chunk_pos)?audio_chunk_pos:video_chunk_pos; + } + + mp_msg(MSGT_SEEK,MSGL_V,"SEEK: idx=%d (a:%d v:%d) v.skip=%d a.skip=%d/%4.3f \n", + (int)priv->idx_pos,audio_chunk_pos,video_chunk_pos, + (int)priv->skip_video_frames,skip_audio_bytes,skip_audio_secs); + + if(skip_audio_bytes){ + demux_read_data(d_audio,NULL,skip_audio_bytes); + } + + } + d_video->pts=priv->avi_video_pts; // OSD + +} + + +static void demux_close_avi(demuxer_t *demuxer) +{ + avi_priv_t* priv=demuxer->priv; + + if(!priv) + return; + + if(priv->idx_size > 0) + free(priv->idx); + free(priv); +} + + +static int demux_avi_control(demuxer_t *demuxer,int cmd, void *arg){ + avi_priv_t *priv=demuxer->priv; + demux_stream_t *d_video=demuxer->video; + sh_video_t *sh_video=d_video->sh; + + switch(cmd) { + case DEMUXER_CTRL_GET_TIME_LENGTH: + if (!priv->numberofframes || !sh_video) return DEMUXER_CTRL_DONTKNOW; + *((double *)arg)=(double)priv->numberofframes/sh_video->fps; + if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS; + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_GET_PERCENT_POS: + if (!priv->numberofframes || !sh_video) { + return DEMUXER_CTRL_DONTKNOW; + } + *((int *)arg)=(int)(priv->video_pack_no*100/priv->numberofframes); + if (sh_video->video.dwLength<=1) return DEMUXER_CTRL_GUESS; + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_SWITCH_AUDIO: + case DEMUXER_CTRL_SWITCH_VIDEO: { + int audio = (cmd == DEMUXER_CTRL_SWITCH_AUDIO); + demux_stream_t *ds = audio ? demuxer->audio : demuxer->video; + void **streams = audio ? (void **)demuxer->a_streams : (void **)demuxer->v_streams; + int maxid = FFMIN(100, audio ? MAX_A_STREAMS : MAX_V_STREAMS); + int chunkid; + if (ds->id < -1) + ds->id = -1; + + if (*(int *)arg >= 0) + ds->id = *(int *)arg; + else { + int i; + for (i = 0; i < maxid; i++) { + if (++ds->id >= maxid) ds->id = 0; + if (streams[ds->id]) break; + } + } + + chunkid = (ds->id / 10 + '0') | (ds->id % 10 + '0') << 8; + ds->sh = NULL; + if (!streams[ds->id]) // stream not available + ds->id = -1; + else + demux_avi_select_stream(demuxer, chunkid); + *(int *)arg = ds->id; + return DEMUXER_CTRL_OK; + } + + default: + return DEMUXER_CTRL_NOTIMPL; + } +} + + +static int avi_check_file(demuxer_t *demuxer) +{ + int id=stream_read_dword_le(demuxer->stream); // "RIFF" + + if((id==mmioFOURCC('R','I','F','F')) || (id==mmioFOURCC('O','N','2',' '))) { + stream_read_dword_le(demuxer->stream); //filesize + id=stream_read_dword_le(demuxer->stream); // "AVI " + if(id==formtypeAVI) + return DEMUXER_TYPE_AVI; + // "Samsung Digimax i6 PMP" crap according to bug 742 + if(id==mmioFOURCC('A','V','I',0x19)) + return DEMUXER_TYPE_AVI; + if(id==mmioFOURCC('O','N','2','f')){ + mp_tmsg(MSGT_DEMUXER,MSGL_INFO,"ON2 AVI format"); + return DEMUXER_TYPE_AVI; + } + } + + return 0; +} + + +const demuxer_desc_t demuxer_desc_avi = { + "AVI demuxer", + "avi", + "AVI", + "Arpi?", + "AVI files, including non interleaved files", + DEMUXER_TYPE_AVI, + 1, // safe autodetect + avi_check_file, + demux_avi_fill_buffer, + demux_open_avi, + demux_close_avi, + demux_seek_avi, + demux_avi_control +}; + +const demuxer_desc_t demuxer_desc_avi_ni = { + "AVI demuxer, non-interleaved", + "avini", + "AVI", + "Arpi?", + "AVI files, including non interleaved files", + DEMUXER_TYPE_AVI, + 1, // safe autodetect + avi_check_file, + demux_avi_fill_buffer_ni, + demux_open_avi, + demux_close_avi, + demux_seek_avi, + demux_avi_control +}; + +const demuxer_desc_t demuxer_desc_avi_nini = { + "AVI demuxer, non-interleaved and no index", + "avinini", + "AVI", + "Arpi?", + "AVI files, including non interleaved files", + DEMUXER_TYPE_AVI, + 1, // safe autodetect + avi_check_file, + demux_avi_fill_buffer_nini, + demux_open_avi, + demux_close_avi, + demux_seek_avi, + demux_avi_control +}; diff --git a/demux/demux_cue.c b/demux/demux_cue.c new file mode 100644 index 0000000000..d2fd06ce71 --- /dev/null +++ b/demux/demux_cue.c @@ -0,0 +1,65 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> + +#include "bstr.h" +#include "demuxer.h" +#include "stream/stream.h" + +// timeline/tl_cue.c +bool mp_probe_cue(struct bstr s); + +#define PROBE_SIZE 512 + +static int try_open_file(struct demuxer *demuxer) +{ + struct stream *s = demuxer->stream; + char buf[PROBE_SIZE]; + int len = stream_read(s, buf, sizeof(buf)); + if (len <= 0) + return 0; + if (!mp_probe_cue((struct bstr) { buf, len })) + return 0; + stream_seek(s, 0); + demuxer->file_contents = stream_read_complete(s, demuxer, 1000000, 0); + if (demuxer->file_contents.start == NULL) + return 0; + if (!mp_probe_cue((struct bstr) { buf, len })) + return 0; + return DEMUXER_TYPE_CUE; +} + +static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds) +{ + return 0; +} + +const struct demuxer_desc demuxer_desc_cue = { + .info = "CUE file demuxer", + .name = "cue", + .shortdesc = "CUE", + .author = "Uoti Urpala", + .comment = "", + .type = DEMUXER_TYPE_CUE, + .safe_check = true, + .check_file = try_open_file, // no separate .open + .fill_buffer = dummy_fill_buffer, +}; diff --git a/demux/demux_edl.c b/demux/demux_edl.c new file mode 100644 index 0000000000..4c864cfe42 --- /dev/null +++ b/demux/demux_edl.c @@ -0,0 +1,58 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> + +#include "demuxer.h" +#include "stream/stream.h" + +static int try_open_file(struct demuxer *demuxer) +{ + struct stream *s = demuxer->stream; + const char header[] = "mplayer EDL file"; + const int len = sizeof(header) - 1; + char buf[len]; + if (stream_read(s, buf, len) < len) + return 0; + if (strncmp(buf, header, len)) + return 0; + stream_seek(s, 0); + demuxer->file_contents = stream_read_complete(s, demuxer, 1000000, 0); + if (demuxer->file_contents.start == NULL) + return 0; + return DEMUXER_TYPE_EDL; +} + +static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds) +{ + return 0; +} + +const struct demuxer_desc demuxer_desc_edl = { + .info = "EDL file demuxer", + .name = "edl", + .shortdesc = "EDL", + .author = "Uoti Urpala", + .comment = "", + .type = DEMUXER_TYPE_EDL, + .safe_check = true, + .check_file = try_open_file, // no separate .open + .fill_buffer = dummy_fill_buffer, +}; diff --git a/demux/demux_gif.c b/demux/demux_gif.c new file mode 100644 index 0000000000..18bf9abfd7 --- /dev/null +++ b/demux/demux_gif.c @@ -0,0 +1,338 @@ +/* + * GIF file parser + * Copyright (C) 2003 Joey Parrish + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <libavformat/avformat.h> + +#include "config.h" + +#include "mp_msg.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" + +#include <gif_lib.h> +#include "libvo/fastmemcpy.h" +typedef struct { + int current_pts; + unsigned char *palette; + GifFileType *gif; + int w, h; + int useref; + uint8_t *refimg; +} gif_priv_t; + +#define GIF_SIGNATURE (('G' << 16) | ('I' << 8) | 'F') + +#ifndef CONFIG_GIF_TVT_HACK +// not supported by certain versions of the library +static int my_read_gif(GifFileType *gif, uint8_t *buf, int len) +{ + return stream_read(gif->UserData, buf, len); +} +#endif + +static int gif_check_file(demuxer_t *demuxer) +{ + if (stream_read_int24(demuxer->stream) == GIF_SIGNATURE) + return DEMUXER_TYPE_GIF; + return 0; +} + +static void memcpy_transp_pic(uint8_t *dst, uint8_t *src, int w, int h, + int dstride, int sstride, int transp, uint8_t trans_col) { + if (transp) { + dstride -= w; + sstride -= w; + while (h-- > 0) { + int wleft = w; + while (wleft-- > 0) { + if (*src != trans_col) + *dst = *src; + dst++; src++; + } + dst += dstride; + src += sstride; + } + } else + memcpy_pic(dst, src, w, h, dstride, sstride); +} + +static int demux_gif_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds) +{ + gif_priv_t *priv = demuxer->priv; + GifFileType *gif = priv->gif; + GifRecordType type = UNDEFINED_RECORD_TYPE; + int len = 0; + demux_packet_t *dp = NULL; + ColorMapObject *effective_map = NULL; + uint8_t *buf = NULL; + int refmode = 0; + int transparency = 0; + uint8_t transparent_col = 0; + + while (type != IMAGE_DESC_RECORD_TYPE) { + if (DGifGetRecordType(gif, &type) == GIF_ERROR) { + PrintGifError(); + return 0; // oops + } + if (type == TERMINATE_RECORD_TYPE) + return 0; // eof + if (type == SCREEN_DESC_RECORD_TYPE) { + if (DGifGetScreenDesc(gif) == GIF_ERROR) { + PrintGifError(); + return 0; // oops + } + } + if (type == EXTENSION_RECORD_TYPE) { + int code; + unsigned char *p = NULL; + if (DGifGetExtension(gif, &code, &p) == GIF_ERROR) { + PrintGifError(); + return 0; // oops + } + if (code == 0xF9) { + int frametime = 0; + if (p[0] == 4) // is the length correct? + { + transparency = p[1] & 1; + refmode = (p[1] >> 2) & 3; + // HACK: specification says + // > 0 - No disposal specified. The decoder is not required to take any action. + // but browsers treat it the same way as + // > 1 - Do not dispose. The graphic is to be left in place. + // Some broken files rely on this, e.g. + // http://samples.mplayerhq.hu/GIF/broken-gif/CLAIRE.GIF + if (refmode == 0) refmode = 1; + frametime = (p[3] << 8) | p[2]; // set the time, centiseconds + transparent_col = p[4]; + } + priv->current_pts += frametime; + } else if ((code == 0xFE) && (verbose)) { // comment extension + // print iff verbose + printf("GIF comment: "); + while (p != NULL) { + int length = p[0]; + char *comments = p + 1; + comments[length] = 0; + printf("%s", comments); + if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) { + PrintGifError(); + return 0; // oops + } + } + printf("\n"); + } + while (p != NULL) { + if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) { + PrintGifError(); + return 0; // oops + } + } + } + } + + if (DGifGetImageDesc(gif) == GIF_ERROR) { + PrintGifError(); + return 0; // oops + } + + buf = calloc(gif->Image.Width, gif->Image.Height); + len = gif->Image.Width * gif->Image.Height; + if (DGifGetLine(gif, buf, len) == GIF_ERROR) { + PrintGifError(); + free(buf); + return 0; // oops + } + + AVPacket *avpacket = talloc(NULL, AVPacket); + if (av_new_packet(avpacket, priv->w * priv->h) != 0) + abort(); + dp = new_demux_packet_fromdata(avpacket->data, avpacket->size); + dp->avpacket = avpacket; + + if (priv->useref) + memcpy(dp->buffer, priv->refimg, priv->w * priv->h); + else + memset(dp->buffer, gif->SBackGroundColor, priv->w * priv->h); + + effective_map = gif->Image.ColorMap; + if (effective_map == NULL) effective_map = gif->SColorMap; + + { + int y; + int cnt = FFMIN(effective_map->ColorCount, 256); + int l = av_clip(gif->Image.Left, 0, priv->w); + int t = av_clip(gif->Image.Top, 0, priv->h); + int w = av_clip(gif->Image.Width, 0, priv->w - l); + int h = av_clip(gif->Image.Height, 0, priv->h - t); + unsigned char *dest = dp->buffer + priv->w * t + l; + + // copy the palette + for (y = 0; y < cnt; y++) { + priv->palette[(y * 4) + 0] = effective_map->Colors[y].Blue; + priv->palette[(y * 4) + 1] = effective_map->Colors[y].Green; + priv->palette[(y * 4) + 2] = effective_map->Colors[y].Red; + priv->palette[(y * 4) + 3] = 0; + } + + if (gif->Image.Interlace) { + uint8_t *s = buf; + int ih = (h - 0 + 7) >> 3; + memcpy_transp_pic(dest, s, w, ih, + priv->w << 3, gif->Image.Width, + transparency, transparent_col); + s += ih * w; + ih = (h - 4 + 7) >> 3; + memcpy_transp_pic(dest + (priv->w << 2), s, w, ih, + priv->w << 3, gif->Image.Width, + transparency, transparent_col); + s += ih * w; + ih = (h - 2 + 3) >> 2; + memcpy_transp_pic(dest + (priv->w << 1), s, w, ih, + priv->w << 2, gif->Image.Width, + transparency, transparent_col); + s += ih * w; + ih = (h - 1 + 1) >> 1; + memcpy_transp_pic(dest + priv->w, s, w, ih, + priv->w << 1, gif->Image.Width, + transparency, transparent_col); + } else + memcpy_transp_pic(dest, buf, w, h, priv->w, gif->Image.Width, + transparency, transparent_col); + + if (refmode == 1) memcpy(priv->refimg, dp->buffer, priv->w * priv->h); + if (refmode == 2 && priv->useref) { + dest = priv->refimg + priv->w * t + l; + memset(buf, gif->SBackGroundColor, len); + memcpy_pic(dest, buf, w, h, priv->w, gif->Image.Width); + } + if (!(refmode & 2)) priv->useref = refmode & 1; + } + + free(buf); + + int palsize = 256 * 4; + uint8_t *pal = av_packet_new_side_data(avpacket, AV_PKT_DATA_PALETTE, + palsize); + if (pal) + memcpy(pal, priv->palette, palsize); + + demuxer->video->dpos++; + dp->pts = ((float)priv->current_pts) / 100; + dp->pos = stream_tell(demuxer->stream); + ds_add_packet(demuxer->video, dp); + + return 1; +} + +static demuxer_t* demux_open_gif(demuxer_t* demuxer) +{ + gif_priv_t *priv = calloc(1, sizeof(gif_priv_t)); + sh_video_t *sh_video = NULL; + GifFileType *gif = NULL; + + priv->current_pts = 0; + demuxer->seekable = 0; // FIXME + + // go back to the beginning + stream_seek(demuxer->stream,demuxer->stream->start_pos); + +#ifdef CONFIG_GIF_TVT_HACK + // without the TVT functionality of libungif, a hard seek must be + // done to the beginning of the file. this is because libgif is + // unable to use mplayer's cache, and without this lseek libgif will + // not read from the beginning of the file and the command will fail. + // with this hack enabled, you will lose the ability to stream a GIF. + lseek(demuxer->stream->fd, 0, SEEK_SET); + gif = DGifOpenFileHandle(demuxer->stream->fd); +#else + gif = DGifOpen(demuxer->stream, my_read_gif); +#endif + if (!gif) { + PrintGifError(); + free(priv); + return NULL; + } + + // create a new video stream header + sh_video = new_sh_video(demuxer, 0); + + // make sure the demuxer knows about the new video stream header + // (even though new_sh_video() ought to take care of it) + demuxer->video->sh = sh_video; + + // make sure that the video demuxer stream header knows about its + // parent video demuxer stream (this is getting wacky), or else + // video_read_properties() will choke + sh_video->ds = demuxer->video; + + sh_video->format = mmioFOURCC('r', 'a', 'w', ' '); + + sh_video->fps = 5.0f; + sh_video->frametime = 1.0f / sh_video->fps; + + int size = sizeof(*sh_video->bih) + (256 * 4); + sh_video->bih = calloc(1, size); + sh_video->bih->biSize = size; + sh_video->bih->biCompression = sh_video->format; + sh_video->bih->biWidth = priv->w = (uint16_t)gif->SWidth; + sh_video->bih->biHeight = priv->h = (uint16_t)gif->SHeight; + sh_video->bih->biBitCount = 8; + sh_video->bih->biPlanes = 2; + priv->palette = (unsigned char *)(sh_video->bih + 1); + priv->refimg = malloc(priv->w * priv->h); + + priv->gif = gif; + demuxer->priv = priv; + + return demuxer; +} + +static void demux_close_gif(demuxer_t* demuxer) +{ + gif_priv_t *priv = demuxer->priv; + if (!priv) return; + if (priv->gif && DGifCloseFile(priv->gif) == GIF_ERROR) + PrintGifError(); + free(priv->refimg); + free(priv); +} + + +const demuxer_desc_t demuxer_desc_gif = { + "GIF demuxer", + "gif", + "GIF", + "Joey Parrish", + "", + DEMUXER_TYPE_GIF, + 0, // unsafe autodetect + gif_check_file, + demux_gif_fill_buffer, + demux_open_gif, + demux_close_gif, + NULL, + NULL +}; diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c new file mode 100644 index 0000000000..35f3f6a83e --- /dev/null +++ b/demux/demux_lavf.c @@ -0,0 +1,1092 @@ +/* + * Copyright (C) 2004 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// #include <stdio.h> +#include <stdlib.h> +// #include <unistd.h> +#include <limits.h> +#include <stdbool.h> +#include <string.h> +#include <assert.h> + +#include <libavformat/avformat.h> +#include <libavformat/avio.h> +#include <libavutil/avutil.h> +#include <libavutil/avstring.h> +#include <libavutil/mathematics.h> +#include <libavutil/opt.h> +#include "libav_compat.h" + +#include "config.h" +#include "options.h" +#include "mp_msg.h" +#include "av_opts.h" +#include "bstr.h" + +#include "stream/stream.h" +#include "aviprint.h" +#include "demuxer.h" +#include "stheader.h" +#include "m_option.h" + +#include "mp_taglists.h" + +#define INITIAL_PROBE_SIZE STREAM_BUFFER_SIZE +#define SMALL_MAX_PROBE_SIZE (32 * 1024) +#define PROBE_BUF_SIZE (2 * 1024 * 1024) + +const m_option_t lavfdopts_conf[] = { + OPT_INTRANGE("probesize", lavfdopts.probesize, 0, 32, INT_MAX), + OPT_STRING("format", lavfdopts.format, 0), + OPT_INTRANGE("analyzeduration", lavfdopts.analyzeduration, 0, 0, INT_MAX), + OPT_STRING("cryptokey", lavfdopts.cryptokey, 0), + OPT_STRING("o", lavfdopts.avopt, 0), + {NULL, NULL, 0, 0, 0, 0, NULL} +}; + +#define BIO_BUFFER_SIZE 32768 + +typedef struct lavf_priv { + AVInputFormat *avif; + AVFormatContext *avfc; + AVIOContext *pb; + uint8_t buffer[BIO_BUFFER_SIZE]; + int audio_streams; + int video_streams; + int sub_streams; + int autoselect_sub; + int64_t last_pts; + int astreams[MAX_A_STREAMS]; + int vstreams[MAX_V_STREAMS]; + int sstreams[MAX_S_STREAMS]; + int cur_program; + int nb_streams_last; + bool internet_radio_hack; + bool use_dts; + bool seek_by_bytes; + int bitrate; +} lavf_priv_t; + +static int mp_read(void *opaque, uint8_t *buf, int size) +{ + struct demuxer *demuxer = opaque; + struct stream *stream = demuxer->stream; + int ret; + + ret = stream_read(stream, buf, size); + + mp_msg(MSGT_HEADER, MSGL_DBG2, + "%d=mp_read(%p, %p, %d), pos: %"PRId64", eof:%d\n", + ret, stream, buf, size, stream_tell(stream), stream->eof); + return ret; +} + +static int64_t mp_seek(void *opaque, int64_t pos, int whence) +{ + struct demuxer *demuxer = opaque; + struct stream *stream = demuxer->stream; + int64_t current_pos; + mp_msg(MSGT_HEADER, MSGL_DBG2, "mp_seek(%p, %"PRId64", %d)\n", + stream, pos, whence); + if (whence == SEEK_CUR) + pos += stream_tell(stream); + else if (whence == SEEK_END && stream->end_pos > 0) + pos += stream->end_pos; + else if (whence == SEEK_SET) + pos += stream->start_pos; + else if (whence == AVSEEK_SIZE && stream->end_pos > 0) { + off_t size; + if (stream_control(stream, STREAM_CTRL_GET_SIZE, &size) == STREAM_OK) + return size; + return stream->end_pos - stream->start_pos; + } else + return -1; + + if (pos < 0) + return -1; + current_pos = stream_tell(stream); + if (stream_seek(stream, pos) == 0) { + stream_reset(stream); + stream_seek(stream, current_pos); + return -1; + } + + return pos - stream->start_pos; +} + +static int64_t mp_read_seek(void *opaque, int stream_idx, int64_t ts, int flags) +{ + struct demuxer *demuxer = opaque; + struct stream *stream = demuxer->stream; + struct lavf_priv *priv = demuxer->priv; + + AVStream *st = priv->avfc->streams[stream_idx]; + double pts = (double)ts * st->time_base.num / st->time_base.den; + int ret = stream_control(stream, STREAM_CTRL_SEEK_TO_TIME, &pts); + if (ret < 0) + ret = AVERROR(ENOSYS); + return ret; +} + +static void list_formats(void) +{ + mp_msg(MSGT_DEMUX, MSGL_INFO, "Available lavf input formats:\n"); + AVInputFormat *fmt = NULL; + while ((fmt = av_iformat_next(fmt))) + mp_msg(MSGT_DEMUX, MSGL_INFO, "%15s : %s\n", fmt->name, fmt->long_name); +} + +static int lavf_check_file(demuxer_t *demuxer) +{ + struct MPOpts *opts = demuxer->opts; + struct lavfdopts *lavfdopts = &opts->lavfdopts; + AVProbeData avpd; + lavf_priv_t *priv; + int probe_data_size = 0; + int read_size = INITIAL_PROBE_SIZE; + int score; + + if (!demuxer->priv) + demuxer->priv = calloc(sizeof(lavf_priv_t), 1); + priv = demuxer->priv; + priv->autoselect_sub = -1; + + char *format = lavfdopts->format; + if (!format) + format = demuxer->stream->lavf_type; + if (format) { + if (strcmp(format, "help") == 0) { + list_formats(); + return 0; + } + priv->avif = av_find_input_format(format); + if (!priv->avif) { + mp_msg(MSGT_DEMUX, MSGL_FATAL, "Unknown lavf format %s\n", format); + return 0; + } + mp_msg(MSGT_DEMUX, MSGL_INFO, "Forced lavf %s demuxer\n", + priv->avif->long_name); + return DEMUXER_TYPE_LAVF; + } + + avpd.buf = av_mallocz(FFMAX(BIO_BUFFER_SIZE, PROBE_BUF_SIZE) + + FF_INPUT_BUFFER_PADDING_SIZE); + do { + read_size = stream_read(demuxer->stream, avpd.buf + probe_data_size, + read_size); + if (read_size < 0) { + av_free(avpd.buf); + return 0; + } + probe_data_size += read_size; + avpd.filename = demuxer->stream->url; + if (!avpd.filename) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "Stream url is not set!\n"); + avpd.filename = ""; + } + if (!strncmp(avpd.filename, "ffmpeg://", 9) || + !strncmp(avpd.filename, "lavf://", 7)) + avpd.filename += 9; + avpd.buf_size = probe_data_size; + + score = 0; + priv->avif = av_probe_input_format2(&avpd, probe_data_size > 0, &score); + read_size = FFMIN(2 * read_size, PROBE_BUF_SIZE - probe_data_size); + } while ((demuxer->desc->type != DEMUXER_TYPE_LAVF_PREFERRED || + probe_data_size < SMALL_MAX_PROBE_SIZE) && + score <= AVPROBE_SCORE_MAX / 4 && + read_size > 0 && probe_data_size < PROBE_BUF_SIZE); + av_free(avpd.buf); + + if (!priv->avif || score <= AVPROBE_SCORE_MAX / 4) { + mp_msg(MSGT_HEADER, MSGL_V, + "LAVF_check: no clue about this gibberish!\n"); + return 0; + } else + mp_msg(MSGT_HEADER, MSGL_V, "LAVF_check: %s\n", priv->avif->long_name); + + demuxer->filetype = priv->avif->long_name; + if (!demuxer->filetype) + demuxer->filetype = priv->avif->name; + + return DEMUXER_TYPE_LAVF; +} + +static bool matches_avinputformat_name(struct lavf_priv *priv, + const char *name) +{ + const char *avifname = priv->avif->name; + while (1) { + const char *next = strchr(avifname, ','); + if (!next) + return !strcmp(avifname, name); + int len = next - avifname; + if (len == strlen(name) && !memcmp(avifname, name, len)) + return true; + avifname = next + 1; + } +} + +/* formats for which an internal demuxer is preferred */ +static const char * const preferred_internal[] = { + /* lavf Matroska demuxer doesn't support ordered chapters and fails + * for more files */ + "matroska", + NULL +}; + +static int lavf_check_preferred_file(demuxer_t *demuxer) +{ + if (lavf_check_file(demuxer)) { + const char * const *p; + lavf_priv_t *priv = demuxer->priv; + for (p = preferred_internal; *p; p++) + if (matches_avinputformat_name(priv, *p)) + return 0; + return DEMUXER_TYPE_LAVF_PREFERRED; + } + return 0; +} + +static uint8_t char2int(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return 0; +} + +static void parse_cryptokey(AVFormatContext *avfc, const char *str) +{ + int len = strlen(str) / 2; + uint8_t *key = av_mallocz(len); + int i; + avfc->keylen = len; + avfc->key = key; + for (i = 0; i < len; i++, str += 2) + *key++ = (char2int(str[0]) << 4) | char2int(str[1]); +} + +static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) +{ + lavf_priv_t *priv = demuxer->priv; + AVStream *st = avfc->streams[i]; + AVCodecContext *codec = st->codec; + char *stream_type = NULL; + int stream_id; + AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); + AVDictionaryEntry *title = av_dict_get(st->metadata, "title", NULL, 0); + // Work around collisions resulting from the hacks changing codec_tag. + int lavf_codec_tag = codec->codec_tag; + // Don't use native MPEG codec tag values with our generic tag tables. + // May contain for example value 3 for MP3, which we'd map to PCM audio. + if (matches_avinputformat_name(priv, "mpeg") || + matches_avinputformat_name(priv, "mpegts")) + codec->codec_tag = 0; + int override_tag = mp_taglist_override(codec->codec_id); + // For some formats (like PCM) always trust CODEC_ID_* more than codec_tag + if (override_tag) + codec->codec_tag = override_tag; + + AVCodec *avc = avcodec_find_decoder(codec->codec_id); + const char *codec_name = avc ? avc->name : "unknown"; + + bool set_demuxer_id = matches_avinputformat_name(priv, "mpeg"); + + switch (codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: { + WAVEFORMATEX *wf; + sh_audio_t *sh_audio; + sh_audio = new_sh_audio_aid(demuxer, i, priv->audio_streams); + if (!sh_audio) + break; + sh_audio->demuxer_codecname = codec_name; + if (set_demuxer_id) + sh_audio->gsh->demuxer_id = st->id; + stream_type = "audio"; + priv->astreams[priv->audio_streams] = i; + sh_audio->libav_codec_id = codec->codec_id; + sh_audio->gsh->lavf_codec_tag = lavf_codec_tag; + wf = calloc(sizeof(*wf) + codec->extradata_size, 1); + // mp4a tag is used for all mp4 files no matter what they actually contain + if (codec->codec_tag == MKTAG('m', 'p', '4', 'a')) + codec->codec_tag = 0; + if (!codec->codec_tag) + codec->codec_tag = mp_taglist_audio(codec->codec_id); + if (!codec->codec_tag) + codec->codec_tag = -1; + wf->wFormatTag = codec->codec_tag; + wf->nChannels = codec->channels; + wf->nSamplesPerSec = codec->sample_rate; + wf->nAvgBytesPerSec = codec->bit_rate / 8; + wf->nBlockAlign = codec->block_align; + wf->wBitsPerSample = codec->bits_per_coded_sample; + wf->cbSize = codec->extradata_size; + if (codec->extradata_size) + memcpy(wf + 1, codec->extradata, codec->extradata_size); + sh_audio->wf = wf; + sh_audio->audio.dwSampleSize = codec->block_align; + if (codec->frame_size && codec->sample_rate) { + sh_audio->audio.dwScale = codec->frame_size; + sh_audio->audio.dwRate = codec->sample_rate; + } else { + sh_audio->audio.dwScale = codec->block_align ? codec->block_align * 8 : 8; + sh_audio->audio.dwRate = codec->bit_rate; + } + int g = av_gcd(sh_audio->audio.dwScale, sh_audio->audio.dwRate); + sh_audio->audio.dwScale /= g; + sh_audio->audio.dwRate /= g; +// printf("sca:%d rat:%d fs:%d sr:%d ba:%d\n", sh_audio->audio.dwScale, sh_audio->audio.dwRate, codec->frame_size, codec->sample_rate, codec->block_align); + sh_audio->ds = demuxer->audio; + sh_audio->format = codec->codec_tag; + sh_audio->channels = codec->channels; + sh_audio->samplerate = codec->sample_rate; + sh_audio->i_bps = codec->bit_rate / 8; + switch (codec->codec_id) { + case CODEC_ID_PCM_S8: + case CODEC_ID_PCM_U8: + sh_audio->samplesize = 1; + break; + case CODEC_ID_PCM_S16LE: + case CODEC_ID_PCM_S16BE: + case CODEC_ID_PCM_U16LE: + case CODEC_ID_PCM_U16BE: + sh_audio->samplesize = 2; + break; + case CODEC_ID_PCM_ALAW: + sh_audio->format = 0x6; + break; + case CODEC_ID_PCM_MULAW: + sh_audio->format = 0x7; + break; + } + if (title && title->value) { + sh_audio->gsh->title = talloc_strdup(sh_audio, title->value); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_NAME=%s\n", + priv->audio_streams, title->value); + } + if (lang && lang->value) { + sh_audio->lang = talloc_strdup(sh_audio, lang->value); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_LANG=%s\n", + priv->audio_streams, sh_audio->lang); + } + if (st->disposition & AV_DISPOSITION_DEFAULT) + sh_audio->gsh->default_track = 1; + if (mp_msg_test(MSGT_HEADER, MSGL_V)) + print_wave_header(sh_audio->wf, MSGL_V); + st->discard = AVDISCARD_ALL; + stream_id = priv->audio_streams++; + break; + } + case AVMEDIA_TYPE_VIDEO: { + sh_video_t *sh_video; + BITMAPINFOHEADER *bih; + sh_video = new_sh_video_vid(demuxer, i, priv->video_streams); + if (!sh_video) + break; + sh_video->demuxer_codecname = codec_name; + if (set_demuxer_id) + sh_video->gsh->demuxer_id = st->id; + stream_type = "video"; + priv->vstreams[priv->video_streams] = i; + sh_video->libav_codec_id = codec->codec_id; + sh_video->gsh->lavf_codec_tag = lavf_codec_tag; + bih = calloc(sizeof(*bih) + codec->extradata_size, 1); + + if (codec->codec_id == CODEC_ID_RAWVIDEO) { + switch (codec->pix_fmt) { + case PIX_FMT_RGB24: + codec->codec_tag = MKTAG(24, 'B', 'G', 'R'); + case PIX_FMT_BGR24: + codec->codec_tag = MKTAG(24, 'R', 'G', 'B'); + } + if (!codec->codec_tag) + codec->codec_tag = avcodec_pix_fmt_to_codec_tag(codec->pix_fmt); + } else if (!codec->codec_tag) { + codec->codec_tag = mp_taglist_video(codec->codec_id); + /* 0 might mean either unset or rawvideo; if codec_id + * was not RAWVIDEO assume it's unset + */ + if (!codec->codec_tag) + codec->codec_tag = -1; + } + bih->biSize = sizeof(*bih) + codec->extradata_size; + bih->biWidth = codec->width; + bih->biHeight = codec->height; + bih->biBitCount = codec->bits_per_coded_sample; + bih->biSizeImage = bih->biWidth * bih->biHeight * bih->biBitCount / 8; + bih->biCompression = codec->codec_tag; + sh_video->bih = bih; + sh_video->disp_w = codec->width; + sh_video->disp_h = codec->height; + if (st->time_base.den) { /* if container has time_base, use that */ + sh_video->video.dwRate = st->time_base.den; + sh_video->video.dwScale = st->time_base.num; + } else { + sh_video->video.dwRate = codec->time_base.den; + sh_video->video.dwScale = codec->time_base.num; + } + /* Try to make up some frame rate value, even if it's not reliable. + * FPS information is needed to support subtitle formats which base + * timing on frame numbers. + * Libavformat seems to report no "reliable" FPS value for AVI files, + * while they are typically constant enough FPS that the value this + * heuristic makes up works with subtitles in practice. + */ + double fps; + if (st->r_frame_rate.num) + fps = av_q2d(st->r_frame_rate); + else + fps = 1.0 / FFMAX(av_q2d(st->time_base), + av_q2d(st->codec->time_base) * + st->codec->ticks_per_frame); + sh_video->fps = fps; + sh_video->frametime = 1 / fps; + sh_video->format = bih->biCompression; + if (st->sample_aspect_ratio.num) + sh_video->aspect = codec->width * st->sample_aspect_ratio.num + / (float)(codec->height * st->sample_aspect_ratio.den); + else + sh_video->aspect = codec->width * codec->sample_aspect_ratio.num + / (float)(codec->height * codec->sample_aspect_ratio.den); + sh_video->i_bps = codec->bit_rate / 8; + if (title && title->value) { + sh_video->gsh->title = talloc_strdup(sh_video, title->value); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VID_%d_NAME=%s\n", + priv->video_streams, title->value); + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, "aspect= %d*%d/(%d*%d)\n", + codec->width, codec->sample_aspect_ratio.num, + codec->height, codec->sample_aspect_ratio.den); + + sh_video->ds = demuxer->video; + if (codec->extradata_size) + memcpy(sh_video->bih + 1, codec->extradata, codec->extradata_size); + if ( mp_msg_test(MSGT_HEADER, MSGL_V)) + print_video_header(sh_video->bih, MSGL_V); + if (demuxer->video->id != priv->video_streams + && demuxer->video->id != -1) + st->discard = AVDISCARD_ALL; + else { + demuxer->video->id = i; + demuxer->video->sh = demuxer->v_streams[i]; + } + stream_id = priv->video_streams++; + break; + } + case AVMEDIA_TYPE_SUBTITLE: { + sh_sub_t *sh_sub; + char type; + if (codec->codec_id == CODEC_ID_TEXT || + codec->codec_id == AV_CODEC_ID_SUBRIP) + type = 't'; + else if (codec->codec_id == CODEC_ID_MOV_TEXT) + type = 'm'; + else if (codec->codec_id == CODEC_ID_SSA) + type = 'a'; + else if (codec->codec_id == CODEC_ID_DVD_SUBTITLE) + type = 'v'; + else if (codec->codec_id == CODEC_ID_XSUB) + type = 'x'; + else if (codec->codec_id == CODEC_ID_DVB_SUBTITLE) + type = 'b'; + else if (codec->codec_id == CODEC_ID_DVB_TELETEXT) + type = 'd'; + else if (codec->codec_id == CODEC_ID_HDMV_PGS_SUBTITLE) + type = 'p'; + else + break; + sh_sub = new_sh_sub_sid(demuxer, i, priv->sub_streams); + if (!sh_sub) + break; + sh_sub->demuxer_codecname = codec_name; + if (set_demuxer_id) + sh_sub->gsh->demuxer_id = st->id; + stream_type = "subtitle"; + priv->sstreams[priv->sub_streams] = i; + sh_sub->libav_codec_id = codec->codec_id; + sh_sub->gsh->lavf_codec_tag = lavf_codec_tag; + sh_sub->type = type; + if (codec->extradata_size) { + sh_sub->extradata = malloc(codec->extradata_size); + memcpy(sh_sub->extradata, codec->extradata, codec->extradata_size); + sh_sub->extradata_len = codec->extradata_size; + } + if (title && title->value) { + sh_sub->gsh->title = talloc_strdup(sh_sub, title->value); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_NAME=%s\n", + priv->sub_streams, title->value); + } + if (lang && lang->value) { + sh_sub->lang = talloc_strdup(sh_sub, lang->value); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_LANG=%s\n", + priv->sub_streams, sh_sub->lang); + } + if (st->disposition & AV_DISPOSITION_DEFAULT) + sh_sub->gsh->default_track = 1; + stream_id = priv->sub_streams++; + break; + } + case AVMEDIA_TYPE_ATTACHMENT: { + AVDictionaryEntry *ftag = av_dict_get(st->metadata, "filename", + NULL, 0); + char *filename = ftag ? ftag->value : NULL; + if (st->codec->codec_id == CODEC_ID_TTF) + demuxer_add_attachment(demuxer, bstr0(filename), + bstr0("application/x-truetype-font"), + (struct bstr){codec->extradata, + codec->extradata_size}); + break; + } + default: + st->discard = AVDISCARD_ALL; + } + if (stream_type) { + if (!avc && *stream_type == 's' && demuxer->s_streams[i]) + codec_name = sh_sub_type2str((demuxer->s_streams[i])->type); + mp_msg(MSGT_DEMUX, MSGL_V, "[lavf] stream %d: %s (%s), -%cid %d", + i, stream_type, codec_name, *stream_type, stream_id); + if (lang && lang->value && *stream_type != 'v') + mp_msg(MSGT_DEMUX, MSGL_V, ", -%clang %s", + *stream_type, lang->value); + if (title && title->value) + mp_msg(MSGT_DEMUX, MSGL_V, ", %s", title->value); + mp_msg(MSGT_DEMUX, MSGL_V, "\n"); + } +} + +static demuxer_t *demux_open_lavf(demuxer_t *demuxer) +{ + struct MPOpts *opts = demuxer->opts; + struct lavfdopts *lavfdopts = &opts->lavfdopts; + AVFormatContext *avfc; + AVDictionaryEntry *t = NULL; + lavf_priv_t *priv = demuxer->priv; + int i; + char mp_filename[256] = "mp:"; + + stream_seek(demuxer->stream, 0); + + avfc = avformat_alloc_context(); + + if (lavfdopts->cryptokey) + parse_cryptokey(avfc, lavfdopts->cryptokey); + if (matches_avinputformat_name(priv, "avi")) { + /* for avi libavformat returns the avi timestamps in .dts, + * some made-up stuff that's not really pts in .pts */ + priv->use_dts = true; + demuxer->timestamp_type = TIMESTAMP_TYPE_SORT; + } else { + if (opts->user_correct_pts != 0) + avfc->flags |= AVFMT_FLAG_GENPTS; + } + if (index_mode == 0) + avfc->flags |= AVFMT_FLAG_IGNIDX; + + if (lavfdopts->probesize) { + if (av_opt_set_int(avfc, "probesize", lavfdopts->probesize, 0) < 0) + mp_msg(MSGT_HEADER, MSGL_ERR, + "demux_lavf, couldn't set option probesize to %u\n", + lavfdopts->probesize); + } + if (lavfdopts->analyzeduration) { + if (av_opt_set_int(avfc, "analyzeduration", + lavfdopts->analyzeduration * AV_TIME_BASE, 0) < 0) + mp_msg(MSGT_HEADER, MSGL_ERR, "demux_lavf, couldn't set option " + "analyzeduration to %u\n", lavfdopts->analyzeduration); + } + + if (lavfdopts->avopt) { + if (parse_avopts(avfc, lavfdopts->avopt) < 0) { + mp_msg(MSGT_HEADER, MSGL_ERR, + "Your options /%s/ look like gibberish to me pal\n", + lavfdopts->avopt); + return NULL; + } + } + + if (demuxer->stream->url) { + if (demuxer->stream->lavf_type && !strcmp(demuxer->stream->lavf_type, + "rtsp")) { + // Remove possible leading ffmpeg:// or lavf:// + char *name = strstr(demuxer->stream->url, "rtsp:"); + av_strlcpy(mp_filename, name, sizeof(mp_filename)); + } else + av_strlcat(mp_filename, demuxer->stream->url, sizeof(mp_filename)); + } else + av_strlcat(mp_filename, "foobar.dummy", sizeof(mp_filename)); + + if (!(priv->avif->flags & AVFMT_NOFILE)) { + priv->pb = avio_alloc_context(priv->buffer, BIO_BUFFER_SIZE, 0, + demuxer, mp_read, NULL, mp_seek); + priv->pb->read_seek = mp_read_seek; + priv->pb->seekable = demuxer->stream->end_pos + && (demuxer->stream->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK + ? AVIO_SEEKABLE_NORMAL : 0; + avfc->pb = priv->pb; + } + + if (avformat_open_input(&avfc, mp_filename, priv->avif, NULL) < 0) { + mp_msg(MSGT_HEADER, MSGL_ERR, + "LAVF_header: avformat_open_input() failed\n"); + return NULL; + } + + priv->avfc = avfc; + + if (avformat_find_stream_info(avfc, NULL) < 0) { + mp_msg(MSGT_HEADER, MSGL_ERR, + "LAVF_header: av_find_stream_info() failed\n"); + return NULL; + } + + /* Add metadata. */ + while ((t = av_dict_get(avfc->metadata, "", t, + AV_DICT_IGNORE_SUFFIX))) + demux_info_add(demuxer, t->key, t->value); + + for (i = 0; i < avfc->nb_chapters; i++) { + AVChapter *c = avfc->chapters[i]; + uint64_t start = av_rescale_q(c->start, c->time_base, + (AVRational){1, 1000000000}); + uint64_t end = av_rescale_q(c->end, c->time_base, + (AVRational){1, 1000000000}); + t = av_dict_get(c->metadata, "title", NULL, 0); + demuxer_add_chapter(demuxer, t ? bstr0(t->value) : bstr0(NULL), + start, end); + } + + for (i = 0; i < avfc->nb_streams; i++) + handle_stream(demuxer, avfc, i); + priv->nb_streams_last = avfc->nb_streams; + + if (avfc->nb_programs) { + int p; + for (p = 0; p < avfc->nb_programs; p++) { + AVProgram *program = avfc->programs[p]; + t = av_dict_get(program->metadata, "title", NULL, 0); + mp_msg(MSGT_HEADER, MSGL_INFO, "LAVF: Program %d %s\n", + program->id, t ? t->value : ""); + mp_msg(MSGT_IDENTIFY, MSGL_V, "PROGRAM_ID=%d\n", program->id); + } + } + + mp_msg(MSGT_HEADER, MSGL_V, "LAVF: %d audio and %d video streams found\n", + priv->audio_streams, priv->video_streams); + mp_msg(MSGT_HEADER, MSGL_V, "LAVF: build %d\n", LIBAVFORMAT_BUILD); + demuxer->audio->id = -2; // wait for higher-level code to select track + if (!priv->video_streams) { + if (!priv->audio_streams) { + mp_msg(MSGT_HEADER, MSGL_ERR, + "LAVF: no audio or video headers found - broken file?\n"); + return NULL; + } + demuxer->video->id = -2; // audio-only + } + + // disabled because unreliable per-stream bitrate values returned + // by libavformat trigger this heuristic incorrectly and break things +#if 0 + /* libavformat sets bitrate for mpeg based on pts at start and end + * of file, which fails for files with pts resets. So calculate our + * own bitrate estimate. */ + if (priv->avif->flags & AVFMT_TS_DISCONT) { + for (int i = 0; i < avfc->nb_streams; i++) + priv->bitrate += avfc->streams[i]->codec->bit_rate; + /* pts-based is more accurate if there are no resets; try to make + * a somewhat reasonable guess */ + if (!avfc->duration || avfc->duration == AV_NOPTS_VALUE + || priv->bitrate && (avfc->bit_rate < priv->bitrate / 2 + || avfc->bit_rate > priv->bitrate * 2)) + priv->seek_by_bytes = true; + if (!priv->bitrate) + priv->bitrate = 1440000; + } +#endif + demuxer->accurate_seek = !priv->seek_by_bytes; + + return demuxer; +} + +static void check_internet_radio_hack(struct demuxer *demuxer) +{ + struct lavf_priv *priv = demuxer->priv; + struct AVFormatContext *avfc = priv->avfc; + + if (!matches_avinputformat_name(priv, "ogg")) + return; + if (priv->nb_streams_last == avfc->nb_streams) + return; + if (avfc->nb_streams - priv->nb_streams_last == 1 + && priv->video_streams == 0 && priv->sub_streams == 0 + && demuxer->a_streams[priv->audio_streams - 1]->format == 0x566f // vorbis + && (priv->audio_streams == 2 || priv->internet_radio_hack) + && demuxer->a_streams[0]->format == 0x566f) { + // extradata match could be checked but would require parsing + // headers, as the comment section will vary + if (!priv->internet_radio_hack) { + mp_msg(MSGT_DEMUX, MSGL_V, + "[lavf] enabling internet ogg radio hack\n"); + } + priv->internet_radio_hack = true; + // use new per-track metadata as global metadata + AVDictionaryEntry *t = NULL; + AVStream *stream = avfc->streams[avfc->nb_streams - 1]; + while ((t = av_dict_get(stream->metadata, "", t, + AV_DICT_IGNORE_SUFFIX))) + demux_info_add(demuxer, t->key, t->value); + } else { + if (priv->internet_radio_hack) + mp_tmsg(MSGT_DEMUX, MSGL_WARN, "[lavf] Internet radio ogg hack " + "was enabled, but stream characteristics changed.\n" + "This may or may not work.\n"); + priv->internet_radio_hack = false; + } +} + +static int destroy_avpacket(void *pkt) +{ + av_free_packet(pkt); + return 0; +} + +static int demux_lavf_fill_buffer(demuxer_t *demux, demux_stream_t *dsds) +{ + lavf_priv_t *priv = demux->priv; + demux_packet_t *dp; + demux_stream_t *ds; + int id; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "demux_lavf_fill_buffer()\n"); + + demux->filepos = stream_tell(demux->stream); + + AVPacket *pkt = talloc(NULL, AVPacket); + if (av_read_frame(priv->avfc, pkt) < 0) { + talloc_free(pkt); + return 0; + } + talloc_set_destructor(pkt, destroy_avpacket); + + // handle any new streams that might have been added + for (id = priv->nb_streams_last; id < priv->avfc->nb_streams; id++) + handle_stream(demux, priv->avfc, id); + check_internet_radio_hack(demux); + + priv->nb_streams_last = priv->avfc->nb_streams; + + id = pkt->stream_index; + + assert(id >= 0 && id < MAX_S_STREAMS); + if (demux->s_streams[id] && demux->sub->id == -1 && + demux->s_streams[id]->gsh->demuxer_id == priv->autoselect_sub) + { + priv->autoselect_sub = -1; + demux->sub->id = id; + } + + if (id == demux->audio->id || priv->internet_radio_hack) { + // audio + ds = demux->audio; + if (!ds->sh) { + ds->sh = demux->a_streams[id]; + mp_msg(MSGT_DEMUX, MSGL_V, "Auto-selected LAVF audio ID = %d\n", + ds->id); + } + } else if (id == demux->video->id) { + // video + ds = demux->video; + if (!ds->sh) { + ds->sh = demux->v_streams[id]; + mp_msg(MSGT_DEMUX, MSGL_V, "Auto-selected LAVF video ID = %d\n", + ds->id); + } + } else if (id == demux->sub->id) { + // subtitle + ds = demux->sub; + } else { + talloc_free(pkt); + return 1; + } + + // If the packet has pointers to temporary fields that could be + // overwritten/freed by next av_read_frame(), copy them to persistent + // allocations so we can safely queue the packet for any length of time. + if (av_dup_packet(pkt) < 0) + abort(); + dp = new_demux_packet_fromdata(pkt->data, pkt->size); + dp->avpacket = pkt; + + int64_t ts = priv->use_dts ? pkt->dts : pkt->pts; + if (ts != AV_NOPTS_VALUE) { + dp->pts = ts * av_q2d(priv->avfc->streams[id]->time_base); + priv->last_pts = dp->pts * AV_TIME_BASE; + // always set duration for subtitles, even if AV_PKT_FLAG_KEY isn't set, + // otherwise they will stay on screen to long if e.g. ASS is demuxed + // from mkv + if ((ds == demux->sub || (pkt->flags & AV_PKT_FLAG_KEY)) && + pkt->convergence_duration > 0) + dp->duration = pkt->convergence_duration * + av_q2d(priv->avfc->streams[id]->time_base); + } + dp->pos = demux->filepos; + dp->keyframe = pkt->flags & AV_PKT_FLAG_KEY; + // append packet to DS stream: + ds_add_packet(ds, dp); + return 1; +} + +static void demux_seek_lavf(demuxer_t *demuxer, float rel_seek_secs, + float audio_delay, int flags) +{ + lavf_priv_t *priv = demuxer->priv; + int avsflags = 0; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "demux_seek_lavf(%p, %f, %f, %d)\n", + demuxer, rel_seek_secs, audio_delay, flags); + + if (priv->seek_by_bytes) { + int64_t pos = demuxer->filepos; + rel_seek_secs *= priv->bitrate / 8; + pos += rel_seek_secs; + av_seek_frame(priv->avfc, -1, pos, AVSEEK_FLAG_BYTE); + return; + } + + if (flags & SEEK_ABSOLUTE) + priv->last_pts = 0; + else if (rel_seek_secs < 0) + avsflags = AVSEEK_FLAG_BACKWARD; + if (flags & SEEK_FORWARD) + avsflags = 0; + else if (flags & SEEK_BACKWARD) + avsflags = AVSEEK_FLAG_BACKWARD; + if (flags & SEEK_FACTOR) { + if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE) + return; + priv->last_pts += rel_seek_secs * priv->avfc->duration; + } else + priv->last_pts += rel_seek_secs * AV_TIME_BASE; + if (av_seek_frame(priv->avfc, -1, priv->last_pts, avsflags) < 0) { + avsflags ^= AVSEEK_FLAG_BACKWARD; + av_seek_frame(priv->avfc, -1, priv->last_pts, avsflags); + } +} + +static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg) +{ + lavf_priv_t *priv = demuxer->priv; + + switch (cmd) { + case DEMUXER_CTRL_CORRECT_PTS: + return DEMUXER_CTRL_OK; + case DEMUXER_CTRL_GET_TIME_LENGTH: + if (priv->seek_by_bytes) { + /* Our bitrate estimate may be better than would be used in + * otherwise similar fallback code at higher level */ + if (demuxer->movi_end <= 0) + return DEMUXER_CTRL_DONTKNOW; + *(double *)arg = (demuxer->movi_end - demuxer->movi_start) * 8 / + priv->bitrate; + return DEMUXER_CTRL_GUESS; + } + if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE) + return DEMUXER_CTRL_DONTKNOW; + + *((double *)arg) = (double)priv->avfc->duration / AV_TIME_BASE; + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_GET_PERCENT_POS: + if (priv->seek_by_bytes) + return DEMUXER_CTRL_DONTKNOW; // let it use the fallback code + if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE) + return DEMUXER_CTRL_DONTKNOW; + + *((int *)arg) = (int)((priv->last_pts - priv->avfc->start_time) * 100 / + priv->avfc->duration); + return DEMUXER_CTRL_OK; + case DEMUXER_CTRL_SWITCH_AUDIO: + case DEMUXER_CTRL_SWITCH_VIDEO: + { + int id = *((int *)arg); + int newid = -2; + int i, curridx = -1; + int nstreams, *pstreams; + demux_stream_t *ds; + + if (cmd == DEMUXER_CTRL_SWITCH_VIDEO) { + ds = demuxer->video; + nstreams = priv->video_streams; + pstreams = priv->vstreams; + } else { + ds = demuxer->audio; + nstreams = priv->audio_streams; + pstreams = priv->astreams; + } + for (i = 0; i < nstreams; i++) { + if (pstreams[i] == ds->id) { //current stream id + curridx = i; + break; + } + } + + if (id == -1) { // next track + i = (curridx + 2) % (nstreams + 1) - 1; + if (i >= 0) + newid = pstreams[i]; + } else if (id >= 0 && id < nstreams) { // select track by id + i = id; + newid = pstreams[i]; + } else // no sound + i = -1; + + if (i == curridx) { + *(int *) arg = curridx < 0 ? -2 : curridx; + return DEMUXER_CTRL_OK; + } else { + ds_free_packs(ds); + if (ds->id >= 0) + priv->avfc->streams[ds->id]->discard = AVDISCARD_ALL; + ds->id = newid; + *(int *) arg = i < 0 ? -2 : i; + if (newid >= 0) + priv->avfc->streams[newid]->discard = AVDISCARD_NONE; + return DEMUXER_CTRL_OK; + } + } + case DEMUXER_CTRL_AUTOSELECT_SUBTITLE: + { + demuxer->sub->id = -1; + priv->autoselect_sub = *((int *)arg); + return DEMUXER_CTRL_OK; + } + case DEMUXER_CTRL_IDENTIFY_PROGRAM: + { + demux_program_t *prog = arg; + AVProgram *program; + int p, i; + int start; + + prog->vid = prog->aid = prog->sid = -2; + if (priv->avfc->nb_programs < 1) + return DEMUXER_CTRL_DONTKNOW; + + if (prog->progid == -1) { + p = 0; + while (p < priv->avfc->nb_programs && priv->avfc->programs[p]->id != priv->cur_program) + p++; + p = (p + 1) % priv->avfc->nb_programs; + } else { + for (i = 0; i < priv->avfc->nb_programs; i++) + if (priv->avfc->programs[i]->id == prog->progid) + break; + if (i == priv->avfc->nb_programs) + return DEMUXER_CTRL_DONTKNOW; + p = i; + } + start = p; +redo: + program = priv->avfc->programs[p]; + for (i = 0; i < program->nb_stream_indexes; i++) { + switch (priv->avfc->streams[program->stream_index[i]]->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (prog->vid == -2) + prog->vid = program->stream_index[i]; + break; + case AVMEDIA_TYPE_AUDIO: + if (prog->aid == -2) + prog->aid = program->stream_index[i]; + break; + case AVMEDIA_TYPE_SUBTITLE: + if (prog->sid == -2) + prog->sid = program->stream_index[i]; + break; + } + } + if (prog->aid >= 0 && prog->aid < MAX_A_STREAMS && + demuxer->a_streams[prog->aid]) { + sh_audio_t *sh = demuxer->a_streams[prog->aid]; + prog->aid = sh->aid; + } else + prog->aid = -2; + if (prog->vid >= 0 && prog->vid < MAX_V_STREAMS && + demuxer->v_streams[prog->vid]) { + sh_video_t *sh = demuxer->v_streams[prog->vid]; + prog->vid = sh->vid; + } else + prog->vid = -2; + if (prog->progid == -1 && prog->vid == -2 && prog->aid == -2) { + p = (p + 1) % priv->avfc->nb_programs; + if (p == start) + return DEMUXER_CTRL_DONTKNOW; + goto redo; + } + priv->cur_program = prog->progid = program->id; + return DEMUXER_CTRL_OK; + } + default: + return DEMUXER_CTRL_NOTIMPL; + } +} + +static void demux_close_lavf(demuxer_t *demuxer) +{ + lavf_priv_t *priv = demuxer->priv; + if (priv) { + if (priv->avfc) { + av_freep(&priv->avfc->key); + avformat_close_input(&priv->avfc); + } + av_freep(&priv->pb); + free(priv); + demuxer->priv = NULL; + } +} + + +const demuxer_desc_t demuxer_desc_lavf = { + "libavformat demuxer", + "lavf", + "libavformat", + "Michael Niedermayer", + "supports many formats, requires libavformat", + DEMUXER_TYPE_LAVF, + 0, // Check after other demuxer + lavf_check_file, + demux_lavf_fill_buffer, + demux_open_lavf, + demux_close_lavf, + demux_seek_lavf, + demux_lavf_control +}; + +const demuxer_desc_t demuxer_desc_lavf_preferred = { + "libavformat preferred demuxer", + "lavfpref", + "libavformat", + "Michael Niedermayer", + "supports many formats, requires libavformat", + DEMUXER_TYPE_LAVF_PREFERRED, + 1, + lavf_check_preferred_file, + demux_lavf_fill_buffer, + demux_open_lavf, + demux_close_lavf, + demux_seek_lavf, + demux_lavf_control +}; diff --git a/demux/demux_mf.c b/demux/demux_mf.c new file mode 100644 index 0000000000..6f0f0de897 --- /dev/null +++ b/demux/demux_mf.c @@ -0,0 +1,226 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "osdep/io.h" + +#include "talloc.h" +#include "config.h" +#include "mp_msg.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "mf.h" + +static void demux_seek_mf(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags){ + mf_t * mf = (mf_t *)demuxer->priv; + sh_video_t * sh_video = demuxer->video->sh; + int newpos = (flags & SEEK_ABSOLUTE)?0:mf->curr_frame - 1; + + if ( flags & SEEK_FACTOR ) newpos+=rel_seek_secs*(mf->nr_of_files - 1); + else newpos+=rel_seek_secs * sh_video->fps; + if ( newpos < 0 ) newpos=0; + if( newpos >= mf->nr_of_files) newpos=mf->nr_of_files - 1; + demuxer->filepos=mf->curr_frame=newpos; +} + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +static int demux_mf_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds){ + mf_t * mf; + FILE * f; + + mf=(mf_t*)demuxer->priv; + if ( mf->curr_frame >= mf->nr_of_files ) return 0; + + if ( !( f=fopen( mf->names[mf->curr_frame],"rb" ) ) ) return 0; + { + sh_video_t * sh_video = demuxer->video->sh; + fseek(f, 0, SEEK_END); + long file_size = ftell(f); + fseek(f, 0, SEEK_SET); + demux_packet_t * dp = new_demux_packet( file_size ); + if ( !fread( dp->buffer,file_size,1,f ) ) return 0; + dp->pts=mf->curr_frame / sh_video->fps; + dp->pos=mf->curr_frame; + dp->keyframe = true; + // append packet to DS stream: + ds_add_packet( demuxer->video,dp ); + } + fclose( f ); + + demuxer->filepos=mf->curr_frame++; + return 1; +} + +// force extension/type to have a fourcc + +static const struct { + const char *type; + uint32_t format; +} type2format[] = { + { "bmp", mmioFOURCC('b', 'm', 'p', ' ') }, + { "dpx", mmioFOURCC('d', 'p', 'x', ' ') }, + { "j2c", mmioFOURCC('M', 'J', '2', 'C') }, + { "j2k", mmioFOURCC('M', 'J', '2', 'C') }, + { "jp2", mmioFOURCC('M', 'J', '2', 'C') }, + { "jpc", mmioFOURCC('M', 'J', '2', 'C') }, + { "jpeg", mmioFOURCC('I', 'J', 'P', 'G') }, + { "jpg", mmioFOURCC('I', 'J', 'P', 'G') }, + { "jps", mmioFOURCC('I', 'J', 'P', 'G') }, + { "jls", mmioFOURCC('I', 'J', 'P', 'G') }, + { "thm", mmioFOURCC('I', 'J', 'P', 'G') }, + { "db", mmioFOURCC('I', 'J', 'P', 'G') }, + { "pcx", mmioFOURCC('p', 'c', 'x', ' ') }, + { "png", mmioFOURCC('M', 'P', 'N', 'G') }, + { "pns", mmioFOURCC('M', 'P', 'N', 'G') }, + { "ptx", mmioFOURCC('p', 't', 'x', ' ') }, + { "tga", mmioFOURCC('M', 'T', 'G', 'A') }, + { "tif", mmioFOURCC('t', 'i', 'f', 'f') }, + { "tiff", mmioFOURCC('t', 'i', 'f', 'f') }, + { "sgi", mmioFOURCC('S', 'G', 'I', '1') }, + { "sun", mmioFOURCC('s', 'u', 'n', ' ') }, + { "ras", mmioFOURCC('s', 'u', 'n', ' ') }, + { "ra", mmioFOURCC('s', 'u', 'n', ' ') }, + { "im1", mmioFOURCC('s', 'u', 'n', ' ') }, + { "im8", mmioFOURCC('s', 'u', 'n', ' ') }, + { "im24", mmioFOURCC('s', 'u', 'n', ' ') }, + { "sunras", mmioFOURCC('s', 'u', 'n', ' ') }, + { NULL, 0 } +}; + +static demuxer_t* demux_open_mf(demuxer_t* demuxer){ + sh_video_t *sh_video = NULL; + mf_t *mf = NULL; + int i; + + if(!demuxer->stream->url) return NULL; + if(strncmp(demuxer->stream->url, "mf://", 5)) return NULL; + + + mf=open_mf(demuxer->stream->url + 5); + if(!mf) return NULL; + + if(!mf_type){ + char* p=strrchr(mf->names[0],'.'); + if(!p){ + mp_msg(MSGT_DEMUX, MSGL_INFO, "[demux_mf] file type was not set! (try -mf type=xxx)\n" ); + free( mf ); return NULL; + } + mf_type = talloc_strdup(NULL, p+1); + mp_msg(MSGT_DEMUX, MSGL_INFO, "[demux_mf] file type was not set! trying 'type=%s'...\n", mf_type); + } + + demuxer->filepos=mf->curr_frame=0; + + demuxer->movi_start = 0; + demuxer->movi_end = mf->nr_of_files - 1; + + // create a new video stream header + sh_video = new_sh_video(demuxer, 0); + // make sure the demuxer knows about the new video stream header + // (even though new_sh_video() ought to take care of it) + demuxer->video->sh = sh_video; + + // make sure that the video demuxer stream header knows about its + // parent video demuxer stream (this is getting wacky), or else + // video_read_properties() will choke + sh_video->ds = demuxer->video; + + for (i = 0; type2format[i].type; i++) + if (strcasecmp(mf_type, type2format[i].type) == 0) + break; + if (!type2format[i].type) { + mp_msg(MSGT_DEMUX, MSGL_INFO, "[demux_mf] unknown input file type.\n" ); + free(mf); + return NULL; + } + sh_video->format = type2format[i].format; + + sh_video->disp_w = mf_w; + sh_video->disp_h = mf_h; + sh_video->fps = mf_fps; + sh_video->frametime = 1 / sh_video->fps; + + // emulate BITMAPINFOHEADER: + sh_video->bih=calloc(1, sizeof(*sh_video->bih)); + sh_video->bih->biSize=40; + sh_video->bih->biWidth = mf_w; + sh_video->bih->biHeight = mf_h; + sh_video->bih->biPlanes=1; + sh_video->bih->biBitCount=24; + sh_video->bih->biCompression=sh_video->format; + sh_video->bih->biSizeImage=sh_video->bih->biWidth*sh_video->bih->biHeight*3; + + /* disable seeking */ +// demuxer->seekable = 0; + + demuxer->priv=(void*)mf; + + return demuxer; +} + +static void demux_close_mf(demuxer_t* demuxer) { + mf_t *mf = demuxer->priv; + + free(mf); +} + +static int demux_control_mf(demuxer_t *demuxer, int cmd, void *arg) { + mf_t *mf = (mf_t *)demuxer->priv; + sh_video_t *sh_video = demuxer->video->sh; + + switch(cmd) { + case DEMUXER_CTRL_GET_TIME_LENGTH: + *((double *)arg) = (double)mf->nr_of_files / sh_video->fps; + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_GET_PERCENT_POS: + if (mf->nr_of_files <= 1) + return DEMUXER_CTRL_DONTKNOW; + *((int *)arg) = 100 * mf->curr_frame / (mf->nr_of_files - 1); + return DEMUXER_CTRL_OK; + + default: + return DEMUXER_CTRL_NOTIMPL; + } +} + +const demuxer_desc_t demuxer_desc_mf = { + "mf demuxer", + "mf", + "MF", + "?", + "multiframe?, pictures demuxer", + DEMUXER_TYPE_MF, + 0, // no autodetect + NULL, + demux_mf_fill_buffer, + demux_open_mf, + demux_close_mf, + demux_seek_mf, + demux_control_mf +}; diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c new file mode 100644 index 0000000000..3093fcae0f --- /dev/null +++ b/demux/demux_mkv.c @@ -0,0 +1,2558 @@ +/* + * Matroska demuxer + * Copyright (C) 2004 Aurelien Jacobs <aurel@gnuage.org> + * Based on the one written by Ronald Bultje for gstreamer + * and on demux_mkv.cpp from Moritz Bunkus. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <inttypes.h> +#include <stdbool.h> + +#include <libavutil/common.h> +#include <libavutil/lzo.h> +#include <libavutil/intreadwrite.h> +#include <libavutil/avstring.h> + +#include "config.h" + +#if CONFIG_ZLIB +#include <zlib.h> +#endif + +#include "talloc.h" +#include "options.h" +#include "bstr.h" +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "ebml.h" +#include "matroska.h" +//#include "demux_real.h" + +#include "mp_msg.h" + +static const unsigned char sipr_swaps[38][2] = { + {0,63},{1,22},{2,44},{3,90},{5,81},{7,31},{8,86},{9,58},{10,36},{12,68}, + {13,39},{14,73},{15,53},{16,69},{17,57},{19,88},{20,34},{21,71},{24,46}, + {25,94},{26,54},{28,75},{29,50},{32,70},{33,92},{35,74},{38,85},{40,56}, + {42,87},{43,65},{45,59},{48,79},{49,93},{51,89},{55,95},{61,76},{67,83}, + {77,80} +}; + +// Map flavour to bytes per second +#define SIPR_FLAVORS 4 +#define ATRC_FLAVORS 8 +#define COOK_FLAVORS 34 +static const int sipr_fl2bps[SIPR_FLAVORS] = { 813, 1062, 625, 2000 }; +static const int atrc_fl2bps[ATRC_FLAVORS] = { + 8269, 11714, 13092, 16538, 18260, 22050, 33075, 44100 }; +static const int cook_fl2bps[COOK_FLAVORS] = { + 1000, 1378, 2024, 2584, 4005, 5513, 8010, 4005, 750, 2498, + 4048, 5513, 8010, 11973, 8010, 2584, 4005, 2067, 2584, 2584, + 4005, 4005, 5513, 5513, 8010, 12059, 1550, 8010, 12059, 5513, + 12016, 16408, 22911, 33506 +}; + +typedef struct mkv_content_encoding { + uint64_t order, type, scope; + uint64_t comp_algo; + uint8_t *comp_settings; + int comp_settings_len; +} mkv_content_encoding_t; + +typedef struct mkv_track { + int tnum; + char *name; + int id; // -aid / -sid / -vid option value + + char *codec_id; + int ms_compat; + char *language; + + int type; + + uint32_t v_width, v_height, v_dwidth, v_dheight; + double v_frate; + + uint32_t a_formattag; + uint32_t a_channels, a_bps; + float a_sfreq; + float a_osfreq; + + double default_duration; + + int default_track; + + unsigned char *private_data; + unsigned int private_size; + + /* stuff for realmedia */ + int realmedia; + int64_t rv_kf_base; + int rv_kf_pts; + double rv_pts; /* previous video timestamp */ + double ra_pts; /* previous audio timestamp */ + + /** realaudio descrambling */ + int sub_packet_size; ///< sub packet size, per stream + int sub_packet_h; ///< number of coded frames per block + int coded_framesize; ///< coded frame size, per stream + int audiopk_size; ///< audio packet size + unsigned char *audio_buf; ///< place to store reordered audio data + double *audio_timestamp; ///< timestamp for each audio packet + int sub_packet_cnt; ///< number of subpacket already received + int audio_filepos; ///< file position of first audio packet in block + + /* stuff for quicktime */ + int fix_i_bps; + double qt_last_a_pts; + + int subtitle_type; + + /* generic content encoding support */ + mkv_content_encoding_t *encodings; + int num_encodings; + + /* For VobSubs and SSA/ASS */ + sh_sub_t *sh_sub; +} mkv_track_t; + +typedef struct mkv_index { + int tnum; + uint64_t timecode, filepos; +} mkv_index_t; + +typedef struct mkv_demuxer { + off_t segment_start; + + double duration, last_pts; + uint64_t last_filepos; + + mkv_track_t **tracks; + int num_tracks; + + uint64_t tc_scale, cluster_tc; + + uint64_t cluster_start; + uint64_t cluster_size; + uint64_t blockgroup_size; + + mkv_index_t *indexes; + int num_indexes; + + off_t *parsed_pos; + int num_parsed_pos; + bool parsed_info; + bool parsed_tracks; + bool parsed_tags; + bool parsed_chapters; + bool parsed_attachments; + + struct cluster_pos { + uint64_t filepos; + uint64_t timecode; + } *cluster_positions; + int num_cluster_pos; + + uint64_t skip_to_timecode; + int v_skip_to_keyframe, a_skip_to_keyframe; + + int num_audio_tracks; + int num_video_tracks; +} mkv_demuxer_t; + +#define REALHEADER_SIZE 16 +#define RVPROPERTIES_SIZE 34 +#define RAPROPERTIES4_SIZE 56 +#define RAPROPERTIES5_SIZE 70 + +/** + * \brief ensures there is space for at least one additional element + * \param array array to grow + * \param nelem current number of elements in array + * \param elsize size of one array element + */ +static void *grow_array(void *array, int nelem, size_t elsize) +{ + if (!(nelem & 31)) + array = realloc(array, (nelem + 32) * elsize); + return array; +} + +static bool is_parsed_header(struct mkv_demuxer *mkv_d, off_t pos) +{ + int low = 0; + int high = mkv_d->num_parsed_pos; + while (high > low + 1) { + int mid = high + low >> 1; + if (mkv_d->parsed_pos[mid] > pos) + high = mid; + else + low = mid; + } + if (mkv_d->num_parsed_pos && mkv_d->parsed_pos[low] == pos) + return true; + if (!(mkv_d->num_parsed_pos & 31)) + mkv_d->parsed_pos = talloc_realloc(mkv_d, mkv_d->parsed_pos, off_t, + mkv_d->num_parsed_pos + 32); + mkv_d->num_parsed_pos++; + for (int i = mkv_d->num_parsed_pos - 1; i > low; i--) + mkv_d->parsed_pos[i] = mkv_d->parsed_pos[i - 1]; + mkv_d->parsed_pos[low] = pos; + return false; +} + +static mkv_track_t *find_track_by_num(struct mkv_demuxer *d, int n, int type) +{ + for (int i = 0; i < d->num_tracks; i++) + if (d->tracks[i] != NULL && d->tracks[i]->type == type) + if (d->tracks[i]->id == n) + return d->tracks[i]; + + return NULL; +} + +static void add_cluster_position(mkv_demuxer_t *mkv_d, uint64_t filepos, + uint64_t timecode) +{ + if (mkv_d->indexes) + return; + + int n = mkv_d->num_cluster_pos; + if (n > 0 && mkv_d->cluster_positions[n-1].filepos >= filepos) + return; + + mkv_d->cluster_positions = + grow_array(mkv_d->cluster_positions, mkv_d->num_cluster_pos, + sizeof(*mkv_d->cluster_positions)); + mkv_d->cluster_positions[mkv_d->num_cluster_pos++] = (struct cluster_pos){ + .filepos = filepos, + .timecode = timecode, + }; +} + + +#define AAC_SYNC_EXTENSION_TYPE 0x02b7 +static int aac_get_sample_rate_index(uint32_t sample_rate) +{ + static const int srates[] = { + 92017, 75132, 55426, 46009, 37566, 27713, + 23004, 18783, 13856, 11502, 9391, 0 + }; + int i = 0; + while (sample_rate < srates[i]) + i++; + return i; +} + +static void demux_mkv_decode(mkv_track_t *track, uint8_t *src, + uint8_t **dest, uint32_t *size, uint32_t type) +{ + uint8_t *orig_src = src; + + *dest = src; + + for (int i = 0; i < track->num_encodings; i++) { + struct mkv_content_encoding *enc = track->encodings + i; + if (!(enc->scope & type)) + continue; + + if (src != *dest && src != orig_src) + talloc_free(src); + src = *dest; // output from last iteration is new source + + if (enc->comp_algo == 0) { +#if CONFIG_ZLIB + /* zlib encoded track */ + + if (*size == 0) + continue; + + z_stream zstream; + + zstream.zalloc = (alloc_func) 0; + zstream.zfree = (free_func) 0; + zstream.opaque = (voidpf) 0; + if (inflateInit(&zstream) != Z_OK) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] zlib initialization failed.\n"); + goto error; + } + zstream.next_in = (Bytef *) src; + zstream.avail_in = *size; + + *dest = NULL; + zstream.avail_out = *size; + int result; + do { + *size += 4000; + *dest = talloc_realloc_size(NULL, *dest, *size); + zstream.next_out = (Bytef *) (*dest + zstream.total_out); + result = inflate(&zstream, Z_NO_FLUSH); + if (result != Z_OK && result != Z_STREAM_END) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] zlib decompression failed.\n"); + talloc_free(*dest); + *dest = NULL; + inflateEnd(&zstream); + goto error; + } + zstream.avail_out += 4000; + } while (zstream.avail_out == 4000 && zstream.avail_in != 0 + && result != Z_STREAM_END); + + *size = zstream.total_out; + inflateEnd(&zstream); +#endif + } else if (enc->comp_algo == 2) { + /* lzo encoded track */ + int out_avail; + int dstlen = *size * 3; + + *dest = NULL; + while (1) { + int srclen = *size; + *dest = talloc_realloc_size(NULL, *dest, + dstlen + AV_LZO_OUTPUT_PADDING); + out_avail = dstlen; + int result = av_lzo1x_decode(*dest, &out_avail, src, &srclen); + if (result == 0) + break; + if (!(result & AV_LZO_OUTPUT_FULL)) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] lzo decompression failed.\n"); + talloc_free(*dest); + *dest = NULL; + goto error; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, + "[mkv] lzo decompression buffer too small.\n"); + dstlen *= 2; + } + *size = dstlen - out_avail; + } else if (enc->comp_algo == 3) { + *dest = talloc_size(NULL, *size + enc->comp_settings_len); + memcpy(*dest, enc->comp_settings, enc->comp_settings_len); + memcpy(*dest + enc->comp_settings_len, src, *size); + *size += enc->comp_settings_len; + } + } + + error: + if (src != *dest && src != orig_src) + talloc_free(src); +} + + +static int demux_mkv_read_info(demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = demuxer->priv; + stream_t *s = demuxer->stream; + int res = 0; + + mkv_d->tc_scale = 1000000; + mkv_d->duration = 0; + + struct ebml_info info = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &info, &ebml_info_desc) < 0) + return -1; + if (info.n_timecode_scale) { + mkv_d->tc_scale = info.timecode_scale; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + timecode scale: %" PRIu64 "\n", mkv_d->tc_scale); + } + if (info.n_duration) { + mkv_d->duration = info.duration * mkv_d->tc_scale / 1e9; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + duration: %.3fs\n", + mkv_d->duration); + } + if (info.n_segment_uid) { + int len = info.segment_uid.len; + if (len != sizeof(demuxer->matroska_data.segment_uid)) { + mp_msg(MSGT_DEMUX, MSGL_INFO, + "[mkv] segment uid invalid length %d\n", len); + } else { + memcpy(demuxer->matroska_data.segment_uid, info.segment_uid.start, + len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + segment uid"); + for (int i = 0; i < len; i++) + mp_msg(MSGT_DEMUX, MSGL_V, " %02x", + demuxer->matroska_data.segment_uid[i]); + mp_msg(MSGT_DEMUX, MSGL_V, "\n"); + } + } + if (demuxer->params && demuxer->params->matroska_wanted_uids) { + unsigned char (*uids)[16] = demuxer->params->matroska_wanted_uids; + if (!info.n_segment_uid) + uids = NULL; + for (int i = 0; i < MP_TALLOC_ELEMS(uids); i++) { + if (!memcmp(info.segment_uid.start, uids[i], 16)) + goto out; + } + mp_tmsg(MSGT_DEMUX, MSGL_INFO, + "[mkv] This is not one of the wanted files. " + "Stopping attempt to open.\n"); + res = -2; + } + out: + talloc_free(parse_ctx.talloc_ctx); + return res; +} + +static void parse_trackencodings(struct demuxer *demuxer, + struct mkv_track *track, + struct ebml_content_encodings *encodings) +{ + // initial allocation to be a non-NULL context before realloc + mkv_content_encoding_t *ce = talloc_size(track, 1); + + for (int n_enc = 0; n_enc < encodings->n_content_encoding; n_enc++) { + struct ebml_content_encoding *enc = encodings->content_encoding + n_enc; + struct mkv_content_encoding e = {}; + e.order = enc->content_encoding_order; + if (enc->n_content_encoding_scope) + e.scope = enc->content_encoding_scope; + else + e.scope = 1; + e.type = enc->content_encoding_type; + + if (enc->n_content_compression) { + struct ebml_content_compression *z = &enc->content_compression; + e.comp_algo = z->content_comp_algo; + if (z->n_content_comp_settings) { + int sz = z->content_comp_settings.len; + e.comp_settings = talloc_size(ce, sz); + memcpy(e.comp_settings, z->content_comp_settings.start, sz); + e.comp_settings_len = sz; + } + } + + if (e.type == 1) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, "[mkv] Track " + "number %u has been encrypted and " + "decryption has not yet been\n" + "[mkv] implemented. Skipping track.\n", + track->tnum); + } else if (e.type != 0) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] Unknown content encoding type for " + "track %u. Skipping track.\n", + track->tnum); + } else if (e.comp_algo != 0 && e.comp_algo != 2 && e.comp_algo != 3) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] Track %u has been compressed with " + "an unknown/unsupported compression\n" + "[mkv] algorithm (%" PRIu64 "). Skipping track.\n", + track->tnum, e.comp_algo); + } +#if !CONFIG_ZLIB + else if (e.comp_algo == 0) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, + "[mkv] Track %u was compressed with zlib " + "but mpv has not been compiled\n" + "[mkv] with support for zlib compression. " + "Skipping track.\n", + track->tnum); + } +#endif + int i; + for (i = 0; i < n_enc; i++) + if (e.order >= ce[i].order) + break; + ce = talloc_realloc_size(track, ce, (n_enc + 1) * sizeof(*ce)); + memmove(ce + i + 1, ce + i, (n_enc - i) * sizeof(*ce)); + memcpy(ce + i, &e, sizeof(e)); + } + + track->encodings = ce; + track->num_encodings = encodings->n_content_encoding; +} + +static void parse_trackaudio(struct demuxer *demuxer, struct mkv_track *track, + struct ebml_audio *audio) +{ + if (audio->n_sampling_frequency) { + track->a_sfreq = audio->sampling_frequency; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + Sampling frequency: %f\n", track->a_sfreq); + } else + track->a_sfreq = 8000; + if (audio->n_output_sampling_frequency) { + track->a_osfreq = audio->output_sampling_frequency; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + Output sampling frequency: %f\n", track->a_osfreq); + } else + track->a_osfreq = track->a_sfreq; + if (audio->n_bit_depth) { + track->a_bps = audio->bit_depth; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Bit depth: %u\n", + track->a_bps); + } + if (audio->n_channels) { + track->a_channels = audio->channels; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Channels: %u\n", + track->a_channels); + } else + track->a_channels = 1; +} + +static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track, + struct ebml_video *video) +{ + if (video->n_frame_rate) { + track->v_frate = video->frame_rate; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Frame rate: %f\n", + track->v_frate); + if (track->v_frate > 0) + track->default_duration = 1 / track->v_frate; + } + if (video->n_display_width) { + track->v_dwidth = video->display_width; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Display width: %u\n", + track->v_dwidth); + } + if (video->n_display_height) { + track->v_dheight = video->display_height; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Display height: %u\n", + track->v_dheight); + } + if (video->n_pixel_width) { + track->v_width = video->pixel_width; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Pixel width: %u\n", + track->v_width); + } + if (video->n_pixel_height) { + track->v_height = video->pixel_height; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Pixel height: %u\n", + track->v_height); + } +} + +/** + * \brief free any data associated with given track + * \param track track of which to free data + */ +static void demux_mkv_free_trackentry(mkv_track_t *track) +{ + free(track->audio_buf); + free(track->audio_timestamp); + talloc_free(track); +} + +static void parse_trackentry(struct demuxer *demuxer, + struct ebml_track_entry *entry) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + struct mkv_track *track = talloc_zero_size(NULL, sizeof(*track)); + + track->tnum = entry->track_number; + if (track->tnum) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Track number: %u\n", + track->tnum); + else + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Missing track number!\n"); + + if (entry->n_name) { + track->name = talloc_strndup(track, entry->name.start, + entry->name.len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Name: %s\n", + track->name); + } + + track->type = entry->track_type; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Track type: "); + switch (track->type) { + case MATROSKA_TRACK_AUDIO: + mp_msg(MSGT_DEMUX, MSGL_V, "Audio\n"); + break; + case MATROSKA_TRACK_VIDEO: + mp_msg(MSGT_DEMUX, MSGL_V, "Video\n"); + break; + case MATROSKA_TRACK_SUBTITLE: + mp_msg(MSGT_DEMUX, MSGL_V, "Subtitle\n"); + break; + default: + mp_msg(MSGT_DEMUX, MSGL_V, "unknown\n"); + break; + } + + if (entry->n_audio) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Audio track\n"); + parse_trackaudio(demuxer, track, &entry->audio); + } + + if (entry->n_video) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Video track\n"); + parse_trackvideo(demuxer, track, &entry->video); + } + + if (entry->n_codec_id) { + track->codec_id = talloc_strndup(track, entry->codec_id.start, + entry->codec_id.len); + if (!strcmp(track->codec_id, MKV_V_MSCOMP) + || !strcmp(track->codec_id, MKV_A_ACM)) + track->ms_compat = 1; + else if (!strcmp(track->codec_id, MKV_S_VOBSUB)) + track->subtitle_type = 'v'; + else if (!strcmp(track->codec_id, MKV_S_TEXTSSA) + || !strcmp(track->codec_id, MKV_S_TEXTASS) + || !strcmp(track->codec_id, MKV_S_SSA) + || !strcmp(track->codec_id, MKV_S_ASS)) + track->subtitle_type = 'a'; + else if (!strcmp(track->codec_id, MKV_S_TEXTASCII) + || !strcmp(track->codec_id, MKV_S_TEXTUTF8)) + track->subtitle_type = 't'; + else if (!strcmp(track->codec_id, MKV_S_PGS)) + track->subtitle_type = 'p'; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Codec ID: %s\n", + track->codec_id); + } else + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Missing codec ID!\n"); + + if (entry->n_codec_private) { + int len = entry->codec_private.len; + track->private_data = talloc_size(track, len + AV_LZO_INPUT_PADDING); + memcpy(track->private_data, entry->codec_private.start, len); + track->private_size = len; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + CodecPrivate, length %u\n", + track->private_size); + } + + if (entry->n_language) { + track->language = talloc_strndup(track, entry->language.start, + entry->language.len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Language: %s\n", + track->language); + } else + track->language = talloc_strdup(track, "eng"); + + if (entry->n_flag_default) { + track->default_track = entry->flag_default; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Default flag: %u\n", + track->default_track); + } else + track->default_track = 1; + + if (entry->n_default_duration) { + track->default_duration = entry->default_duration / 1e9; + if (entry->default_duration == 0) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Default duration: 0"); + else { + if (!track->v_frate) + track->v_frate = 1e9 / entry->default_duration; + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] | + Default duration: %.3fms ( = %.3f fps)\n", + entry->default_duration / 1000000.0, track->v_frate); + } + } + + if (entry->n_content_encodings) + parse_trackencodings(demuxer, track, &entry->content_encodings); + + mkv_d->tracks[mkv_d->num_tracks++] = track; +} + +static int demux_mkv_read_tracks(demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + stream_t *s = demuxer->stream; + + struct ebml_tracks tracks = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &tracks, &ebml_tracks_desc) < 0) + return -1; + + mkv_d->tracks = talloc_size(mkv_d, + tracks.n_track_entry * sizeof(*mkv_d->tracks)); + for (int i = 0; i < tracks.n_track_entry; i++) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + a track...\n"); + parse_trackentry(demuxer, &tracks.track_entry[i]); + } + talloc_free(parse_ctx.talloc_ctx); + return 0; +} + +static int demux_mkv_read_cues(demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + stream_t *s = demuxer->stream; + + if (index_mode == 0 || index_mode == 2) { + ebml_read_skip(s, NULL); + return 0; + } + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing cues ] -----------\n"); + struct ebml_cues cues = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &cues, &ebml_cues_desc) < 0) + return -1; + for (int i = 0; i < cues.n_cue_point; i++) { + struct ebml_cue_point *cuepoint = &cues.cue_point[i]; + if (cuepoint->n_cue_time != 1 || !cuepoint->n_cue_track_positions) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Malformed CuePoint element\n"); + continue; + } + uint64_t time = cuepoint->cue_time; + for (int i = 0; i < cuepoint->n_cue_track_positions; i++) { + struct ebml_cue_track_positions *trackpos = + &cuepoint->cue_track_positions[i]; + uint64_t track = trackpos->cue_track; + uint64_t pos = trackpos->cue_cluster_position; + mkv_d->indexes = + grow_array(mkv_d->indexes, mkv_d->num_indexes, + sizeof(mkv_index_t)); + mkv_d->indexes[mkv_d->num_indexes].tnum = track; + mkv_d->indexes[mkv_d->num_indexes].timecode = time; + mkv_d->indexes[mkv_d->num_indexes].filepos = + mkv_d->segment_start + pos; + mp_msg(MSGT_DEMUX, MSGL_DBG2, + "[mkv] |+ found cue point for track %" PRIu64 + ": timecode %" PRIu64 ", filepos: %" PRIu64 "\n", track, + time, mkv_d->segment_start + pos); + mkv_d->num_indexes++; + } + } + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing cues ] -----------\n"); + talloc_free(parse_ctx.talloc_ctx); + return 0; +} + +static int demux_mkv_read_chapters(struct demuxer *demuxer) +{ + struct MPOpts *opts = demuxer->opts; + stream_t *s = demuxer->stream; + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n"); + struct ebml_chapters file_chapters = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &file_chapters, + &ebml_chapters_desc) < 0) + return -1; + + int selected_edition = 0; + int num_editions = file_chapters.n_edition_entry; + struct ebml_edition_entry *editions = file_chapters.edition_entry; + if (opts->edition_id >= 0 && opts->edition_id < num_editions) { + selected_edition = opts->edition_id; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] User-specified edition: %d\n", + selected_edition); + } else + for (int i = 0; i < num_editions; i++) + if (editions[i].edition_flag_default) { + selected_edition = i; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Default edition: %d\n", i); + break; + } + struct matroska_chapter *m_chapters = NULL; + if (editions[selected_edition].edition_flag_ordered) { + int count = editions[selected_edition].n_chapter_atom; + m_chapters = talloc_array_ptrtype(demuxer, m_chapters, count); + demuxer->matroska_data.ordered_chapters = m_chapters; + demuxer->matroska_data.num_ordered_chapters = count; + } + + for (int idx = 0; idx < num_editions; idx++) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] New edition %d\n", idx); + int warn_level = idx == selected_edition ? MSGL_WARN : MSGL_V; + if (editions[idx].n_edition_flag_default) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Default edition flag: %"PRIu64 + "\n", editions[idx].edition_flag_default); + if (editions[idx].n_edition_flag_ordered) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Ordered chapter flag: %"PRIu64 + "\n", editions[idx].edition_flag_ordered); + for (int i = 0; i < editions[idx].n_chapter_atom; i++) { + struct ebml_chapter_atom *ca = editions[idx].chapter_atom + i; + struct matroska_chapter chapter = { }; + struct bstr name = { "(unnamed)", 9 }; + + if (!ca->n_chapter_time_start) + mp_msg(MSGT_DEMUX, warn_level, + "[mkv] Chapter lacks start time\n"); + chapter.start = ca->chapter_time_start; + chapter.end = ca->chapter_time_end; + + if (ca->n_chapter_display) { + if (ca->n_chapter_display > 1) + mp_msg(MSGT_DEMUX, warn_level, "[mkv] Multiple chapter " + "names not supported, picking first\n"); + if (!ca->chapter_display[0].n_chap_string) + mp_msg(MSGT_DEMUX, warn_level, "[mkv] Malformed chapter " + "name entry\n"); + else + name = ca->chapter_display[0].chap_string; + } + + if (ca->n_chapter_segment_uid) { + chapter.has_segment_uid = true; + int len = ca->chapter_segment_uid.len; + if (len != sizeof(chapter.segment_uid)) + mp_msg(MSGT_DEMUX, warn_level, + "[mkv] Chapter segment uid bad length %d\n", len); + else if (ca->n_chapter_segment_edition_uid) { + mp_tmsg(MSGT_DEMUX, warn_level, "[mkv] Warning: " + "unsupported edition recursion in chapter; " + "will skip on playback!\n"); + } else { + memcpy(chapter.segment_uid, ca->chapter_segment_uid.start, + len); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Chapter segment uid "); + for (int i = 0; i < len; i++) + mp_msg(MSGT_DEMUX, MSGL_V, "%02x ", + chapter.segment_uid[i]); + mp_msg(MSGT_DEMUX, MSGL_V, "\n"); + } + } + + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] Chapter %u from %02d:%02d:%02d.%03d " + "to %02d:%02d:%02d.%03d, %.*s\n", i, + (int) (chapter.start / 60 / 60 / 1000000000), + (int) ((chapter.start / 60 / 1000000000) % 60), + (int) ((chapter.start / 1000000000) % 60), + (int) (chapter.start % 1000000000), + (int) (chapter.end / 60 / 60 / 1000000000), + (int) ((chapter.end / 60 / 1000000000) % 60), + (int) ((chapter.end / 1000000000) % 60), + (int) (chapter.end % 1000000000), + BSTR_P(name)); + + if (idx == selected_edition){ + demuxer_add_chapter(demuxer, name, chapter.start, chapter.end); + if (editions[idx].edition_flag_ordered) { + chapter.name = talloc_strndup(m_chapters, name.start, + name.len); + m_chapters[i] = chapter; + } + } + } + } + if (num_editions > 1) + mp_msg(MSGT_DEMUX, MSGL_INFO, + "[mkv] Found %d editions, will play #%d (first is 0).\n", + num_editions, selected_edition); + + demuxer->num_editions = num_editions; + demuxer->edition = selected_edition; + + talloc_free(parse_ctx.talloc_ctx); + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] \\---- [ parsing chapters ] ---------\n"); + return 0; +} + +static int demux_mkv_read_tags(demuxer_t *demuxer) +{ + stream_t *s = demuxer->stream; + + struct ebml_parse_ctx parse_ctx = {}; + struct ebml_tags tags = {}; + if (ebml_read_element(s, &parse_ctx, &tags, &ebml_tags_desc) < 0) + return -1; + + for (int i = 0; i < tags.n_tag; i++) { + struct ebml_tag tag = tags.tag[i]; + if (tag.targets.target_track_uid || tag.targets.target_edition_uid || + tag.targets.target_chapter_uid || tag.targets.target_attachment_uid) + continue; + + for (int j = 0; j < tag.n_simple_tag; j++) + demux_info_add_bstr(demuxer, tag.simple_tag[j].tag_name, tag.simple_tag[j].tag_string); + } + + talloc_free(parse_ctx.talloc_ctx); + return 0; +} + +static int demux_mkv_read_attachments(demuxer_t *demuxer) +{ + stream_t *s = demuxer->stream; + + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] /---- [ parsing attachments ] ---------\n"); + + struct ebml_attachments attachments = {}; + struct ebml_parse_ctx parse_ctx = {}; + if (ebml_read_element(s, &parse_ctx, &attachments, + &ebml_attachments_desc) < 0) + return -1; + + for (int i = 0; i < attachments.n_attached_file; i++) { + struct ebml_attached_file *attachment = &attachments.attached_file[i]; + if (!attachment->n_file_name || !attachment->n_file_mime_type + || !attachment->n_file_data) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Malformed attachment\n"); + continue; + } + struct bstr name = attachment->file_name; + struct bstr mime = attachment->file_mime_type; + demuxer_add_attachment(demuxer, name, mime, attachment->file_data); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Attachment: %.*s, %.*s, %zu bytes\n", + BSTR_P(name), BSTR_P(mime), attachment->file_data.len); + } + + talloc_free(parse_ctx.talloc_ctx); + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] \\---- [ parsing attachments ] ---------\n"); + return 0; +} + +static int read_header_element(struct demuxer *demuxer, uint32_t id, + off_t at_filepos); + +static int demux_mkv_read_seekhead(demuxer_t *demuxer) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + struct stream *s = demuxer->stream; + int res = 0; + struct ebml_seek_head seekhead = {}; + struct ebml_parse_ctx parse_ctx = {}; + + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] /---- [ parsing seek head ] ---------\n"); + if (ebml_read_element(s, &parse_ctx, &seekhead, &ebml_seek_head_desc) < 0) { + res = -1; + goto out; + } + /* off now holds the position of the next element after the seek head. */ + off_t off = stream_tell(s); + for (int i = 0; i < seekhead.n_seek; i++) { + struct ebml_seek *seek = &seekhead.seek[i]; + if (seek->n_seek_id != 1 || seek->n_seek_position != 1) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Invalid SeekHead entry\n"); + continue; + } + uint64_t pos = seek->seek_position + mkv_d->segment_start; + if (pos >= demuxer->movi_end) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] SeekHead position beyond " + "end of file - incomplete file?\n"); + continue; + } + int r = read_header_element(demuxer, seek->seek_id, pos); + if (r <= -2) { + res = r; + goto out; + } + } + if (!stream_seek(s, off)) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Couldn't seek back after " + "SeekHead??\n"); + res = -1; + } + out: + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] \\---- [ parsing seek head ] ---------\n"); + talloc_free(parse_ctx.talloc_ctx); + return res; +} + +static bool seek_pos_id(struct stream *s, off_t pos, uint32_t id) +{ + if (!stream_seek(s, pos)) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Failed to seek in file\n"); + return false; + } + if (ebml_read_id(s, NULL) != id) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Expected element not found\n"); + return false; + } + return true; +} + +static int read_header_element(struct demuxer *demuxer, uint32_t id, + off_t at_filepos) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + stream_t *s = demuxer->stream; + off_t pos = stream_tell(s) - 4; + int res = 1; + + switch(id) { + case MATROSKA_ID_INFO: + if (mkv_d->parsed_info) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment information...\n"); + mkv_d->parsed_info = true; + return demux_mkv_read_info(demuxer); + + case MATROSKA_ID_TRACKS: + if (mkv_d->parsed_tracks) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_tracks = true; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment tracks...\n"); + return demux_mkv_read_tracks(demuxer); + + case MATROSKA_ID_CUES: + if (is_parsed_header(mkv_d, pos)) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + return demux_mkv_read_cues(demuxer); + + case MATROSKA_ID_TAGS: + if (mkv_d->parsed_tags) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_tags = true; + return demux_mkv_read_tags(demuxer); + + case MATROSKA_ID_SEEKHEAD: + if (is_parsed_header(mkv_d, pos)) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + return demux_mkv_read_seekhead(demuxer); + + case MATROSKA_ID_CHAPTERS: + if (mkv_d->parsed_chapters) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_chapters = true; + return demux_mkv_read_chapters(demuxer); + + case MATROSKA_ID_ATTACHMENTS: + if (mkv_d->parsed_attachments) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_attachments = true; + return demux_mkv_read_attachments(demuxer); + + case EBML_ID_VOID: + break; + + default: + res = 2; + } + if (!at_filepos) + ebml_read_skip(s, NULL); + return res; +} + + + +static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track, + int vid); +static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track, + int aid); +static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track, + int sid); + +static void display_create_tracks(demuxer_t *demuxer) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + int i, vid = 0, aid = 0, sid = 0; + + for (i = 0; i < mkv_d->num_tracks; i++) { + char *type = "unknown", str[32]; + *str = '\0'; + switch (mkv_d->tracks[i]->type) { + case MATROSKA_TRACK_VIDEO: + type = "video"; + mkv_d->tracks[i]->id = -1; + if (vid == MAX_V_STREAMS) + break; + mkv_d->tracks[i]->id = vid; + demux_mkv_open_video(demuxer, mkv_d->tracks[i], vid); + if (mkv_d->tracks[i]->name) + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VID_%d_NAME=%s\n", vid, + mkv_d->tracks[i]->name); + sprintf(str, "-vid %u", vid++); + break; + case MATROSKA_TRACK_AUDIO: + type = "audio"; + mkv_d->tracks[i]->id = -1; + if (aid == MAX_A_STREAMS) + break; + mkv_d->tracks[i]->id = aid; + demux_mkv_open_audio(demuxer, mkv_d->tracks[i], aid); + if (mkv_d->tracks[i]->name) + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_NAME=%s\n", aid, + mkv_d->tracks[i]->name); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_LANG=%s\n", aid, + mkv_d->tracks[i]->language); + sprintf(str, "-aid %u, -alang %.5s", aid++, + mkv_d->tracks[i]->language); + break; + case MATROSKA_TRACK_SUBTITLE: + type = "subtitles"; + mkv_d->tracks[i]->id = -1; + if (sid == MAX_S_STREAMS) + break; + mkv_d->tracks[i]->id = sid; + demux_mkv_open_sub(demuxer, mkv_d->tracks[i], sid); + if (mkv_d->tracks[i]->name) + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_NAME=%s\n", sid, + mkv_d->tracks[i]->name); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_LANG=%s\n", sid, + mkv_d->tracks[i]->language); + sprintf(str, "-sid %u, -slang %.5s", sid++, + mkv_d->tracks[i]->language); + break; + } + if (mkv_d->tracks[i]->name) + mp_tmsg(MSGT_DEMUX, MSGL_V, + "[mkv] Track ID %u: %s (%s) \"%s\", %s\n", + mkv_d->tracks[i]->tnum, type, mkv_d->tracks[i]->codec_id, + mkv_d->tracks[i]->name, str); + else + mp_tmsg(MSGT_DEMUX, MSGL_V, "[mkv] Track ID %u: %s (%s), %s\n", + mkv_d->tracks[i]->tnum, type, mkv_d->tracks[i]->codec_id, + str); + } + mkv_d->num_audio_tracks = aid; + mkv_d->num_video_tracks = vid; +} + +typedef struct { + char *id; + int fourcc; + int extradata; +} videocodec_info_t; + +static const videocodec_info_t vinfo[] = { + {MKV_V_MJPEG, mmioFOURCC('m', 'j', 'p', 'g'), 1}, + {MKV_V_MPEG1, mmioFOURCC('m', 'p', 'g', '1'), 0}, + {MKV_V_MPEG2, mmioFOURCC('m', 'p', 'g', '2'), 0}, + {MKV_V_MPEG4_SP, mmioFOURCC('m', 'p', '4', 'v'), 1}, + {MKV_V_MPEG4_ASP, mmioFOURCC('m', 'p', '4', 'v'), 1}, + {MKV_V_MPEG4_AP, mmioFOURCC('m', 'p', '4', 'v'), 1}, + {MKV_V_MPEG4_AVC, mmioFOURCC('a', 'v', 'c', '1'), 1}, + {MKV_V_THEORA, mmioFOURCC('t', 'h', 'e', 'o'), 1}, + {MKV_V_VP8, mmioFOURCC('V', 'P', '8', '0'), 0}, + {NULL, 0, 0} +}; + +static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track, + int vid) +{ + BITMAPINFOHEADER *bih; + sh_video_t *sh_v; + + if (track->ms_compat) { /* MS compatibility mode */ + BITMAPINFOHEADER *src; + + if (track->private_data == NULL + || track->private_size < sizeof(*bih)) + return 1; + + src = (BITMAPINFOHEADER *) track->private_data; + bih = calloc(1, track->private_size); + bih->biSize = le2me_32(src->biSize); + bih->biWidth = le2me_32(src->biWidth); + bih->biHeight = le2me_32(src->biHeight); + bih->biPlanes = le2me_16(src->biPlanes); + bih->biBitCount = le2me_16(src->biBitCount); + bih->biCompression = le2me_32(src->biCompression); + bih->biSizeImage = le2me_32(src->biSizeImage); + bih->biXPelsPerMeter = le2me_32(src->biXPelsPerMeter); + bih->biYPelsPerMeter = le2me_32(src->biYPelsPerMeter); + bih->biClrUsed = le2me_32(src->biClrUsed); + bih->biClrImportant = le2me_32(src->biClrImportant); + memcpy(bih + 1, + src + 1, + track->private_size - sizeof(*bih)); + + if (track->v_width == 0) + track->v_width = bih->biWidth; + if (track->v_height == 0) + track->v_height = bih->biHeight; + } else { + bih = calloc(1, sizeof(*bih)); + bih->biSize = sizeof(*bih); + bih->biWidth = track->v_width; + bih->biHeight = track->v_height; + bih->biBitCount = 24; + bih->biSizeImage = bih->biWidth * bih->biHeight * bih->biBitCount / 8; + + if (track->private_size >= RVPROPERTIES_SIZE + && (!strcmp(track->codec_id, MKV_V_REALV10) + || !strcmp(track->codec_id, MKV_V_REALV20) + || !strcmp(track->codec_id, MKV_V_REALV30) + || !strcmp(track->codec_id, MKV_V_REALV40))) { + unsigned char *dst, *src; + uint32_t type2; + unsigned int cnt; + + src = (uint8_t *) track->private_data + RVPROPERTIES_SIZE; + + cnt = track->private_size - RVPROPERTIES_SIZE; + bih = realloc(bih, sizeof(*bih) + 8 + cnt); + bih->biSize = 48 + cnt; + bih->biPlanes = 1; + type2 = AV_RB32(src - 4); + if (type2 == 0x10003000 || type2 == 0x10003001) + bih->biCompression = mmioFOURCC('R', 'V', '1', '3'); + else + bih->biCompression = + mmioFOURCC('R', 'V', track->codec_id[9], '0'); + dst = (unsigned char *) (bih + 1); + // copy type1 and type2 info from rv properties + memcpy(dst, src - 8, 8 + cnt); + track->realmedia = 1; + + } else { + const videocodec_info_t *vi = vinfo; + while (vi->id && strcmp(vi->id, track->codec_id)) + vi++; + bih->biCompression = vi->fourcc; + if (vi->extradata && track->private_data + && (track->private_size > 0)) { + bih->biSize += track->private_size; + bih = realloc(bih, bih->biSize); + memcpy(bih + 1, track->private_data, track->private_size); + } + if (!vi->id) { + mp_tmsg(MSGT_DEMUX, MSGL_WARN, "[mkv] Unknown/unsupported " + "CodecID (%s) or missing/bad CodecPrivate\n" + "[mkv] data (track %u).\n", + track->codec_id, track->tnum); + free(bih); + return 1; + } + } + } + + sh_v = new_sh_video(demuxer, vid); + sh_v->gsh->demuxer_id = track->tnum; + sh_v->demuxer_codecname = track->codec_id; + sh_v->gsh->title = talloc_strdup(sh_v, track->name); + sh_v->bih = bih; + sh_v->format = sh_v->bih->biCompression; + if (track->v_frate == 0.0) + track->v_frate = 25.0; + sh_v->fps = track->v_frate; + sh_v->frametime = 1 / track->v_frate; + sh_v->aspect = 0; + if (!track->realmedia) { + sh_v->disp_w = track->v_width; + sh_v->disp_h = track->v_height; + if (track->v_dheight) + sh_v->aspect = (double) track->v_dwidth / track->v_dheight; + } else { + // vd_realvid.c will set aspect to disp_w/disp_h and rederive + // disp_w and disp_h from the RealVideo stream contents returned + // by the Real DLLs. If DisplayWidth/DisplayHeight was not set in + // the Matroska file then it has already been set to PixelWidth/Height + // by check_track_information. + sh_v->disp_w = track->v_dwidth; + sh_v->disp_h = track->v_dheight; + } + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Aspect: %f\n", sh_v->aspect); + + sh_v->ds = demuxer->video; + return 0; +} + +static struct mkv_audio_tag { + char *id; bool prefix; uint32_t formattag; +} mkv_audio_tags[] = { + { MKV_A_MP2, 0, 0x0055 }, + { MKV_A_MP3, 0, 0x0055 }, + { MKV_A_AC3, 1, 0x2000 }, + { MKV_A_EAC3, 1, mmioFOURCC('E', 'A', 'C', '3') }, + { MKV_A_DTS, 0, 0x2001 }, + { MKV_A_PCM, 0, 0x0001 }, + { MKV_A_PCM_BE, 0, 0x0001 }, + { MKV_A_AAC_2MAIN, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_2LC, 1, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_2SSR, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4MAIN, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4LC, 1, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4SSR, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC_4LTP, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_AAC, 0, mmioFOURCC('M', 'P', '4', 'A') }, + { MKV_A_VORBIS, 0, mmioFOURCC('v', 'r', 'b', 's') }, + { MKV_A_QDMC, 0, mmioFOURCC('Q', 'D', 'M', 'C') }, + { MKV_A_QDMC2, 0, mmioFOURCC('Q', 'D', 'M', '2') }, + { MKV_A_WAVPACK, 0, mmioFOURCC('W', 'V', 'P', 'K') }, + { MKV_A_TRUEHD, 0, mmioFOURCC('T', 'R', 'H', 'D') }, + { MKV_A_FLAC, 0, mmioFOURCC('f', 'L', 'a', 'C') }, + { MKV_A_REAL28, 0, mmioFOURCC('2', '8', '_', '8') }, + { MKV_A_REALATRC, 0, mmioFOURCC('a', 't', 'r', 'c') }, + { MKV_A_REALCOOK, 0, mmioFOURCC('c', 'o', 'o', 'k') }, + { MKV_A_REALDNET, 0, mmioFOURCC('d', 'n', 'e', 't') }, + { MKV_A_REALSIPR, 0, mmioFOURCC('s', 'i', 'p', 'r') }, + { MKV_A_TTA1, 0, mmioFOURCC('T', 'T', 'A', '1') }, + { NULL }, +}; + + +static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track, + int aid) +{ + sh_audio_t *sh_a = new_sh_audio(demuxer, aid); + if (!sh_a) + return 1; + + if (track->language && (strcmp(track->language, "und") != 0)) + sh_a->lang = talloc_strdup(sh_a, track->language); + sh_a->gsh->demuxer_id = track->tnum; + sh_a->demuxer_codecname = track->codec_id; + sh_a->gsh->title = talloc_strdup(sh_a, track->name); + sh_a->gsh->default_track = track->default_track; + sh_a->ds = demuxer->audio; + if (track->ms_compat) { + if (track->private_size < sizeof(*sh_a->wf)) + goto error; + WAVEFORMATEX *wf = (WAVEFORMATEX *) track->private_data; + sh_a->wf = calloc(1, track->private_size); + sh_a->wf->wFormatTag = le2me_16(wf->wFormatTag); + sh_a->wf->nChannels = le2me_16(wf->nChannels); + sh_a->wf->nSamplesPerSec = le2me_32(wf->nSamplesPerSec); + sh_a->wf->nAvgBytesPerSec = le2me_32(wf->nAvgBytesPerSec); + sh_a->wf->nBlockAlign = le2me_16(wf->nBlockAlign); + sh_a->wf->wBitsPerSample = le2me_16(wf->wBitsPerSample); + sh_a->wf->cbSize = track->private_size - sizeof(*sh_a->wf); + memcpy(sh_a->wf + 1, wf + 1, + track->private_size - sizeof(*sh_a->wf)); + if (track->a_sfreq == 0.0) + track->a_sfreq = sh_a->wf->nSamplesPerSec; + if (track->a_channels == 0) + track->a_channels = sh_a->wf->nChannels; + if (track->a_bps == 0) + track->a_bps = sh_a->wf->wBitsPerSample; + track->a_formattag = sh_a->wf->wFormatTag; + } else { + sh_a->wf = calloc(1, sizeof(*sh_a->wf)); + for (int i = 0; ; i++) { + struct mkv_audio_tag *t = mkv_audio_tags + i; + if (t->id == NULL) + goto error; + if (t->prefix) { + if (!bstr_startswith0(bstr0(track->codec_id), t->id)) + continue; + } else { + if (strcmp(track->codec_id, t->id)) + continue; + } + track->a_formattag = t->formattag; + break; + } + } + + sh_a->format = track->a_formattag; + sh_a->wf->wFormatTag = track->a_formattag; + sh_a->channels = track->a_channels; + sh_a->wf->nChannels = track->a_channels; + sh_a->samplerate = (uint32_t) track->a_sfreq; + sh_a->container_out_samplerate = track->a_osfreq; + sh_a->wf->nSamplesPerSec = (uint32_t) track->a_sfreq; + if (track->a_bps == 0) { + sh_a->samplesize = 2; + sh_a->wf->wBitsPerSample = 16; + } else { + sh_a->samplesize = track->a_bps / 8; + sh_a->wf->wBitsPerSample = track->a_bps; + } + if (track->a_formattag == 0x0055) { /* MP3 || MP2 */ + sh_a->wf->nAvgBytesPerSec = 16000; + sh_a->wf->nBlockAlign = 1152; + } else if ((track->a_formattag == 0x2000) /* AC3 */ + || track->a_formattag == mmioFOURCC('E', 'A', 'C', '3') + || (track->a_formattag == 0x2001)) { /* DTS */ + free(sh_a->wf); + sh_a->wf = NULL; + } else if (track->a_formattag == 0x0001) { /* PCM || PCM_BE */ + sh_a->wf->nAvgBytesPerSec = sh_a->channels * sh_a->samplerate * 2; + sh_a->wf->nBlockAlign = sh_a->wf->nAvgBytesPerSec; + if (!strcmp(track->codec_id, MKV_A_PCM_BE)) + sh_a->format = mmioFOURCC('t', 'w', 'o', 's'); + } else if (!strcmp(track->codec_id, MKV_A_QDMC) + || !strcmp(track->codec_id, MKV_A_QDMC2)) { + sh_a->wf->nAvgBytesPerSec = 16000; + sh_a->wf->nBlockAlign = 1486; + track->fix_i_bps = 1; + track->qt_last_a_pts = 0.0; + goto copy_private_data; + } else if (track->a_formattag == mmioFOURCC('M', 'P', '4', 'A')) { + int profile, srate_idx; + + sh_a->wf->nAvgBytesPerSec = 16000; + sh_a->wf->nBlockAlign = 1024; + + if (!strcmp(track->codec_id, MKV_A_AAC) && track->private_data) + goto copy_private_data; + + /* Recreate the 'private data' */ + /* which faad2 uses in its initialization */ + srate_idx = aac_get_sample_rate_index(sh_a->samplerate); + if (!strncmp(&track->codec_id[12], "MAIN", 4)) + profile = 0; + else if (!strncmp(&track->codec_id[12], "LC", 2)) + profile = 1; + else if (!strncmp(&track->codec_id[12], "SSR", 3)) + profile = 2; + else + profile = 3; + sh_a->codecdata = malloc(5); + sh_a->codecdata[0] = ((profile + 1) << 3) | ((srate_idx & 0xE) >> 1); + sh_a->codecdata[1] = + ((srate_idx & 0x1) << 7) | (track->a_channels << 3); + + if (strstr(track->codec_id, "SBR") != NULL) { + /* HE-AAC (aka SBR AAC) */ + sh_a->codecdata_len = 5; + + sh_a->samplerate *= 2; + sh_a->wf->nSamplesPerSec *= 2; + srate_idx = aac_get_sample_rate_index(sh_a->samplerate); + sh_a->codecdata[2] = AAC_SYNC_EXTENSION_TYPE >> 3; + sh_a->codecdata[3] = ((AAC_SYNC_EXTENSION_TYPE & 0x07) << 5) | 5; + sh_a->codecdata[4] = (1 << 7) | (srate_idx << 3); + track->default_duration = 1024.0 / (sh_a->samplerate / 2); + } else { + sh_a->codecdata_len = 2; + track->default_duration = 1024.0 / sh_a->samplerate; + } + } else if (track->a_formattag == mmioFOURCC('v', 'r', 'b', 's')) { + /* VORBIS */ + if (track->private_size == 0 || track->ms_compat && !sh_a->wf->cbSize) + goto error; + if (!track->ms_compat) { + sh_a->wf->cbSize = track->private_size; + sh_a->wf = realloc(sh_a->wf, sizeof(*sh_a->wf) + sh_a->wf->cbSize); + memcpy((unsigned char *) (sh_a->wf + 1), track->private_data, + sh_a->wf->cbSize); + } + } else if (!strncmp(track->codec_id, MKV_A_REALATRC, 7)) { + if (track->private_size < RAPROPERTIES4_SIZE) + goto error; + /* Common initialization for all RealAudio codecs */ + unsigned char *src = track->private_data; + int codecdata_length, version; + int flavor; + + sh_a->wf->nAvgBytesPerSec = 0; /* FIXME !? */ + + version = AV_RB16(src + 4); + flavor = AV_RB16(src + 22); + track->coded_framesize = AV_RB32(src + 24); + track->sub_packet_h = AV_RB16(src + 40); + sh_a->wf->nBlockAlign = track->audiopk_size = AV_RB16(src + 42); + track->sub_packet_size = AV_RB16(src + 44); + if (version == 4) { + src += RAPROPERTIES4_SIZE; + src += src[0] + 1; + src += src[0] + 1; + } else + src += RAPROPERTIES5_SIZE; + + src += 3; + if (version == 5) + src++; + codecdata_length = AV_RB32(src); + src += 4; + sh_a->wf->cbSize = codecdata_length; + sh_a->wf = realloc(sh_a->wf, sizeof(*sh_a->wf) + sh_a->wf->cbSize); + memcpy(((char *) (sh_a->wf + 1)), src, codecdata_length); + + switch (track->a_formattag) { + case mmioFOURCC('a', 't', 'r', 'c'): + sh_a->wf->nAvgBytesPerSec = atrc_fl2bps[flavor]; + sh_a->wf->nBlockAlign = track->sub_packet_size; + goto audiobuf; + case mmioFOURCC('c', 'o', 'o', 'k'): + sh_a->wf->nAvgBytesPerSec = cook_fl2bps[flavor]; + sh_a->wf->nBlockAlign = track->sub_packet_size; + goto audiobuf; + case mmioFOURCC('s', 'i', 'p', 'r'): + sh_a->wf->nAvgBytesPerSec = sipr_fl2bps[flavor]; + sh_a->wf->nBlockAlign = track->coded_framesize; + goto audiobuf; + case mmioFOURCC('2', '8', '_', '8'): + sh_a->wf->nAvgBytesPerSec = 3600; + sh_a->wf->nBlockAlign = track->coded_framesize; + audiobuf: + track->audio_buf = + malloc(track->sub_packet_h * track->audiopk_size); + track->audio_timestamp = + malloc(track->sub_packet_h * sizeof(double)); + break; + } + + track->realmedia = 1; + } else if (!strcmp(track->codec_id, MKV_A_FLAC) + || (track->a_formattag == 0xf1ac)) { + unsigned char *ptr; + int size; + free(sh_a->wf); + sh_a->wf = NULL; + + if (!track->ms_compat) { + ptr = track->private_data; + size = track->private_size; + } else { + sh_a->format = mmioFOURCC('f', 'L', 'a', 'C'); + ptr = track->private_data + sizeof(*sh_a->wf); + size = track->private_size - sizeof(*sh_a->wf); + } + if (size < 4 || ptr[0] != 'f' || ptr[1] != 'L' || ptr[2] != 'a' + || ptr[3] != 'C') { + sh_a->codecdata = malloc(4); + sh_a->codecdata_len = 4; + memcpy(sh_a->codecdata, "fLaC", 4); + } else { + sh_a->codecdata = malloc(size); + sh_a->codecdata_len = size; + memcpy(sh_a->codecdata, ptr, size); + } + } else if (track->a_formattag == mmioFOURCC('W', 'V', 'P', 'K') || + track->a_formattag == mmioFOURCC('T', 'R', 'H', 'D')) { + copy_private_data: + if (!track->ms_compat && track->private_size) { + sh_a->codecdata = malloc(track->private_size); + sh_a->codecdata_len = track->private_size; + memcpy(sh_a->codecdata, track->private_data, track->private_size); + } + } else if (track->a_formattag == mmioFOURCC('T', 'T', 'A', '1')) { + sh_a->codecdata_len = 30; + sh_a->codecdata = calloc(1, sh_a->codecdata_len); + if (!sh_a->codecdata) + goto error; + char *data = sh_a->codecdata; + memcpy(data + 0, "TTA1", 4); + AV_WL16(data + 4, 1); + AV_WL16(data + 6, sh_a->channels); + AV_WL16(data + 8, sh_a->wf->wBitsPerSample); + AV_WL32(data + 10, sh_a->samplerate); + // Bogus: last frame won't be played. + AV_WL32(data + 14, 0); + } else if (!track->ms_compat) { + goto error; + } + + return 0; + + error: + mp_tmsg(MSGT_DEMUX, MSGL_WARN, "[mkv] Unknown/unsupported audio " + "codec ID '%s' for track %u or missing/faulty\n[mkv] " + "private codec data.\n", track->codec_id, track->tnum); + return 1; +} + +static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track, + int sid) +{ + if (track->subtitle_type) { + int size; + uint8_t *buffer; + sh_sub_t *sh = new_sh_sub(demuxer, sid); + sh->gsh->demuxer_id = track->tnum; + sh->demuxer_codecname = track->codec_id; + track->sh_sub = sh; + sh->type = track->subtitle_type; + size = track->private_size; + demux_mkv_decode(track, track->private_data, &buffer, &size, 2); + if (buffer && buffer != track->private_data) { + talloc_free(track->private_data); + talloc_steal(track, buffer); + track->private_data = buffer; + track->private_size = size; + } + sh->extradata = malloc(track->private_size); + memcpy(sh->extradata, track->private_data, track->private_size); + sh->extradata_len = track->private_size; + if (track->language && (strcmp(track->language, "und") != 0)) + sh->lang = talloc_strdup(sh, track->language); + sh->gsh->title = talloc_strdup(sh, track->name); + sh->gsh->default_track = track->default_track; + } else { + mp_tmsg(MSGT_DEMUX, MSGL_ERR, + "[mkv] Subtitle type '%s' is not supported.\n", + track->codec_id); + return 1; + } + + return 0; +} + +static void mkv_free(struct demuxer *demuxer) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + if (!mkv_d) + return; + for (int i = 0; i < mkv_d->num_tracks; i++) + demux_mkv_free_trackentry(mkv_d->tracks[i]); + free(mkv_d->indexes); + free(mkv_d->cluster_positions); +} + +static int demux_mkv_open(demuxer_t *demuxer) +{ + stream_t *s = demuxer->stream; + mkv_demuxer_t *mkv_d; + mkv_track_t *track; + + stream_seek(s, s->start_pos); + if (ebml_read_id(s, NULL) != EBML_ID_EBML) + return 0; + struct ebml_ebml ebml_master = {}; + struct ebml_parse_ctx parse_ctx = { .no_error_messages = true }; + if (ebml_read_element(s, &parse_ctx, &ebml_master, &ebml_ebml_desc) < 0) + return 0; + if (ebml_master.doc_type.start == NULL) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] File has EBML header but no doctype." + " Assuming \"matroska\".\n"); + } else if (bstrcmp(ebml_master.doc_type, bstr0("matroska")) != 0 + && bstrcmp(ebml_master.doc_type, bstr0("webm")) != 0) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] no head found\n"); + talloc_free(parse_ctx.talloc_ctx); + return 0; + } + if (ebml_master.doc_type_read_version > 2) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] This looks like a Matroska file, " + "but we don't support format version %"PRIu64"\n", + ebml_master.doc_type_read_version); + talloc_free(parse_ctx.talloc_ctx); + return 0; + } + if ((ebml_master.n_ebml_read_version + && ebml_master.ebml_read_version != EBML_VERSION) + || (ebml_master.n_ebml_max_size_length + && ebml_master.ebml_max_size_length > 8) + || (ebml_master.n_ebml_max_id_length + && ebml_master.ebml_max_id_length != 4)) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] This looks like a Matroska file, " + "but the header has bad parameters\n"); + talloc_free(parse_ctx.talloc_ctx); + return 0; + } + talloc_free(parse_ctx.talloc_ctx); + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Found the head...\n"); + + if (ebml_read_id(s, NULL) != MATROSKA_ID_SEGMENT) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] but no segment :(\n"); + return 0; + } + ebml_read_length(s, NULL); /* return bytes number until EOF */ + + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] + a segment...\n"); + + mkv_d = talloc_zero(demuxer, struct mkv_demuxer); + demuxer->priv = mkv_d; + mkv_d->tc_scale = 1000000; + mkv_d->segment_start = stream_tell(s); + + while (1) { + uint32_t id = ebml_read_id(s, NULL); + if (s->eof) { + mp_tmsg(MSGT_DEMUX, MSGL_ERR, "[mkv] Unexpected end of file\n"); + return 0; + } + if (id == MATROSKA_ID_CLUSTER) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found cluster, headers are " + "parsed completely :)\n"); + stream_seek(s, stream_tell(s) - 4); + break; + } + int res = read_header_element(demuxer, id, 0); + if (res <= -2) + return 0; + if (res < 0) + break; + } + + display_create_tracks(demuxer); + + /* select video track */ + track = NULL; + if (demuxer->video->id == -1) { /* automatically select a video track */ + /* search for a video track that has the 'default' flag set */ + for (int i = 0; i < mkv_d->num_tracks; i++) + if (mkv_d->tracks[i]->type == MATROSKA_TRACK_VIDEO + && mkv_d->tracks[i]->default_track) { + track = mkv_d->tracks[i]; + break; + } + + if (track == NULL) + /* no track has the 'default' flag set */ + /* let's take the first video track */ + for (int i = 0; i < mkv_d->num_tracks; i++) + if (mkv_d->tracks[i]->type == MATROSKA_TRACK_VIDEO + && mkv_d->tracks[i]->id >= 0) { + track = mkv_d->tracks[i]; + break; + } + } else if (demuxer->video->id != -2) /* -2 = no video at all */ + track = find_track_by_num(mkv_d, demuxer->video->id, + MATROSKA_TRACK_VIDEO); + + if (track && demuxer->v_streams[track->id]) { + mp_tmsg(MSGT_DEMUX, MSGL_V, "[mkv] Will play video track %u.\n", + track->tnum); + demuxer->video->id = track->id; + demuxer->video->sh = demuxer->v_streams[track->id]; + } else { + mp_tmsg(MSGT_DEMUX, MSGL_INFO, "[mkv] No video track found/wanted.\n"); + demuxer->video->id = -2; + } + + demuxer->audio->id = -2; // wait for higher-level code to select track + + if (s->end_pos == 0) + demuxer->seekable = 0; + else { + demuxer->movi_start = s->start_pos; + demuxer->movi_end = s->end_pos; + demuxer->seekable = 1; + } + + demuxer->accurate_seek = true; + + return DEMUXER_TYPE_MATROSKA; +} + +static int demux_mkv_read_block_lacing(uint8_t *buffer, uint64_t *size, + uint8_t *laces, + uint32_t **all_lace_sizes) +{ + uint32_t total = 0; + uint32_t *lace_size = NULL; + uint8_t flags; + int i; + + *all_lace_sizes = NULL; + /* lacing flags */ + if (*size < 1) + goto error; + flags = *buffer++; + (*size)--; + + switch ((flags & 0x06) >> 1) { + case 0: /* no lacing */ + *laces = 1; + lace_size = calloc(*laces, sizeof(uint32_t)); + lace_size[0] = *size; + break; + + case 1: /* xiph lacing */ + case 2: /* fixed-size lacing */ + case 3: /* EBML lacing */ + if (*size < 1) + goto error; + *laces = *buffer++; + (*size)--; + (*laces)++; + lace_size = calloc(*laces, sizeof(uint32_t)); + + switch ((flags & 0x06) >> 1) { + case 1: /* xiph lacing */ + for (i = 0; i < *laces - 1; i++) { + lace_size[i] = 0; + do { + if (!*size) + goto error; + lace_size[i] += *buffer; + (*size)--; + } while (*buffer++ == 0xFF); + if (lace_size[i] > *size - total || total > *size) + goto error; + total += lace_size[i]; + } + lace_size[i] = *size - total; + break; + + case 2: /* fixed-size lacing */ + for (i = 0; i < *laces; i++) + lace_size[i] = *size / *laces; + break; + + case 3:; /* EBML lacing */ + int l; + uint64_t num = ebml_read_vlen_uint(buffer, &l); + if (num == EBML_UINT_INVALID) + goto error; + buffer += l; + if (*size < l) + goto error; + *size -= l; + if (num > *size) + goto error; + + total = lace_size[0] = num; + for (i = 1; i < *laces - 1; i++) { + int64_t snum = ebml_read_vlen_int(buffer, &l); + if (snum == EBML_INT_INVALID) + goto error; + buffer += l; + if (*size < l) + goto error; + *size -= l; + lace_size[i] = lace_size[i - 1] + snum; + if (lace_size[i] > *size - total || total > *size) + goto error; + total += lace_size[i]; + } + lace_size[i] = *size - total; + break; + } + break; + } + *all_lace_sizes = lace_size; + return 0; + + error: + free(lace_size); + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Bad input [lacing]\n"); + return 1; +} + +#define SKIP_BITS(n) buffer<<=n +#define SHOW_BITS(n) ((buffer)>>(32-(n))) + +static double real_fix_timestamp(unsigned char *buf, unsigned int timestamp, unsigned int format, int64_t *kf_base, int *kf_pts, double *pts){ + double v_pts; + unsigned char *s = buf + 1 + (*buf+1)*8; + uint32_t buffer= (s[0]<<24) + (s[1]<<16) + (s[2]<<8) + s[3]; + unsigned int kf=timestamp; + int pict_type; + + if(format==mmioFOURCC('R','V','3','0') || format==mmioFOURCC('R','V','4','0')){ + if(format==mmioFOURCC('R','V','3','0')){ + SKIP_BITS(3); + pict_type= SHOW_BITS(2); + SKIP_BITS(2 + 7); + }else{ + SKIP_BITS(1); + pict_type= SHOW_BITS(2); + SKIP_BITS(2 + 7 + 3); + } + kf= SHOW_BITS(13); // kf= 2*SHOW_BITS(12); +// if(pict_type==0) + if(pict_type<=1){ + // I frame, sync timestamps: + *kf_base=(int64_t)timestamp-kf; + mp_msg(MSGT_DEMUX, MSGL_DBG2,"\nTS: base=%08"PRIX64"\n",*kf_base); + kf=timestamp; + } else { + // P/B frame, merge timestamps: + int64_t tmp=(int64_t)timestamp-*kf_base; + kf|=tmp&(~0x1fff); // combine with packet timestamp + if(kf<tmp-4096) kf+=8192; else // workaround wrap-around problems + if(kf>tmp+4096) kf-=8192; + kf+=*kf_base; + } + if(pict_type != 3){ // P || I frame -> swap timestamps + unsigned int tmp=kf; + kf=*kf_pts; + *kf_pts=tmp; +// if(kf<=tmp) kf=0; + } + } + v_pts=kf*0.001f; +// if(pts && (v_pts<*pts || !kf)) v_pts=*pts+frametime; + if(pts) *pts=v_pts; + return v_pts; +} + +static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track, + uint8_t *buffer, uint32_t size, bool keyframe) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + demux_packet_t *dp; + uint32_t timestamp = mkv_d->last_pts * 1000; + + dp = new_demux_packet(size); + memcpy(dp->buffer, buffer, size); + + if (mkv_d->v_skip_to_keyframe) { + dp->pts = mkv_d->last_pts; + track->rv_kf_base = 0; + track->rv_kf_pts = timestamp; + } else + dp->pts = + real_fix_timestamp(dp->buffer, timestamp, + ((sh_video_t *) demuxer->video->sh)->bih-> + biCompression, &track->rv_kf_base, + &track->rv_kf_pts, NULL); + dp->pos = demuxer->filepos; + dp->keyframe = keyframe; + + ds_add_packet(demuxer->video, dp); +} + +static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track, + uint8_t *buffer, uint32_t size, bool keyframe) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + int sps = track->sub_packet_size; + int sph = track->sub_packet_h; + int cfs = track->coded_framesize; + int w = track->audiopk_size; + int spc = track->sub_packet_cnt; + demux_packet_t *dp; + int x; + + if ((track->a_formattag == mmioFOURCC('2', '8', '_', '8')) + || (track->a_formattag == mmioFOURCC('c', 'o', 'o', 'k')) + || (track->a_formattag == mmioFOURCC('a', 't', 'r', 'c')) + || (track->a_formattag == mmioFOURCC('s', 'i', 'p', 'r'))) { +// if(!block_bref) +// spc = track->sub_packet_cnt = 0; + switch (track->a_formattag) { + case mmioFOURCC('2', '8', '_', '8'): + for (x = 0; x < sph / 2; x++) + memcpy(track->audio_buf + x * 2 * w + spc * cfs, + buffer + cfs * x, cfs); + break; + case mmioFOURCC('c', 'o', 'o', 'k'): + case mmioFOURCC('a', 't', 'r', 'c'): + for (x = 0; x < w / sps; x++) + memcpy(track->audio_buf + + sps * (sph * x + ((sph + 1) / 2) * (spc & 1) + + (spc >> 1)), buffer + sps * x, sps); + break; + case mmioFOURCC('s', 'i', 'p', 'r'): + memcpy(track->audio_buf + spc * w, buffer, w); + if (spc == sph - 1) { + int n; + int bs = sph * w * 2 / 96; // nibbles per subpacket + // Perform reordering + for (n = 0; n < 38; n++) { + int j; + int i = bs * sipr_swaps[n][0]; + int o = bs * sipr_swaps[n][1]; + // swap nibbles of block 'i' with 'o' TODO: optimize + for (j = 0; j < bs; j++) { + int x = (i & 1) ? + (track->audio_buf[i >> 1] >> 4) : + (track->audio_buf[i >> 1] & 0x0F); + int y = (o & 1) ? + (track->audio_buf[o >> 1] >> 4) : + (track->audio_buf[o >> 1] & 0x0F); + if (o & 1) + track->audio_buf[o >> 1] = + (track->audio_buf[o >> 1] & 0x0F) | (x << 4); + else + track->audio_buf[o >> 1] = + (track->audio_buf[o >> 1] & 0xF0) | x; + if (i & 1) + track->audio_buf[i >> 1] = + (track->audio_buf[i >> 1] & 0x0F) | (y << 4); + else + track->audio_buf[i >> 1] = + (track->audio_buf[i >> 1] & 0xF0) | y; + ++i; + ++o; + } + } + } + break; + } + track->audio_timestamp[track->sub_packet_cnt] = + (track->ra_pts == mkv_d->last_pts) ? 0 : (mkv_d->last_pts); + track->ra_pts = mkv_d->last_pts; + if (track->sub_packet_cnt == 0) + track->audio_filepos = demuxer->filepos; + if (++(track->sub_packet_cnt) == sph) { + int apk_usize = + ((sh_audio_t *) demuxer->audio->sh)->wf->nBlockAlign; + track->sub_packet_cnt = 0; + // Release all the audio packets + for (x = 0; x < sph * w / apk_usize; x++) { + dp = new_demux_packet(apk_usize); + memcpy(dp->buffer, track->audio_buf + x * apk_usize, + apk_usize); + /* Put timestamp only on packets that correspond to original + * audio packets in file */ + dp->pts = (x * apk_usize % w) ? 0 : + track->audio_timestamp[x * apk_usize / w]; + dp->pos = track->audio_filepos; // all equal + dp->keyframe = !x; // Mark first packet as keyframe + ds_add_packet(demuxer->audio, dp); + } + } + } else { // Not a codec that require reordering + dp = new_demux_packet(size); + memcpy(dp->buffer, buffer, size); + if (track->ra_pts == mkv_d->last_pts && !mkv_d->a_skip_to_keyframe) + dp->pts = 0; + else + dp->pts = mkv_d->last_pts; + track->ra_pts = mkv_d->last_pts; + + dp->pos = demuxer->filepos; + dp->keyframe = keyframe; + ds_add_packet(demuxer->audio, dp); + } +} + +static int handle_block(demuxer_t *demuxer, uint8_t *block, uint64_t length, + uint64_t block_duration, bool keyframe, + bool simpleblock) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + mkv_track_t *track = NULL; + demux_stream_t *ds = NULL; + uint64_t old_length; + uint64_t tc; + uint32_t *lace_size; + uint8_t laces, flags; + int i, num, tmp, use_this_block = 1; + double current_pts; + int16_t time; + + /* first byte(s): track num */ + num = ebml_read_vlen_uint(block, &tmp); + block += tmp; + /* time (relative to cluster time) */ + time = block[0] << 8 | block[1]; + block += 2; + length -= tmp + 2; + old_length = length; + flags = block[0]; + if (simpleblock) + keyframe = flags & 0x80; + if (demux_mkv_read_block_lacing(block, &length, &laces, &lace_size)) + return 0; + block += old_length - length; + + tc = time * mkv_d->tc_scale + mkv_d->cluster_tc; + current_pts = tc / 1e9; + + for (i = 0; i < mkv_d->num_tracks; i++) + if (mkv_d->tracks[i]->tnum == num) { + track = mkv_d->tracks[i]; + break; + } + if (track == NULL) { + free(lace_size); + return 1; + } + if (track->type == MATROSKA_TRACK_AUDIO + && track->id == demuxer->audio->id) { + ds = demuxer->audio; + + if (mkv_d->a_skip_to_keyframe) + use_this_block = keyframe; + if (mkv_d->v_skip_to_keyframe) + use_this_block = 0; + + if (track->fix_i_bps && use_this_block) { + sh_audio_t *sh = (sh_audio_t *) ds->sh; + + if (block_duration != 0) { + sh->i_bps = length * 1e9 / block_duration; + track->fix_i_bps = 0; + } else if (track->qt_last_a_pts == 0.0) + track->qt_last_a_pts = current_pts; + else if (track->qt_last_a_pts != current_pts) { + sh->i_bps = length / (current_pts - track->qt_last_a_pts); + track->fix_i_bps = 0; + } + } + } else if (tc < mkv_d->skip_to_timecode) + use_this_block = 0; + else if (track->type == MATROSKA_TRACK_VIDEO + && track->id == demuxer->video->id) { + ds = demuxer->video; + if (mkv_d->v_skip_to_keyframe) + use_this_block = keyframe; + } else if (track->type == MATROSKA_TRACK_SUBTITLE + && track->id == demuxer->sub->id) { + ds = demuxer->sub; + if (laces > 1) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Subtitles use Matroska " + "lacing. This is abnormal and not supported.\n"); + use_this_block = 0; + } + } else + use_this_block = 0; + + if (use_this_block) { + mkv_d->last_pts = current_pts; + mkv_d->last_filepos = demuxer->filepos; + + for (i = 0; i < laces; i++) { + if (ds == demuxer->video && track->realmedia) + handle_realvideo(demuxer, track, block, lace_size[i], + keyframe); + else if (ds == demuxer->audio && track->realmedia) + handle_realaudio(demuxer, track, block, lace_size[i], + keyframe); + else { + int size = lace_size[i]; + demux_packet_t *dp; + uint8_t *buffer; + demux_mkv_decode(track, block, &buffer, &size, 1); + if (buffer) { + dp = new_demux_packet(size); + memcpy(dp->buffer, buffer, size); + if (buffer != block) + talloc_free(buffer); + dp->keyframe = keyframe; + /* If default_duration is 0, assume no pts value is known + * for packets after the first one (rather than all pts + * values being the same) */ + if (i == 0 || track->default_duration) + dp->pts = + mkv_d->last_pts + i * track->default_duration; + dp->duration = block_duration / 1e9; + ds_add_packet(ds, dp); + } + } + block += lace_size[i]; + } + + if (ds == demuxer->video) { + mkv_d->v_skip_to_keyframe = 0; + mkv_d->skip_to_timecode = 0; + } else if (ds == demuxer->audio) + mkv_d->a_skip_to_keyframe = 0; + + free(lace_size); + return 1; + } + + free(lace_size); + return 0; +} + +static int demux_mkv_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + stream_t *s = demuxer->stream; + uint64_t l; + int il, tmp; + + while (1) { + while (mkv_d->cluster_size > 0) { + uint64_t block_duration = 0, block_length = 0; + bool keyframe = true; + uint8_t *block = NULL; + + while (mkv_d->blockgroup_size > 0) { + switch (ebml_read_id(s, &il)) { + case MATROSKA_ID_BLOCKDURATION: + block_duration = ebml_read_uint(s, &l); + if (block_duration == EBML_UINT_INVALID) { + free(block); + return 0; + } + block_duration *= mkv_d->tc_scale; + break; + + case MATROSKA_ID_BLOCK: + block_length = ebml_read_length(s, &tmp); + free(block); + if (block_length > 500000000) + return 0; + block = malloc(block_length + AV_LZO_INPUT_PADDING); + demuxer->filepos = stream_tell(s); + if (stream_read(s, block, block_length) != + (int) block_length) { + free(block); + return 0; + } + l = tmp + block_length; + break; + + case MATROSKA_ID_REFERENCEBLOCK:; + int64_t num = ebml_read_int(s, &l); + if (num == EBML_INT_INVALID) { + free(block); + return 0; + } + if (num) + keyframe = false; + break; + + case EBML_ID_INVALID: + free(block); + return 0; + + default: + ebml_read_skip(s, &l); + break; + } + mkv_d->blockgroup_size -= l + il; + mkv_d->cluster_size -= l + il; + } + + if (block) { + int res = handle_block(demuxer, block, block_length, + block_duration, keyframe, false); + free(block); + if (res < 0) + return 0; + if (res) + return 1; + } + + if (mkv_d->cluster_size > 0) { + switch (ebml_read_id(s, &il)) { + case MATROSKA_ID_TIMECODE:; + uint64_t num = ebml_read_uint(s, &l); + if (num == EBML_UINT_INVALID) + return 0; + mkv_d->cluster_tc = num * mkv_d->tc_scale; + add_cluster_position(mkv_d, mkv_d->cluster_start, + mkv_d->cluster_tc); + break; + + case MATROSKA_ID_BLOCKGROUP: + mkv_d->blockgroup_size = ebml_read_length(s, &tmp); + l = tmp; + break; + + case MATROSKA_ID_SIMPLEBLOCK:; + int res; + block_length = ebml_read_length(s, &tmp); + if (block_length > 500000000) + return 0; + block = malloc(block_length); + demuxer->filepos = stream_tell(s); + if (stream_read(s, block, block_length) != + (int) block_length) { + free(block); + return 0; + } + l = tmp + block_length; + res = handle_block(demuxer, block, block_length, + block_duration, false, true); + free(block); + mkv_d->cluster_size -= l + il; + if (res < 0) + return 0; + else if (res) + return 1; + else + mkv_d->cluster_size += l + il; + break; + + case EBML_ID_INVALID: + return 0; + + default: + ebml_read_skip(s, &l); + break; + } + mkv_d->cluster_size -= l + il; + } + } + + while (ebml_read_id(s, &il) != MATROSKA_ID_CLUSTER) { + ebml_read_skip(s, NULL); + if (s->eof) + return 0; + } + mkv_d->cluster_start = stream_tell(s) - il; + mkv_d->cluster_size = ebml_read_length(s, NULL); + } + + return 0; +} + +static int seek_creating_index(struct demuxer *demuxer, float rel_seek_secs, + int flags) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + struct stream *s = demuxer->stream; + int64_t target_tc_ns = (int64_t) (rel_seek_secs * 1e9); + if (target_tc_ns < 0) + target_tc_ns = 0; + uint64_t max_filepos = 0; + int64_t max_tc = -1; + int n = mkv_d->num_cluster_pos; + if (n > 0) { + max_filepos = mkv_d->cluster_positions[n - 1].filepos; + max_tc = mkv_d->cluster_positions[n - 1].timecode; + } + + if (target_tc_ns > max_tc) { + if ((off_t) max_filepos > stream_tell(s)) + stream_seek(s, max_filepos); + else + stream_seek(s, stream_tell(s) + mkv_d->cluster_size); + /* parse all the clusters upto target_filepos */ + while (!s->eof) { + uint64_t start = stream_tell(s); + uint32_t type = ebml_read_id(s, NULL); + uint64_t len = ebml_read_length(s, NULL); + uint64_t end = stream_tell(s) + len; + if (type == MATROSKA_ID_CLUSTER) { + while (!s->eof && stream_tell(s) < end) { + if (ebml_read_id(s, NULL) == MATROSKA_ID_TIMECODE) { + uint64_t tc = ebml_read_uint(s, NULL); + tc *= mkv_d->tc_scale; + add_cluster_position(mkv_d, start, tc); + if (tc >= target_tc_ns) + goto enough_index; + break; + } + } + } + if (s->eof) + break; + stream_seek(s, end); + } + enough_index: + if (s->eof) + stream_reset(s); + } + if (!mkv_d->num_cluster_pos) { + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] no target for seek found\n"); + return -1; + } + uint64_t cluster_pos = mkv_d->cluster_positions[0].filepos; + /* Let's find the nearest cluster */ + int64_t min_diff = 0xFFFFFFFFFFFFFFF; + for (int i = 0; i < mkv_d->num_cluster_pos; i++) { + int64_t diff = mkv_d->cluster_positions[i].timecode - target_tc_ns; + if (flags & SEEK_BACKWARD && diff < 0 && -diff < min_diff) { + cluster_pos = mkv_d->cluster_positions[i].filepos; + min_diff = -diff; + } else if (flags & SEEK_FORWARD + && (diff < 0 ? -1 * diff : diff) < min_diff) { + cluster_pos = mkv_d->cluster_positions[i].filepos; + min_diff = diff < 0 ? -1 * diff : diff; + } + } + mkv_d->cluster_size = mkv_d->blockgroup_size = 0; + stream_seek(s, cluster_pos); + return 0; +} + +static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id, + int64_t target_timecode, int flags) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + struct mkv_index *index = NULL; + + /* Find the entry in the index closest to the target timecode in the + * give direction. If there are no such entries - we're trying to seek + * backward from a target time before the first entry or forward from a + * target time after the last entry - then still seek to the first/last + * entry if that's further in the direction wanted than mkv_d->last_pts. + */ + int64_t min_diff = target_timecode - (int64_t)(mkv_d->last_pts * 1e9 + 0.5); + if (flags & SEEK_BACKWARD) + min_diff = -min_diff; + min_diff = FFMAX(min_diff, 1); + for (int i = 0; i < mkv_d->num_indexes; i++) + if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) { + int64_t diff = + target_timecode - + (int64_t) (mkv_d->indexes[i].timecode * mkv_d->tc_scale); + if (flags & SEEK_BACKWARD) + diff = -diff; + if (diff <= 0) { + if (min_diff <= 0 && diff <= min_diff) + continue; + } else if (diff >= min_diff) + continue; + min_diff = diff; + index = mkv_d->indexes + i; + } + + if (index) { /* We've found an entry. */ + mkv_d->cluster_size = mkv_d->blockgroup_size = 0; + stream_seek(demuxer->stream, index->filepos); + } + return index; +} + +static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, + float audio_delay, int flags) +{ + mkv_demuxer_t *mkv_d = demuxer->priv; + uint64_t v_tnum = -1; + if (demuxer->video->id >= 0) + v_tnum = find_track_by_num(mkv_d, demuxer->video->id, + MATROSKA_TRACK_VIDEO)->tnum; + uint64_t a_tnum = -1; + if (demuxer->audio->id >= 0) + a_tnum = find_track_by_num(mkv_d, demuxer->audio->id, + MATROSKA_TRACK_AUDIO)->tnum; + if (!(flags & (SEEK_BACKWARD | SEEK_FORWARD))) { + if (flags & SEEK_ABSOLUTE || rel_seek_secs < 0) + flags |= SEEK_BACKWARD; + else + flags |= SEEK_FORWARD; + } + // Adjust the target a little bit to catch cases where the target position + // specifies a keyframe with high, but not perfect, precision. + rel_seek_secs += flags & SEEK_FORWARD ? -0.005 : 0.005; + + if (!(flags & SEEK_FACTOR)) { /* time in secs */ + mkv_index_t *index = NULL; + + if (!(flags & SEEK_ABSOLUTE)) /* relative seek */ + rel_seek_secs += mkv_d->last_pts; + rel_seek_secs = FFMAX(rel_seek_secs, 0); + int64_t target_timecode = rel_seek_secs * 1e9 + 0.5; + + if (mkv_d->indexes == NULL) { /* no index was found */ + if (seek_creating_index(demuxer, rel_seek_secs, flags) < 0) + return; + } else { + int seek_id = (demuxer->video->id < 0) ? + a_tnum : v_tnum; + index = seek_with_cues(demuxer, seek_id, target_timecode, flags); + if (!index) + index = seek_with_cues(demuxer, -1, target_timecode, flags); + } + + if (demuxer->video->id >= 0) + mkv_d->v_skip_to_keyframe = 1; + if (flags & SEEK_FORWARD) + mkv_d->skip_to_timecode = target_timecode; + else + mkv_d->skip_to_timecode = index ? index->timecode * mkv_d->tc_scale + : 0; + mkv_d->a_skip_to_keyframe = 1; + + demux_mkv_fill_buffer(demuxer, NULL); + } else if ((demuxer->movi_end <= 0) || !(flags & SEEK_ABSOLUTE)) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek unsupported flags\n"); + else { + stream_t *s = demuxer->stream; + uint64_t target_filepos; + mkv_index_t *index = NULL; + int i; + + if (mkv_d->indexes == NULL) { /* not implemented without index */ + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek unsupported flags\n"); + return; + } + + target_filepos = (uint64_t) (demuxer->movi_end * rel_seek_secs); + for (i = 0; i < mkv_d->num_indexes; i++) + if (mkv_d->indexes[i].tnum == v_tnum) + if ((index == NULL) + || ((mkv_d->indexes[i].filepos >= target_filepos) + && ((index->filepos < target_filepos) + || (mkv_d->indexes[i].filepos < index->filepos)))) + index = &mkv_d->indexes[i]; + + if (!index) + return; + + mkv_d->cluster_size = mkv_d->blockgroup_size = 0; + stream_seek(s, index->filepos); + + if (demuxer->video->id >= 0) + mkv_d->v_skip_to_keyframe = 1; + mkv_d->skip_to_timecode = index->timecode * mkv_d->tc_scale; + mkv_d->a_skip_to_keyframe = 1; + + demux_mkv_fill_buffer(demuxer, NULL); + } +} + +static int demux_mkv_control(demuxer_t *demuxer, int cmd, void *arg) +{ + mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; + + switch (cmd) { + case DEMUXER_CTRL_CORRECT_PTS: + return DEMUXER_CTRL_OK; + case DEMUXER_CTRL_GET_TIME_LENGTH: + if (mkv_d->duration == 0) + return DEMUXER_CTRL_DONTKNOW; + + *((double *) arg) = (double) mkv_d->duration; + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_GET_PERCENT_POS: + if (mkv_d->duration == 0) { + return DEMUXER_CTRL_DONTKNOW; + } + + *((int *) arg) = (int) (100 * mkv_d->last_pts / mkv_d->duration); + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_SWITCH_AUDIO:; + int new_aid = *(int *) arg; + int current_aid = demuxer->audio->id; + if (current_aid < 0) + current_aid = -1; + if (new_aid == -1) { // cycle to next + new_aid = current_aid; + while (1) { + new_aid = (new_aid + 2) % (mkv_d->num_audio_tracks + 1) - 1; + if (new_aid == -1 || demuxer->a_streams[new_aid]) + break; + } + } + if (new_aid < 0 || new_aid >= mkv_d->num_audio_tracks || + !demuxer->a_streams[new_aid]) + new_aid = -2; + *(int *) arg = new_aid; + if (current_aid != new_aid) + ds_free_packs(demuxer->audio); + demuxer->audio->id = new_aid; + return DEMUXER_CTRL_OK; + + case DEMUXER_CTRL_SWITCH_VIDEO:; + int new_vid = *(int *) arg; + int current_vid = demuxer->video->id; + if (current_vid < 0) + current_vid = -1; + if (new_vid == -1) { // cycle to next + new_vid = current_vid; + while (1) { + new_vid = (new_vid + 2) % (mkv_d->num_video_tracks + 1) - 1; + if (new_vid == -1 || demuxer->v_streams[new_vid]) + break; + } + } + if (new_vid < 0 || new_vid >= mkv_d->num_video_tracks || + !demuxer->v_streams[new_vid]) + new_vid = -2; + *(int *) arg = new_vid; + if (current_vid != new_vid) + ds_free_packs(demuxer->video); + demuxer->video->id = new_vid; + return DEMUXER_CTRL_OK; + + default: + return DEMUXER_CTRL_NOTIMPL; + } +} + +const demuxer_desc_t demuxer_desc_matroska = { + "Matroska demuxer", + "mkv", + "Matroska", + "Aurelien Jacobs", + "", + DEMUXER_TYPE_MATROSKA, + 1, // safe autodetect + demux_mkv_open, + demux_mkv_fill_buffer, + NULL, + mkv_free, + demux_mkv_seek, + demux_mkv_control +}; diff --git a/demux/demux_mng.c b/demux/demux_mng.c new file mode 100644 index 0000000000..ad08ba8c4b --- /dev/null +++ b/demux/demux_mng.c @@ -0,0 +1,623 @@ +/* + * MNG file demuxer for MPlayer + * + * Copyright (C) 2008 Stefan Schuermans <stefan blinkenarea org> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" + +#include "mp_msg.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "libmpcodecs/img_format.h" + +#define MNG_SUPPORT_READ +#define MNG_SUPPORT_DISPLAY +#include <libmng.h> + +/** + * \brief some small fixed start time > 0 + * + * Start time must be > 0 for the variable frame time mechanism + * (GIF, MATROSKA, MNG) in video.c to work for the first frame. + */ +#define MNG_START_PTS 0.01f + +/** + * \brief private context structure + * + * This structure is used as private data for MPlayer demuxer + * and also as private data for the MNG library. + * + * All members ending in \p _ms are in milliseconds + */ +typedef struct { + stream_t * stream; ///< pointer to MNG data input stream + mng_handle h_mng; ///< MNG library image handle + int header_processed; ///< if MNG image header is processed + mng_uint32 width; ///< MNG image width + mng_uint32 height; ///< MNG image height + int total_time_ms; ///< total MNG animation time + unsigned char * canvas; /**< \brief canvas to draw the image onto + * \details + * \li lines top-down + * \li pixels left-to-right + * \li channels RGB + * \li no padding + * \li NULL if no canvas yet + */ + int displaying; /**< \brief if displaying already, + * i.e. if mng_display has + * already been called + */ + int finished; ///< if animation is finished + int global_time_ms; ///< current global time for MNG library + int anim_cur_time_ms; ///< current frame time in MNG animation + int anim_frame_duration_ms; ///< current frame duration in MNG animation + int show_cur_time_ms; /**< \brief current time in the show process, + * i.e. time of last demux packet + */ + int show_next_time_ms; /**< \brief next time in the show process, + * i.e. time of next demux packet + */ + int timer_ms; /**< \brief number of milliseconds after which + * libmng wants to be called again + */ +} mng_priv_t; + +/** + * \brief MNG library callback: Allocate a new zero-filled memory block. + * \param[in] size memory block size + * \return pointer to new memory block + */ +static mng_ptr demux_mng_alloc(mng_size_t size) +{ + return calloc(1, size); +} + +/** + * \brief MNG library callback: Free memory block. + * \param[in] ptr pointer to memory block + * \param[in] size memory block size + */ +static void demux_mng_free(mng_ptr ptr, mng_size_t size) +{ + free(ptr); +} + +/** + * \brief MNG library callback: Open MNG stream. + * \param[in] h_mng MNG library image handle + * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens) + */ +static mng_bool demux_mng_openstream(mng_handle h_mng) +{ + mng_priv_t * mng_priv = mng_get_userdata(h_mng); + stream_t * stream = mng_priv->stream; + + // rewind stream to the beginning + stream_seek(stream, stream->start_pos); + + return MNG_TRUE; +} + +/** + * \brief MNG library callback: Close MNG stream. + * \param[in] h_mng MNG library image handle + * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens) + */ +static mng_bool demux_mng_closestream(mng_handle h_mng) +{ + return MNG_TRUE; +} + +/** + * \brief MNG library callback: Read data from stream. + * \param[in] h_mng MNG library image handle + * \param[in] buf pointer to buffer to fill with data + * \param[in] size size of buffer + * \param[out] read number of bytes read from stream + * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens) + */ +static mng_bool demux_mng_readdata(mng_handle h_mng, mng_ptr buf, + mng_uint32 size, mng_uint32 * read) +{ + mng_priv_t * mng_priv = mng_get_userdata(h_mng); + stream_t * stream = mng_priv->stream; + + // simply read data from stream and return number of bytes or error + *read = stream_read(stream, buf, size); + + return MNG_TRUE; +} + +/** + * \brief MNG library callback: Header information is processed now. + * \param[in] h_mng MNG library image handle + * \param[in] width image width + * \param[in] height image height + * \return \p MNG_TRUE on success, \p MNG_FALSE on error + */ +static mng_bool demux_mng_processheader(mng_handle h_mng, mng_uint32 width, + mng_uint32 height) +{ + mng_priv_t * mng_priv = mng_get_userdata(h_mng); + + // remember size in private data + mng_priv->header_processed = 1; + mng_priv->width = width; + mng_priv->height = height; + + // get total animation time + mng_priv->total_time_ms = mng_get_playtime(h_mng); + + // allocate canvas + mng_priv->canvas = malloc(height * width * 4); + if (!mng_priv->canvas) { + mp_msg(MSGT_DEMUX, MSGL_ERR, + "demux_mng: could not allocate canvas of size %dx%d\n", + width, height); + return MNG_FALSE; + } + + return MNG_TRUE; +} + +/** + * \brief MNG library callback: Get access to a canvas line. + * \param[in] h_mng MNG library image handle + * \param[in] line y coordinate of line to access + * \return pointer to line on success, \p MNG_NULL on error + */ +static mng_ptr demux_mng_getcanvasline(mng_handle h_mng, mng_uint32 line) +{ + mng_priv_t * mng_priv = mng_get_userdata(h_mng); + + // return pointer to canvas line + if (line < mng_priv->height && mng_priv->canvas) + return (mng_ptr)(mng_priv->canvas + line * mng_priv->width * 4); + else + return (mng_ptr)MNG_NULL; +} + +/** + * \brief MNG library callback: A part of the canvas should be shown. + * + * This function is called by libmng whenever it thinks a + * rectangular part of the display should be updated. This + * can happen multiple times for a frame and/or a single time + * for a frame. Only the the part of the display occupied by + * the rectangle defined by x, y, width, height is to be updated. + * It is possible that some parts of the display are not updated + * for many frames. There is no chance here to find out if the + * current frame is completed with this update or not. + * + * This mechanism does not match MPlayer's demuxer architecture, + * so it will not be used exactly as intended by libmng. + * A new frame is generated in the demux_mng_fill_buffer() function + * whenever libmng tells us to wait for some time. + * + * \param[in] h_mng MNG library image handle + * \param[in] x rectangle's left edge + * \param[in] y rectangle's top edge + * \param[in] width rectangle's width + * \param[in] height rectangle's heigt + * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens) + */ +static mng_bool demux_mng_refresh(mng_handle h_mng, mng_uint32 x, mng_uint32 y, + mng_uint32 width, mng_uint32 height) +{ + // nothing to do here, the image data is already on the canvas + return MNG_TRUE; +} + +/** + * \brief MNG library callback: Get how many milliseconds have passed. + * \param[in] h_mng MNG library image handle + * \return global time in milliseconds + */ +static mng_uint32 demux_mng_gettickcount(mng_handle h_mng) +{ + mng_priv_t * mng_priv = mng_get_userdata(h_mng); + + // return current global time + return mng_priv->global_time_ms; +} + +/** + * \brief MNG library callback: Please call again after some milliseconds. + * \param[in] h_mng MNG library image handle + * \param[in] msecs number of milliseconds after which to call again + * \return \p MNG_TRUE on success, \p MNG_FALSE on error (never happens) + */ +static mng_bool demux_mng_settimer(mng_handle h_mng, mng_uint32 msecs) +{ + mng_priv_t * mng_priv = mng_get_userdata(h_mng); + + // Save number of milliseconds after which to call the MNG library again + // in private data. + mng_priv->timer_ms = msecs; + return MNG_TRUE; +} + +/** + * \brief MPlayer callback: Check if stream contains MNG data. + * \param[in] demuxer demuxer structure + * \return demuxer type constant, \p 0 if unknown + */ +static int demux_mng_check_file(demuxer_t *demuxer) +{ + char buf[4]; + if (stream_read(demuxer->stream, buf, 4) != 4) + return 0; + if (memcmp(buf, "\x8AMNG", 4)) + return 0; + return DEMUXER_TYPE_MNG; +} + +/** + * \brief MPlayer callback: Fill buffer from MNG stream. + * \param[in] demuxer demuxer structure + * \param[in] ds demuxer stream + * \return \p 1 on success, \p 0 on error + */ +static int demux_mng_fill_buffer(demuxer_t * demuxer, + demux_stream_t * ds) +{ + mng_priv_t * mng_priv = demuxer->priv; + mng_handle h_mng = mng_priv->h_mng; + mng_retcode mng_ret; + demux_packet_t * dp; + + // exit if animation is finished + if (mng_priv->finished) + return 0; + + // advance animation to requested next show time + while (mng_priv->anim_cur_time_ms + mng_priv->anim_frame_duration_ms + <= mng_priv->show_next_time_ms && !mng_priv->finished) { + + // advance global and animation time + mng_priv->global_time_ms += mng_priv->anim_frame_duration_ms; + mng_priv->anim_cur_time_ms += mng_priv->anim_frame_duration_ms; + + // Clear variable MNG library will write number of milliseconds to + // (via settimer callback). + mng_priv->timer_ms = 0; + + // get next image from MNG library + if (mng_priv->displaying) + mng_ret = mng_display_resume(h_mng); // resume displaying MNG data + // to canvas + else + mng_ret = mng_display(h_mng); // start displaying MNG data to canvas + if (mng_ret && mng_ret != MNG_NEEDTIMERWAIT) { + mp_msg(MSGT_DEMUX, MSGL_ERR, + "demux_mng: could not display MNG data to canvas: " + "mng_retcode %d\n", mng_ret); + return 0; + } + mng_priv->displaying = 1; // mng_display() has been called now + mng_priv->finished = mng_ret == 0; // animation is finished iff + // mng_display() returned 0 + + // save current frame duration + mng_priv->anim_frame_duration_ms = mng_priv->timer_ms < 1 + ? 1 : mng_priv->timer_ms; + + } // while (mng_priv->anim_cur_time_ms + ... + + // create a new demuxer packet + dp = new_demux_packet(mng_priv->height * mng_priv->width * 4); + + // copy image data into demuxer packet + memcpy(dp->buffer, mng_priv->canvas, + mng_priv->height * mng_priv->width * 4); + + // set current show time to requested show time + mng_priv->show_cur_time_ms = mng_priv->show_next_time_ms; + + // get time of next frame to show + mng_priv->show_next_time_ms = mng_priv->anim_cur_time_ms + + mng_priv->anim_frame_duration_ms; + + // Set position and timing information in demuxer video and demuxer packet. + // - Time must be time of next frame and always be > 0 for the variable + // frame time mechanism (GIF, MATROSKA, MNG) in video.c to work. + demuxer->video->dpos++; + dp->pts = (float)mng_priv->show_next_time_ms / 1000.0f + MNG_START_PTS; + dp->pos = stream_tell(demuxer->stream); + ds_add_packet(demuxer->video, dp); + + return 1; +} + +/** + * \brief MPlayer callback: Open MNG stream. + * \param[in] demuxer demuxer structure + * \return demuxer structure on success, \p NULL on error + */ +static demuxer_t * demux_mng_open(demuxer_t * demuxer) +{ + mng_priv_t * mng_priv; + mng_handle h_mng; + mng_retcode mng_ret; + sh_video_t * sh_video; + + // create private data structure + mng_priv = calloc(1, sizeof(mng_priv_t)); + + //stream pointer into private data + mng_priv->stream = demuxer->stream; + + // initialize MNG image instance + h_mng = mng_initialize((mng_ptr)mng_priv, demux_mng_alloc, + demux_mng_free, MNG_NULL); + if (!h_mng) { + mp_msg(MSGT_DEMUX, MSGL_ERR, + "demux_mng: could not initialize MNG image instance\n"); + free(mng_priv); + return NULL; + } + + // MNG image handle into private data + mng_priv->h_mng = h_mng; + + // set required MNG callbacks + if (mng_setcb_openstream(h_mng, demux_mng_openstream) || + mng_setcb_closestream(h_mng, demux_mng_closestream) || + mng_setcb_readdata(h_mng, demux_mng_readdata) || + mng_setcb_processheader(h_mng, demux_mng_processheader) || + mng_setcb_getcanvasline(h_mng, demux_mng_getcanvasline) || + mng_setcb_refresh(h_mng, demux_mng_refresh) || + mng_setcb_gettickcount(h_mng, demux_mng_gettickcount) || + mng_setcb_settimer(h_mng, demux_mng_settimer) || + mng_set_canvasstyle(h_mng, MNG_CANVAS_RGBA8)) { + mp_msg(MSGT_DEMUX, MSGL_ERR, + "demux_mng: could not set MNG callbacks\n"); + mng_cleanup(&h_mng); + free(mng_priv); + return NULL; + } + + // start reading MNG data + mng_ret = mng_read(h_mng); + if (mng_ret) { + mp_msg(MSGT_DEMUX, MSGL_ERR, + "demux_mng: could not start reading MNG data: " + "mng_retcode %d\n", mng_ret); + mng_cleanup(&h_mng); + free(mng_priv); + return NULL; + } + + // check that MNG header is processed now + if (!mng_priv->header_processed) { + mp_msg(MSGT_DEMUX, MSGL_ERR, + "demux_mng: internal error: header not processed\n"); + mng_cleanup(&h_mng); + free(mng_priv); + return NULL; + } + + // create a new video stream header + sh_video = new_sh_video(demuxer, 0); + + // Make sure the demuxer knows about the new video stream header + // (even though new_sh_video() ought to take care of it). + // (Thanks to demux_gif.c for this.) + demuxer->video->sh = sh_video; + + // Make sure that the video demuxer stream header knows about its + // parent video demuxer stream (this is getting wacky), or else + // video_read_properties() will choke. + // (Thanks to demux_gif.c for this.) + sh_video->ds = demuxer->video; + + // set format of pixels in video packets + sh_video->format = IMGFMT_RGB32; + + // set framerate to some value (MNG does not have a fixed framerate) + sh_video->fps = 5.0f; + sh_video->frametime = 1.0f / sh_video->fps; + + // set video frame parameters + sh_video->bih = calloc(1, sizeof(*sh_video->bih)); + sh_video->bih->biCompression = sh_video->format; + sh_video->bih->biWidth = mng_priv->width; + sh_video->bih->biHeight = mng_priv->height; + sh_video->bih->biBitCount = 32; + sh_video->bih->biPlanes = 1; + + // Set start time to something > 0. + // - This is required for the variable frame time mechanism + // (GIF, MATROSKA, MNG) in video.c to work for the first frame. + sh_video->ds->pts = MNG_START_PTS; + + // set private data in demuxer and return demuxer + demuxer->priv = mng_priv; + return demuxer; +} + +/** + * \brief MPlayer callback: Close MNG stream. + * \param[in] demuxer demuxer structure + */ +static void demux_mng_close(demuxer_t* demuxer) +{ + mng_priv_t * mng_priv = demuxer->priv; + + if (mng_priv) { + + // shutdown MNG image instance + if (mng_priv->h_mng) + mng_cleanup(&mng_priv->h_mng); + + // free private data + free(mng_priv->canvas); + + free(mng_priv); + } +} + +/** + * \brief MPlayer callback: Seek in MNG stream. + * \param[in] demuxer demuxer structure + * \param[in] rel_seek_secs relative seek time in seconds + * \param[in] audio_delay unused, MNG does not contain audio + * \param[in] flags bit flags, \p 1: absolute, \p 2: fractional position + */ +static void demux_mng_seek(demuxer_t * demuxer, float rel_seek_secs, + float audio_delay, int flags) +{ + mng_priv_t * mng_priv = demuxer->priv; + mng_handle h_mng = mng_priv->h_mng; + mng_retcode mng_ret; + int seek_ms, pos_ms; + + // exit if not ready to seek (header not yet read or not yet displaying) + if (!mng_priv->header_processed || !mng_priv->displaying) + return; + + // get number of milliseconds to seek to + if (flags & 2) // seek by fractional position (0.0 ... 1.0) + seek_ms = (int)(rel_seek_secs * (float)mng_priv->total_time_ms); + else // seek by time in seconds + seek_ms = (int)(rel_seek_secs * 1000.0f + 0.5f); + + // get new position in milliseconds + if (flags & 1) // absolute + pos_ms = seek_ms; + else // relative + pos_ms = mng_priv->show_cur_time_ms + seek_ms; + + // fix position + if (pos_ms < 0) + pos_ms = 0; + if (pos_ms > mng_priv->total_time_ms) + pos_ms = mng_priv->total_time_ms; + + // FIXME + // In principle there is a function to seek in MNG: mng_display_gotime(). + // - Using it did not work out (documentation is very brief, + // example code does not exist?). + // - The following code works, but its performance is quite bad. + + // seeking forward + if (pos_ms >= mng_priv->show_cur_time_ms) { + + // Simply advance show time to seek position. + // - Everything else will be handled in demux_mng_fill_buffer(). + mng_priv->show_next_time_ms = pos_ms; + + } // if (pos_ms > mng_priv->show_time_ms) + + // seeking backward + else { // if (pos_ms > mng_priv->show_time_ms) + + // Clear variable MNG library will write number of milliseconds to + // (via settimer callback). + mng_priv->timer_ms = 0; + + // Restart displaying and advance show time to seek position. + // - Everything else will be handled in demux_mng_fill_buffer(). + mng_ret = mng_display_reset(h_mng); + // If a timer wait is needed, fool libmng that requested time + // passed and try again. + if (mng_ret == MNG_NEEDTIMERWAIT) { + mng_priv->global_time_ms += mng_priv->timer_ms; + mng_ret = mng_display_reset(h_mng); + } + if (mng_ret) { + mp_msg(MSGT_DEMUX, MSGL_ERR, + "demux_mng: could not reset MNG display state: " + "mng_retcode %d\n", mng_ret); + return; + } + mng_priv->displaying = 0; + mng_priv->finished = 0; + mng_priv->anim_cur_time_ms = 0; + mng_priv->anim_frame_duration_ms = 0; + mng_priv->show_next_time_ms = pos_ms; + + } // if (pos_ms > mng_priv->show_time_ms) ... else +} + +/** + * \brief MPlayer callback: Control MNG stream. + * \param[in] demuxer demuxer structure + * \param[in] cmd code of control command to perform + * \param[in,out] arg command argument + * \return demuxer control response code + */ +static int demux_mng_control(demuxer_t * demuxer, int cmd, void * arg) +{ + mng_priv_t * mng_priv = demuxer->priv; + + switch(cmd) { + + // get total movie length + case DEMUXER_CTRL_GET_TIME_LENGTH: + if (mng_priv->header_processed) { + *(double *)arg = (double)mng_priv->total_time_ms / 1000.0; + return DEMUXER_CTRL_OK; + } else { + return DEMUXER_CTRL_DONTKNOW; + } + break; + + // get position in movie + case DEMUXER_CTRL_GET_PERCENT_POS: + if (mng_priv->header_processed && mng_priv->total_time_ms > 0) { + *(int *)arg = (100 * mng_priv->show_cur_time_ms + + mng_priv->total_time_ms / 2) + / mng_priv->total_time_ms; + return DEMUXER_CTRL_OK; + } else { + return DEMUXER_CTRL_DONTKNOW; + } + break; + + default: + return DEMUXER_CTRL_NOTIMPL; + + } // switch (cmd) +} + +const demuxer_desc_t demuxer_desc_mng = { + "MNG demuxer", + "mng", + "MNG", + "Stefan Schuermans <stefan@blinkenarea.org>", + "MNG files, using libmng", + DEMUXER_TYPE_MNG, + 0, // unsafe autodetect (only checking magic at beginning of stream) + demux_mng_check_file, + demux_mng_fill_buffer, + demux_mng_open, + demux_mng_close, + demux_mng_seek, + demux_mng_control +}; diff --git a/demux/demux_mpg.c b/demux/demux_mpg.c new file mode 100644 index 0000000000..44ec85d2db --- /dev/null +++ b/demux/demux_mpg.c @@ -0,0 +1,1248 @@ +/* + * MPG/VOB file parser for DEMUXER v2.5 + * copyright (c) 2001 by A'rpi/ESP-team + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <math.h> + +#include "config.h" +#include "mp_msg.h" +#include "options.h" + +#include "libavutil/attributes.h" +#include "libmpcodecs/dec_audio.h" +#include "stream/stream.h" +#include "demuxer.h" +#include "parse_es.h" +#include "stheader.h" +#include "mp3_hdr.h" + +//#define MAX_PS_PACKETSIZE 2048 +#define MAX_PS_PACKETSIZE (224*1024) + +#define UNKNOWN 0 +#define VIDEO_MPEG1 0x10000001 +#define VIDEO_MPEG2 0x10000002 +#define VIDEO_MPEG4 0x10000004 +#define VIDEO_H264 0x10000005 +#define AUDIO_MP2 0x50 +#define AUDIO_A52 0x2000 +#define AUDIO_LPCM_BE 0x10001 +#define AUDIO_AAC mmioFOURCC('M', 'P', '4', 'A') + +typedef struct mpg_demuxer { + float last_pts; + float first_pts; // first pts found in stream + float first_to_final_pts_len; // difference between final pts and first pts + int has_valid_timestamps; // !=0 iff time stamps look linear + // (not necessarily starting with 0) + unsigned int es_map[0x40]; //es map of stream types (associated to the pes id) from 0xb0 to 0xef + int num_a_streams; + int a_stream_ids[MAX_A_STREAMS]; +} mpg_demuxer_t; + +static int mpeg_pts_error=0; +off_t ps_probe = 0; + +static int parse_psm(demuxer_t *demux, int len) { + unsigned char c, id, type; + unsigned int plen, prog_len, es_map_len; + mpg_demuxer_t *priv = (mpg_demuxer_t *) demux->priv; + + mp_dbg(MSGT_DEMUX,MSGL_V, "PARSE_PSM, len=%d\n", len); + if(! len || len > 1018) + return 0; + + c = stream_read_char(demux->stream); + if(! (c & 0x80)) { + stream_skip(demux->stream, len - 1); //not yet valid, discard + return 0; + } + stream_skip(demux->stream, 1); + prog_len = stream_read_word(demux->stream); //length of program descriptors + stream_skip(demux->stream, prog_len); //.. that we ignore + es_map_len = stream_read_word(demux->stream); //length of elementary streams map + es_map_len = FFMIN(es_map_len, len - prog_len - 8); //sanity check + while(es_map_len > 0) { + type = stream_read_char(demux->stream); + id = stream_read_char(demux->stream); + if(id >= 0xB0 && id <= 0xEF && priv) { + int idoffset = id - 0xB0; + switch(type) { + case 0x1: + priv->es_map[idoffset] = VIDEO_MPEG1; + break; + case 0x2: + priv->es_map[idoffset] = VIDEO_MPEG2; + break; + case 0x3: + case 0x4: + priv->es_map[idoffset] = AUDIO_MP2; + break; + case 0x0f: + case 0x11: + priv->es_map[idoffset] = AUDIO_AAC; + break; + case 0x10: + priv->es_map[idoffset] = VIDEO_MPEG4; + break; + case 0x1b: + priv->es_map[idoffset] = VIDEO_H264; + break; + case 0x81: + priv->es_map[idoffset] = AUDIO_A52; + break; + } + mp_dbg(MSGT_DEMUX,MSGL_V, "PSM ES, id=0x%x, type=%x, stype: %x\n", id, type, priv->es_map[idoffset]); + } + plen = stream_read_word(demux->stream); //length of elementary stream descriptors + plen = FFMIN(plen, es_map_len); //sanity check + stream_skip(demux->stream, plen); //skip descriptors for now + es_map_len -= 4 + plen; + } + stream_skip(demux->stream, 4); //skip crc32 + return 1; +} + +// 500000 is a wild guess +#define TIMESTAMP_PROBE_LEN 500000 + +//MAX_PTS_DIFF_FOR_CONSECUTIVE denotes the maximum difference +//between two pts to consider them consecutive +//1.0 is a wild guess +#define MAX_PTS_DIFF_FOR_CONSECUTIVE 1.0 + +//returns the first pts found within TIME_STAMP_PROBE_LEN bytes after stream_pos in demuxer's stream. +//if no pts is found or an error occurs, -1.0 is returned. +//Packs are freed. +static float read_first_mpeg_pts_at_position(demuxer_t* demuxer, off_t stream_pos) +{ + stream_t *s = demuxer->stream; + mpg_demuxer_t *mpg_d = demuxer->priv; + float pts = -1.0; //the pts to return; + float found_pts1; //the most recently found pts + float found_pts2; //the pts found before found_pts1 + float found_pts3; //the pts found before found_pts2 + int found = 0; + + if(!mpg_d || stream_pos < 0) + return pts; + + found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; + stream_seek(s, stream_pos); + + //We look for pts. + //However, we do not stop at the first found one, as timestamps may reset + //Therefore, we seek until we found three consecutive + //pts within MAX_PTS_DIFF_FOR_CONSECUTIVE. + + while(found<3 && !s->eof + && (fabsf(found_pts2-found_pts1) < MAX_PTS_DIFF_FOR_CONSECUTIVE) + && (fabsf(found_pts3-found_pts2) < MAX_PTS_DIFF_FOR_CONSECUTIVE) + && (stream_tell(s) < stream_pos + TIMESTAMP_PROBE_LEN) + && ds_fill_buffer(demuxer->video)) + { + if(mpg_d->last_pts != found_pts1) + { + if(!found) + found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; //the most recently found pts + else + { + found_pts3 = found_pts2; + found_pts2 = found_pts1; + found_pts1 = mpg_d->last_pts; + } + found++; + } + } + + if(found == 3) pts = found_pts3; + + //clean up from searching of first pts; + demux_flush(demuxer); + + return pts; +} + +/// Open an mpg physical stream +static demuxer_t* demux_mpg_open(demuxer_t* demuxer) { + stream_t *s = demuxer->stream; + mpg_demuxer_t* mpg_d; + + if (!ds_fill_buffer(demuxer->video)) return 0; + mpg_d = calloc(1,sizeof(mpg_demuxer_t)); + if(mpg_d) + { + demuxer->priv = mpg_d; + mpg_d->last_pts = -1.0; + mpg_d->first_pts = -1.0; + + //if seeking is allowed set has_valid_timestamps if appropriate + if(demuxer->seekable + && (demuxer->stream->type == STREAMTYPE_FILE + || demuxer->stream->type == STREAMTYPE_VCD) + && demuxer->movi_start != demuxer-> movi_end + ) + { + //We seek to the beginning of the stream, to somewhere in the + //middle, and to the end of the stream, while remembering the pts + //at each of the three positions. With these pts, we check whether + //or not the pts are "linear enough" to justify seeking by the pts + //of the stream + + //The position where the stream is now + off_t pos = stream_tell(s); + float first_pts = read_first_mpeg_pts_at_position(demuxer, demuxer->movi_start); + if(first_pts != -1.0) + { + float middle_pts = read_first_mpeg_pts_at_position(demuxer, (demuxer->movi_end + demuxer->movi_start)/2); + if(middle_pts != -1.0) + { + float final_pts = read_first_mpeg_pts_at_position(demuxer, demuxer->movi_end - TIMESTAMP_PROBE_LEN); + if(final_pts != -1.0) + { + // found proper first, middle, and final pts. + float proportion = (middle_pts-first_pts==0) ? -1 : (final_pts-middle_pts)/(middle_pts-first_pts); + // if they are linear enough set has_valid_timestamps + if((0.5 < proportion) && (proportion < 2)) + { + mpg_d->first_pts = first_pts; + mpg_d->first_to_final_pts_len = final_pts - first_pts; + mpg_d->has_valid_timestamps = 1; + } + } + } + } + + //Cleaning up from seeking in stream + demuxer->stream->eof=0; + demuxer->video->eof=0; + demuxer->audio->eof=0; + + stream_seek(s,pos); + ds_fill_buffer(demuxer->video); + } // if ( demuxer->seekable ) + } // if ( mpg_d ) + return demuxer; +} + +static void demux_close_mpg(demuxer_t* demuxer) { + mpg_demuxer_t* mpg_d = demuxer->priv; + free(mpg_d); +} + + +static unsigned long long read_mpeg_timestamp(stream_t *s,int c){ + unsigned int d,e; + unsigned long long pts; + d=stream_read_word(s); + e=stream_read_word(s); + if( ((c&1)!=1) || ((d&1)!=1) || ((e&1)!=1) ){ + ++mpeg_pts_error; + return 0; // invalid pts + } + pts=(((uint64_t)((c>>1)&7))<<30)|((d>>1)<<15)|(e>>1); + mp_dbg(MSGT_DEMUX,MSGL_DBG3," pts {%llu}",pts); + return pts; +} + +static void new_audio_stream(demuxer_t *demux, int aid){ + if(!demux->a_streams[aid]){ + mpg_demuxer_t *mpg_d=(mpg_demuxer_t*)demux->priv; + sh_audio_t* sh_a; + new_sh_audio(demux,aid); + sh_a = (sh_audio_t*)demux->a_streams[aid]; + sh_a->needs_parsing = 1; + switch(aid & 0xE0){ // 1110 0000 b (high 3 bit: type low 5: id) + case 0x00: sh_a->format=0x50;break; // mpeg + case 0xA0: sh_a->format=0x10001;break; // dvd pcm + case 0x80: if((aid & 0xF8) == 0x88) sh_a->format=0x2001;//dts + else sh_a->format=0x2000;break; // ac3 + } + //evo files + if((aid & 0xC0) == 0xC0) sh_a->format=0x2000; + else if(aid >= 0x98 && aid <= 0x9f) sh_a->format=0x2001; + if (mpg_d) mpg_d->a_stream_ids[mpg_d->num_a_streams++] = aid; + } + if(demux->audio->id==-1) demux->audio->id=aid; +} + +static int demux_mpg_read_packet(demuxer_t *demux,int id){ + int d av_unused; + int len; + int set_pts=0; // !=0 iff pts has been set to a proper value + unsigned char c=0; + unsigned long long pts=0; + unsigned long long dts av_unused = 0; + int l; + int pes_ext2_subid=-1; + double stream_pts = MP_NOPTS_VALUE; + demux_stream_t *ds=NULL; + demux_packet_t* dp; + mpg_demuxer_t *priv = (mpg_demuxer_t *) demux->priv; + + mp_dbg(MSGT_DEMUX,MSGL_DBG3,"demux_read_packet: %X\n",id); + +// if(id==0x1F0){ +// demux->synced=0; // force resync after 0x1F0 +// return -1; +//} + +// if(id==0x1BA) packet_start_pos=stream_tell(demux->stream); + if((id<0x1BC || id>=0x1F0) && id != 0x1FD) return -1; + if(id==0x1BE) return -1; // padding stream + if(id==0x1BF) return -1; // private2 + + len=stream_read_word(demux->stream); + mp_dbg(MSGT_DEMUX,MSGL_DBG3,"PACKET len=%d",len); +// if(len==62480){ demux->synced=0;return -1;} /* :) */ + if(len==0 || len>MAX_PS_PACKETSIZE){ + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"Invalid PS packet len: %d\n",len); + return -2; // invalid packet !!!!!! + } + + mpeg_pts_error=0; + + if(id==0x1BC) { + parse_psm(demux, len); + return 0; + } + + while(len>0){ // Skip stuFFing bytes + c=stream_read_char(demux->stream); + --len; + if(c!=0xFF)break; + } + if((c>>6)==1){ // Read (skip) STD scale & size value +// printf(" STD_scale=%d",(c>>5)&1); + d=((c&0x1F)<<8)|stream_read_char(demux->stream); + len-=2; +// printf(" STD_size=%d",d); + c=stream_read_char(demux->stream); + } + // Read System-1 stream timestamps: + if((c>>4)==2){ + pts=read_mpeg_timestamp(demux->stream,c); + set_pts=1; + len-=4; + } else + if((c>>4)==3){ + pts=read_mpeg_timestamp(demux->stream,c); + c=stream_read_char(demux->stream); + if((c>>4)!=1) pts=0; //printf("{ERROR4}"); + else set_pts = 1; + dts=read_mpeg_timestamp(demux->stream,c); + len-=4+1+4; + } else + if((c>>6)==2){ + int pts_flags; + int hdrlen; + int parse_ext2; + // System-2 (.VOB) stream: + c=stream_read_char(demux->stream); + pts_flags=c>>6; + parse_ext2 = (id == 0x1FD) && ((c & 0x3F) == 1); + c=stream_read_char(demux->stream); + hdrlen=c; + len-=2; + mp_dbg(MSGT_DEMUX,MSGL_DBG3," hdrlen=%d (len=%d)",hdrlen,len); + if(hdrlen>len){ mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: invalid header length \n"); return -1;} + if(pts_flags==2 && hdrlen>=5){ + c=stream_read_char(demux->stream); + pts=read_mpeg_timestamp(demux->stream,c); + set_pts=1; + len-=5;hdrlen-=5; + } else + if(pts_flags==3 && hdrlen>=10){ + c=stream_read_char(demux->stream); + pts=read_mpeg_timestamp(demux->stream,c); + set_pts=1; + c=stream_read_char(demux->stream); + dts=read_mpeg_timestamp(demux->stream,c); + len-=10;hdrlen-=10; + } + len-=hdrlen; + if(parse_ext2 && hdrlen>=3) { + c=stream_read_char(demux->stream); + hdrlen--; + + if((c & 0x0F) != 0x0F) { + mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: pes_extension_flag2 not set, discarding pes packet\n"); + return -1; + } + if(c & 0x80) { //pes_private_data_flag + if(hdrlen<16) { + mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: not enough pes_private_data bytes: %d < 16, discarding pes packet\n", hdrlen); + return -1; + } + stream_skip(demux->stream, 16); + hdrlen-=16; + } + if(c & 0x40) { //pack_header_field_flag + int l = stream_read_char(demux->stream); + if(l < 0) //couldn't read from the stream? + return -1; + hdrlen--; + if(l < 0 || hdrlen < l) { + mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: not enough pack_header bytes: hdrlen: %d < skip: %d, discarding pes packet\n", + hdrlen, l); + return -1; + } + stream_skip(demux->stream, l); + hdrlen-=l; + } + if(c & 0x20) { //program_packet_sequence_counter_flag + if(hdrlen < 2) { + mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: not enough program_packet bytes: hdrlen: %d, discarding pes packet\n", hdrlen); + return -1; + } + stream_skip(demux->stream, 2); + hdrlen-=2; + } + if(c & 0x10) { + //STD + stream_skip(demux->stream, 2); + hdrlen-=2; + } + c=stream_read_char(demux->stream); //pes_extension2 flag + hdrlen--; + if(c!=0x81) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: unknown pes_extension2 format, len is > 1 \n"); return -1;} + c=stream_read_char(demux->stream); //pes_extension2 payload === substream id + hdrlen--; + if(c<0x55 || c>0x5F) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: unknown vc1 substream_id: 0x%x \n", c); return -1;} + pes_ext2_subid=c; + } + if(hdrlen>0) + stream_skip(demux->stream,hdrlen); // skip header and stuffing bytes + + if(id==0x1FD && pes_ext2_subid!=-1) { + //==== EVO VC1 STREAMS ===// + if(!demux->v_streams[pes_ext2_subid]) new_sh_video(demux,pes_ext2_subid); + if(demux->video->id==-1) demux->video->id=pes_ext2_subid; + if(demux->video->id==pes_ext2_subid){ + ds=demux->video; + if(!ds->sh) ds->sh=demux->v_streams[pes_ext2_subid]; + if(priv && ds->sh) { + sh_video_t *sh = (sh_video_t *)ds->sh; + sh->format = mmioFOURCC('W', 'V', 'C', '1'); + } + } + } + //============== DVD Audio sub-stream ====================== + if(id==0x1BD){ + int aid, rawa52 = 0; + off_t tmppos; + unsigned int tmp; + + tmppos = stream_tell(demux->stream); + tmp = stream_read_word(demux->stream); + stream_seek(demux->stream, tmppos); + /// vdr stores A52 without the 4 header bytes, so we have to check this condition first + if(tmp == 0x0B77) { + aid = 128; + rawa52 = 1; + } + else { + aid=stream_read_char(demux->stream);--len; + if(len<3) return -1; // invalid audio packet + } + + // AID: + // 0x20..0x3F subtitle + // 0x80..0x87 and 0xC0..0xCF AC3 audio + // 0x88..0x8F and 0x98..0x9F DTS audio + // 0xA0..0xBF PCM audio + + if((aid & 0xE0) == 0x20){ + // subtitle: + aid&=0x1F; + + if(!demux->s_streams[aid]){ + sh_sub_t *sh = new_sh_sub(demux, aid); + if (sh) sh->type = 'v'; + mp_msg(MSGT_DEMUX,MSGL_V,"==> Found subtitle: %d\n",aid); + } + + if(demux->sub->id > -1) + demux->sub->id &= 0x1F; + if(!demux->opts->sub_lang && demux->sub->id == -1) + demux->sub->id = aid; + if(demux->sub->id==aid){ + ds=demux->sub; + } + } else if((aid >= 0x80 && aid <= 0x8F) || (aid >= 0x98 && aid <= 0xAF) || (aid >= 0xC0 && aid <= 0xCF)) { + +// aid=128+(aid&0x7F); + // aid=0x80..0xBF + new_audio_stream(demux, aid); + if(demux->audio->id==aid){ + int type; + ds=demux->audio; + if(!ds->sh) ds->sh=demux->a_streams[aid]; + // READ Packet: Skip additional audio header data: + if(!rawa52) { + c=stream_read_char(demux->stream);//num of frames + type=stream_read_char(demux->stream);//startpos hi + type=(type<<8)|stream_read_char(demux->stream);//startpos lo +// printf("\r[%02X][%04X]",c,type); + len-=3; + } + if((aid&0xE0)==0xA0 && len>=3){ + unsigned char* hdr; + // save audio header as codecdata! + if(!((sh_audio_t*)(ds->sh))->codecdata_len){ + ((sh_audio_t*)(ds->sh))->codecdata=malloc(3); + ((sh_audio_t*)(ds->sh))->codecdata_len=3; + } + hdr=((sh_audio_t*)(ds->sh))->codecdata; + // read LPCM header: + // emphasis[1], mute[1], rvd[1], frame number[5]: + hdr[0]=stream_read_char(demux->stream); +// printf(" [%01X:%02d]",c>>5,c&31); + // quantization[2],freq[2],rvd[1],channels[3] + hdr[1]=stream_read_char(demux->stream); +// printf("[%01X:%01X] ",c>>4,c&15); + // dynamic range control (0x80=off): + hdr[2]=stream_read_char(demux->stream); +// printf("[%02X] ",c); + len-=3; + if(len<=0) mp_msg(MSGT_DEMUX,MSGL_V,"End of packet while searching for PCM header\n"); + } +// printf(" \n"); + } // if(demux->audio->id==aid) + + } else mp_msg(MSGT_DEMUX,MSGL_V,"Unknown 0x1BD substream: 0x%02X \n",aid); + } //if(id==0x1BD) + } else { + if(c!=0x0f){ + mp_msg(MSGT_DEMUX,MSGL_V," {ERROR5,c=%d} \n",c); + return -1; // invalid packet !!!!!! + } + } + if(mpeg_pts_error) mp_msg(MSGT_DEMUX,MSGL_V," {PTS_err:%d} \n",mpeg_pts_error); + mp_dbg(MSGT_DEMUX,MSGL_DBG3," => len=%d\n",len); + +// if(len<=0 || len>MAX_PS_PACKETSIZE) return -1; // Invalid packet size + if(len<=0 || len>MAX_PS_PACKETSIZE){ + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"Invalid PS data len: %d\n",len); + return -1; // invalid packet !!!!!! + } + + if(id>=0x1C0 && id<=0x1DF){ + // mpeg audio + int aid=id-0x1C0; + new_audio_stream(demux, aid); + if(demux->audio->id==aid){ + ds=demux->audio; + if(!ds->sh) ds->sh=demux->a_streams[aid]; + if(priv && ds->sh) { + sh_audio_t *sh = (sh_audio_t *)ds->sh; + if(priv->es_map[id - 0x1B0]) + sh->format = priv->es_map[id - 0x1B0]; + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"ASSIGNED TO STREAM %d CODEC %x\n", id, priv->es_map[id - 0x1B0]); + } + } + } else + if(id>=0x1E0 && id<=0x1EF){ + // mpeg video + int aid=id-0x1E0; + if(!demux->v_streams[aid]) new_sh_video(demux,aid); + if(demux->video->id==-1) demux->video->id=aid; + if(demux->video->id==aid){ + ds=demux->video; + if(!ds->sh) ds->sh=demux->v_streams[aid]; + if(priv && ds->sh) { + sh_video_t *sh = (sh_video_t *)ds->sh; + if(priv->es_map[id - 0x1B0]) { + sh->format = priv->es_map[id - 0x1B0]; + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"ASSIGNED TO STREAM %d CODEC %x\n", id, priv->es_map[id - 0x1B0]); + } + } + } + } + + if(ds){ + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_MPG: Read %d data bytes from packet %04X\n",len,id); +// printf("packet start = 0x%X \n",stream_tell(demux->stream)-packet_start_pos); + + dp=new_demux_packet(len); + if(!dp) { + mp_dbg(MSGT_DEMUX,MSGL_ERR,"DEMUX_MPG ERROR: couldn't create demux_packet(%d bytes)\n",len); + stream_skip(demux->stream,len); + return 0; + } + l = stream_read(demux->stream,dp->buffer,len); + if(l<len) + resize_demux_packet(dp, l); + len = l; + if(set_pts) + dp->pts=pts/90000.0f; + dp->pos=demux->filepos; + /* + workaround: + set dp->stream_pts only when feeding the video stream, or strangely interleaved files + (such as SWIII) will show strange alternations in the stream time, wildly going + back and forth + */ + if(ds == demux->video && stream_control(demux->stream, STREAM_CTRL_GET_CURRENT_TIME,(void *)&stream_pts)!=STREAM_UNSUPPORTED) + dp->stream_pts = stream_pts; + ds_add_packet(ds,dp); + if (demux->priv && set_pts) ((mpg_demuxer_t*)demux->priv)->last_pts = pts/90000.0f; +// if(ds==demux->sub) parse_dvdsub(ds->last->buffer,ds->last->len); + return 1; + } + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_MPG: Skipping %d data bytes from packet %04X\n",len,id); + if(len<=2356) stream_skip(demux->stream,len); + return 0; +} + +static int num_elementary_packets100=0; +static int num_elementary_packets101=0; +static int num_elementary_packets12x=0; +static int num_elementary_packets1B6=0; +static int num_elementary_packetsPES=0; +static int num_mpeg12_startcode=0; +static int num_h264_slice=0; //combined slice +static int num_h264_dpa=0; //DPA Slice +static int num_h264_dpb=0; //DPB Slice +static int num_h264_dpc=0; //DPC Slice +static int num_h264_idr=0; //IDR Slice +static int num_h264_sps=0; +static int num_h264_pps=0; + +static int num_mp3audio_packets=0; + +static void clear_stats(void) +{ + num_elementary_packets100=0; + num_elementary_packets101=0; + num_elementary_packets1B6=0; + num_elementary_packets12x=0; + num_elementary_packetsPES=0; + num_mpeg12_startcode=0; + num_h264_slice=0; //combined slice + num_h264_dpa=0; //DPA Slice + num_h264_dpb=0; //DPB Slice + num_h264_dpc=0; //DPC Slice + num_h264_idr=0; //IDR Slice + num_h264_sps=0; + num_h264_pps=0; + num_mp3audio_packets=0; +} + +//assumes demuxer->synced < 2 +static inline void update_stats(int head) +{ + if(head==0x1B6) ++num_elementary_packets1B6; + else if(head==0x1B3 || head==0x1B8) ++num_mpeg12_startcode; + else if(head==0x100) ++num_elementary_packets100; + else if(head==0x101) ++num_elementary_packets101; + else if(head==0x1BD || (0x1C0<=head && head<=0x1EF)) + num_elementary_packetsPES++; + else if(head>=0x120 && head<=0x12F) ++num_elementary_packets12x; + if(head>=0x100 && head<0x1B0) + { + if((head&~0x60) == 0x101) ++num_h264_slice; + else if((head&~0x60) == 0x102) ++num_h264_dpa; + else if((head&~0x60) == 0x103) ++num_h264_dpb; + else if((head&~0x60) == 0x104) ++num_h264_dpc; + else if((head&~0x60) == 0x105 && head != 0x105) ++num_h264_idr; + else if((head&~0x60) == 0x107 && head != 0x107) ++num_h264_sps; + else if((head&~0x60) == 0x108 && head != 0x108) ++num_h264_pps; + } +} + +static int demux_mpg_probe(demuxer_t *demuxer) { + int pes av_unused = 1; + int tmp; + off_t tmppos; + int file_format = DEMUXER_TYPE_UNKNOWN; + + tmppos=stream_tell(demuxer->stream); + tmp=stream_read_dword(demuxer->stream); + if(tmp==0x1E0 || tmp==0x1C0) { + tmp=stream_read_word(demuxer->stream); + if(tmp>1 && tmp<=2048) pes=0; // demuxer->synced=3; // PES... + } + stream_seek(demuxer->stream,tmppos); + + clear_stats(); + + if(demux_mpg_open(demuxer)) + file_format=DEMUXER_TYPE_MPEG_PS; + else { + mp_msg(MSGT_DEMUX,MSGL_V,"MPEG packet stats: p100: %d p101: %d p1B6: %d p12x: %d sli: %d a: %d b: %d c: %d idr: %d sps: %d pps: %d PES: %d MP3: %d, synced: %d\n", + num_elementary_packets100,num_elementary_packets101, + num_elementary_packets1B6,num_elementary_packets12x, + num_h264_slice, num_h264_dpa, + num_h264_dpb, num_h264_dpc=0, + num_h264_idr, num_h264_sps=0, + num_h264_pps, + num_elementary_packetsPES,num_mp3audio_packets, demuxer->synced); + + //MPEG packet stats: p100: 458 p101: 458 PES: 0 MP3: 1103 (.m2v) + if(num_mp3audio_packets>50 && num_mp3audio_packets>2*num_elementary_packets100 + && abs(num_elementary_packets100-num_elementary_packets101)>2) + return file_format; + + // some hack to get meaningfull error messages to our unhappy users: + if(num_mpeg12_startcode>=2 && num_elementary_packets100>=2 && num_elementary_packets101>=2 && + abs(num_elementary_packets101+8-num_elementary_packets100)<16) { + if(num_elementary_packetsPES>=4 && num_elementary_packetsPES>=num_elementary_packets100-4) { + return file_format; + } + file_format=DEMUXER_TYPE_MPEG_ES; // <-- hack is here :) + } else + // fuzzy mpeg4-es detection. do NOT enable without heavy testing of mpeg formats detection! + if(num_elementary_packets1B6>3 && num_elementary_packets12x>=1 && + num_elementary_packetsPES==0 && num_elementary_packets100<=num_elementary_packets12x && + demuxer->synced<2) { + file_format=DEMUXER_TYPE_MPEG4_ES; + } else + // fuzzy h264-es detection. do NOT enable without heavy testing of mpeg formats detection! + if((num_h264_slice>3 || (num_h264_dpa>3 && num_h264_dpb>3 && num_h264_dpc>3)) && + /* FIXME num_h264_sps>=1 && */ num_h264_pps>=1 && num_h264_idr>=1 && + num_elementary_packets1B6==0 && num_elementary_packetsPES==0 && + demuxer->synced<2) { + file_format=DEMUXER_TYPE_H264_ES; + } else + { + if(demuxer->synced==2) + mp_msg(MSGT_DEMUXER, MSGL_ERR, "MPEG: %s", + mp_gtext("Missing video stream!? Contact the author, it may be a bug :(\n")); + else + mp_tmsg(MSGT_DEMUXER,MSGL_V,"Not MPEG System Stream format... (maybe Transport Stream?)\n"); + } + } + //FIXME this shouldn't be necessary + stream_seek(demuxer->stream,tmppos); + return file_format; +} + +static int demux_mpg_es_fill_buffer(demuxer_t *demux, demux_stream_t *ds){ + // Elementary video stream + if(demux->stream->eof) return 0; + demux->filepos=stream_tell(demux->stream); + ds_read_packet(demux->video,demux->stream,STREAM_BUFFER_SIZE,0,demux->filepos,0); + return 1; +} + +/** + * \brief discard until 0x100 header and return a filled buffer + * \param b buffer-end pointer + * \param pos current pos in stream, negative since b points to end of buffer + * \param s stream to read from + * \return new position, differs from original pos when eof hit and thus + * b was modified to point to the new end of buffer + */ +static int find_end(unsigned char **b, int pos, stream_t *s) { + register int state = 0xffffffff; + unsigned char *buf = *b; + int start = pos; + int read, unused; + // search already read part + while (state != 0x100 && pos) { + state = state << 8 | buf[pos++]; + } + // continue search in stream + while (state != 0x100) { + register int c = stream_read_char(s); + if (c < 0) break; + state = state << 8 | c; + } + // modify previous header (from 0x1bc or 0x1bf to 0x100) + buf[start++] = 0; + // copy remaining buffer part to current pos + memmove(&buf[start], &buf[pos], -pos); + unused = start + -pos; // -unused bytes in buffer + read = stream_read(s, &buf[unused], -unused); + unused += read; + // fix buffer so it ends at pos == 0 (eof case) + *b = &buf[unused]; + start -= unused; + return start; +} + +/** + * This format usually uses an insane bitrate, which makes this function + * performance-critical! + * Be sure to benchmark any changes with different compiler versions. + */ +static int demux_mpg_gxf_fill_buffer(demuxer_t *demux, demux_stream_t *ds) { + demux_packet_t *pack; + int len; + demux->filepos = stream_tell(demux->stream); + pack = new_demux_packet(STREAM_BUFFER_SIZE); + len = stream_read(demux->stream, pack->buffer, STREAM_BUFFER_SIZE); + if (len <= 0) + { + free_demux_packet(pack); + return 0; + } + { + register uint32_t state = (uint32_t)demux->priv; + register int pos = -len; + unsigned char *buf = &pack->buffer[len]; + do { + state = state << 8 | buf[pos]; + if (unlikely((state | 3) == 0x1bf)) + pos = find_end(&buf, pos, demux->stream); + } while (++pos < 0); + demux->priv = (void *)state; + len = buf - pack->buffer; + } + if (len < STREAM_BUFFER_SIZE) + resize_demux_packet(pack, len); + ds_add_packet(ds, pack); + return 1; +} + +static int demux_mpg_fill_buffer(demuxer_t *demux, demux_stream_t *ds) +{ +unsigned int head=0; +int skipped=0; +int max_packs=256; // 512kbyte +int ret=0; + +// System stream +do{ + demux->filepos=stream_tell(demux->stream); + //lame workaround: this is needed to show the progress bar when playing dvdnav:// + //(ths poor guy doesn't know teh length of the stream at startup) + demux->movi_end = demux->stream->end_pos; + head=stream_read_dword(demux->stream); + if((head&0xFFFFFF00)!=0x100){ + // sync... + demux->filepos-=skipped; + while(1){ + int c=stream_read_char(demux->stream); + if(c<0) break; //EOF + head<<=8; + if(head!=0x100){ + head|=c; + if(mp_check_mp3_header(head)) ++num_mp3audio_packets; + ++skipped; //++demux->filepos; + continue; + } + head|=c; + break; + } + demux->filepos+=skipped; + } + if(stream_eof(demux->stream)) break; + // sure: head=0x000001XX + mp_dbg(MSGT_DEMUX,MSGL_DBG4,"*** head=0x%X\n",head); + if(demux->synced==0){ + if(head==0x1BA) demux->synced=1; //else +// if(head==0x1BD || (head>=0x1C0 && head<=0x1EF)) demux->synced=3; // PES? + } else + if(demux->synced==1){ + if(head==0x1BB || head==0x1BD || (head>=0x1C0 && head<=0x1EF)){ + demux->synced=2; + mp_msg(MSGT_DEMUX,MSGL_V,"system stream synced at 0x%"PRIX64" (%"PRId64")!\n",(int64_t)demux->filepos,(int64_t)demux->filepos); + num_elementary_packets100=0; // requires for re-sync! + num_elementary_packets101=0; // requires for re-sync! + } else demux->synced=0; + } // else + if(demux->synced>=2){ + ret=demux_mpg_read_packet(demux,head); + if(!ret) + if(--max_packs==0){ + demux->stream->eof=1; + mp_tmsg(MSGT_DEMUX,MSGL_ERR,"demux: File doesn't contain the selected audio or video stream.\n"); + return 0; + } + if(demux->synced==3) demux->synced=(ret==1)?2:0; // PES detect + } else { + update_stats(head); + if(head>=0x100 && head<0x1B0) + mp_msg(MSGT_DEMUX,MSGL_DBG3,"Opps... elementary video packet found: %03X\n",head); + else if((head>=0x1C0 && head<0x1F0) || head==0x1BD) + mp_msg(MSGT_DEMUX,MSGL_DBG3,"Opps... PES packet found: %03X\n",head); + + if(((num_elementary_packets100>50 && num_elementary_packets101>50) || + (num_elementary_packetsPES>50)) && skipped>4000000){ + mp_msg(MSGT_DEMUX,MSGL_V,"sync_mpeg_ps: seems to be ES/PES stream...\n"); + demux->stream->eof=1; + break; + } + if(num_mp3audio_packets>100 && num_elementary_packets100<10){ + mp_msg(MSGT_DEMUX,MSGL_V,"sync_mpeg_ps: seems to be MP3 stream...\n"); + demux->stream->eof=1; + break; + } + } +} while(ret!=1); + mp_dbg(MSGT_DEMUX,MSGL_DBG2,"demux: %d bad bytes skipped\n",skipped); + if(demux->stream->eof){ + mp_msg(MSGT_DEMUX,MSGL_V,"MPEG Stream reached EOF\n"); + return 0; + } + return 1; +} + +static void demux_seek_mpg(demuxer_t *demuxer, float rel_seek_secs, + float audio_delay, int flags) +{ + demux_stream_t *d_audio=demuxer->audio; + demux_stream_t *d_video=demuxer->video; + sh_audio_t *sh_audio=d_audio->sh; + sh_video_t *sh_video=d_video->sh; + mpg_demuxer_t *mpg_d=(mpg_demuxer_t*)demuxer->priv; + int precision = 1; + float oldpts = 0; + off_t oldpos = demuxer->filepos; + float newpts = 0; + off_t newpos = (flags & SEEK_ABSOLUTE) ? demuxer->movi_start : oldpos; + + if(mpg_d) + oldpts = mpg_d->last_pts; + newpts = (flags & SEEK_ABSOLUTE) ? 0.0 : oldpts; + //================= seek in MPEG ========================== + //calculate the pts to seek to + if(flags & SEEK_FACTOR) { + if (mpg_d && mpg_d->first_to_final_pts_len > 0.0) + newpts += mpg_d->first_to_final_pts_len * rel_seek_secs; + else + newpts += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) * oldpts / oldpos; + } else + newpts += rel_seek_secs; + if (newpts < 0) newpts = 0; + + if(flags&SEEK_FACTOR){ + // float seek 0..1 + newpos+=(demuxer->movi_end-demuxer->movi_start)*rel_seek_secs; + } else { + // time seek (secs) + if (mpg_d && mpg_d->has_valid_timestamps) { + if (mpg_d->first_to_final_pts_len > 0.0) + newpos += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) / mpg_d->first_to_final_pts_len; + else if (oldpts > 0.0) + newpos += rel_seek_secs * (oldpos - demuxer->movi_start) / oldpts; + } else if(!sh_video || !sh_video->i_bps) // unspecified or VBR + newpos+=2324*75*rel_seek_secs; // 174.3 kbyte/sec + else + newpos+=sh_video->i_bps*rel_seek_secs; + } + + while (1) { + if(newpos<demuxer->movi_start){ + if(demuxer->stream->type!=STREAMTYPE_VCD) demuxer->movi_start=0; // for VCD + if(newpos<demuxer->movi_start) newpos=demuxer->movi_start; + } + + stream_seek(demuxer->stream,newpos); + + // re-sync video: + videobuf_code_len=0; // reset ES stream buffer + + ds_fill_buffer(d_video); + if(sh_audio){ + ds_fill_buffer(d_audio); + } + + while(1){ + int i; + if(sh_audio && !d_audio->eof && d_video->pts && d_audio->pts){ + float a_pts=d_audio->pts; + a_pts+=(ds_tell_pts(d_audio)-sh_audio->a_in_buffer_len)/(float)sh_audio->i_bps; + if(d_video->pts>a_pts){ + skip_audio_frame(sh_audio); // sync audio + continue; + } + } + if(!sh_video) break; + i=sync_video_packet(d_video); + if(sh_video->format == mmioFOURCC('W', 'V', 'C', '1')) { + if(i==0x10E || i==0x10F) //entry point or sequence header + break; + } else + if(sh_video->format == 0x10000004) { //mpeg4 + if(i==0x1B6) { //vop (frame) startcode + int pos = videobuf_len; + if(!read_video_packet(d_video)) break; // EOF + if((videobuffer[pos+4] & 0x3F) == 0) break; //I-frame + } + } else if(sh_video->format == 0x10000005){ //h264 + if((i & ~0x60) == 0x105) break; + } else { //default mpeg1/2 + if(i==0x1B3 || i==0x1B8) break; // found it! + } + if(!i || !skip_video_packet(d_video)) break; // EOF? + } + if(!mpg_d) + break; + if (!precision || abs(newpts - mpg_d->last_pts) < 0.5 || (mpg_d->last_pts == oldpts)) break; + if ((newpos - oldpos) * (mpg_d->last_pts - oldpts) < 0) { // invalid timestamps + mpg_d->has_valid_timestamps = 0; + break; + } + precision--; + //prepare another seek because we are off by more than 0.5s + if(mpg_d) { + newpos += (newpts - mpg_d->last_pts) * (newpos - oldpos) / (mpg_d->last_pts - oldpts); + demux_flush(demuxer); + demuxer->stream->eof=0; // clear eof flag + d_video->eof=0; + d_audio->eof=0; + } + } +} + +static int demux_mpg_control(demuxer_t *demuxer, int cmd, void *arg) +{ + mpg_demuxer_t *mpg_d=(mpg_demuxer_t*)demuxer->priv; + + switch(cmd) { + case DEMUXER_CTRL_GET_TIME_LENGTH: + if(stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, arg) != STREAM_UNSUPPORTED) { + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"\r\nDEMUX_MPG_CTRL, (%.3f)\r\n", *((double*)arg)); + return DEMUXER_CTRL_GUESS; + } + if (mpg_d && mpg_d->has_valid_timestamps) { + *((double *)arg)=(double)mpg_d->first_to_final_pts_len; + return DEMUXER_CTRL_OK; + } + return DEMUXER_CTRL_DONTKNOW; + + case DEMUXER_CTRL_GET_PERCENT_POS: + if (mpg_d && mpg_d->has_valid_timestamps && mpg_d->first_to_final_pts_len > 0.0) { + *((int *)arg)=(int)(100 * (mpg_d->last_pts-mpg_d->first_pts) / mpg_d->first_to_final_pts_len); + return DEMUXER_CTRL_OK; + } + return DEMUXER_CTRL_DONTKNOW; + + case DEMUXER_CTRL_SWITCH_AUDIO: + if(! (mpg_d && mpg_d->num_a_streams > 1 && demuxer->audio && demuxer->audio->sh)) + return DEMUXER_CTRL_NOTIMPL; + else { + demux_stream_t *d_audio = demuxer->audio; + sh_audio_t *sh_audio = d_audio->sh; + sh_audio_t *sh_a = sh_audio; + int i; + if(!sh_audio) + return DEMUXER_CTRL_NOTIMPL; + if (*((int*)arg) < 0) + { + for (i = 0; i < mpg_d->num_a_streams; i++) { + if (d_audio->id == mpg_d->a_stream_ids[i]) break; + } + i = (i+1) % mpg_d->num_a_streams; + sh_a = (sh_audio_t*)demuxer->a_streams[mpg_d->a_stream_ids[i]]; + } + else { + for (i = 0; i < mpg_d->num_a_streams; i++) + if (*((int*)arg) == mpg_d->a_stream_ids[i]) break; + if (i < mpg_d->num_a_streams) + sh_a = (sh_audio_t*)demuxer->a_streams[*((int*)arg)]; + } + if (i < mpg_d->num_a_streams && d_audio->id != mpg_d->a_stream_ids[i]) { + d_audio->id = mpg_d->a_stream_ids[i]; + d_audio->sh = sh_a; + ds_free_packs(d_audio); + } + } + *((int*)arg) = demuxer->audio->id; + return DEMUXER_CTRL_OK; + + default: + return DEMUXER_CTRL_NOTIMPL; + } +} + + +static int demux_mpg_pes_probe(demuxer_t *demuxer) { + demuxer->synced = 3; + return (demux_mpg_probe(demuxer) == DEMUXER_TYPE_MPEG_PS) ? DEMUXER_TYPE_MPEG_PES : 0; +} + + +static demuxer_t* demux_mpg_es_open(demuxer_t* demuxer) +{ + sh_video_t *sh_video=NULL; + + demuxer->audio->sh = NULL; // ES streams has no audio channel + demuxer->video->sh = new_sh_video(demuxer,0); // create dummy video stream header, id=0 + sh_video=demuxer->video->sh;sh_video->ds=demuxer->video; + + return demuxer; +} + +static demuxer_t *demux_mpg_gxf_open(demuxer_t *demuxer) { + demuxer->audio->sh = NULL; + demuxer->video->sh = new_sh_video(demuxer,0); + ((sh_video_t *)demuxer->video->sh)->ds = demuxer->video; + demuxer->priv = (void *) 0xffffffff; + return demuxer; +} + +static demuxer_t* demux_mpg_ps_open(demuxer_t* demuxer) +{ + sh_audio_t *sh_audio=NULL; + sh_video_t *sh_video=NULL; + + sh_video=demuxer->video->sh;sh_video->ds=demuxer->video; + + if(demuxer->audio->id!=-2) { + if(!ds_fill_buffer(demuxer->audio)){ + mp_msg(MSGT_DEMUXER, MSGL_INFO, "MPEG: %s", + mp_gtext("No audio stream found -> no sound.\n")); + demuxer->audio->sh=NULL; + } else { + sh_audio=demuxer->audio->sh;sh_audio->ds=demuxer->audio; + } + } + + if(!sh_video->format && ps_probe > 0) { + int head; + off_t pos = stream_tell(demuxer->stream); + + clear_stats(); + do { + head=sync_video_packet(demuxer->video); + if(!head) break; + update_stats(head); + skip_video_packet(demuxer->video); + } while(stream_tell(demuxer->stream) < pos + ps_probe && !demuxer->stream->eof); + + ds_free_packs(demuxer->video); + demuxer->stream->eof=0; + stream_seek(demuxer->stream, pos); + mp_msg(MSGT_DEMUX,MSGL_INFO,"MPEG packet stats: p100: %d p101: %d p1B6: %d p12x: %d sli: %d a: %d b: %d c: %d idr: %d sps: %d pps: %d\n", + num_elementary_packets100, num_elementary_packets101, + num_elementary_packets1B6, num_elementary_packets12x, + num_h264_slice, num_h264_dpa, num_h264_dpb, num_h264_dpc, + num_h264_idr, num_h264_sps, num_h264_pps); + + if(num_elementary_packets1B6>3 && num_elementary_packets12x>=1 && + num_elementary_packets100<=num_elementary_packets12x) + sh_video->format = 0x10000004; + else if((num_h264_slice>3 || (num_h264_dpa>3 && num_h264_dpb>3 && num_h264_dpc>3)) && + num_h264_sps>=1 && num_h264_pps>=1 && num_h264_idr>=1 && + num_elementary_packets1B6==0) + sh_video->format = 0x10000005; + else sh_video->format = 0x10000002; + } + + return demuxer; +} + + +const demuxer_desc_t demuxer_desc_mpeg_ps = { + "MPEG PS demuxer", + "mpegps", + "MPEG-PS", + "Arpi?", + "Mpeg", + DEMUXER_TYPE_MPEG_PS, + 0, // unsafe autodetect + demux_mpg_probe, + demux_mpg_fill_buffer, + demux_mpg_ps_open, + demux_close_mpg, + demux_seek_mpg, + demux_mpg_control, +}; + + +const demuxer_desc_t demuxer_desc_mpeg_pes = { + "MPEG PES demuxer", + "mpegpes", + "MPEG-PES", + "Arpi?", + "Mpeg", + DEMUXER_TYPE_MPEG_PES, + 0, // unsafe autodetect + demux_mpg_pes_probe, + demux_mpg_fill_buffer, + demux_mpg_ps_open, + demux_close_mpg, + demux_seek_mpg, + demux_mpg_control, +}; + + +const demuxer_desc_t demuxer_desc_mpeg_gxf = { + "MPEG ES in GXF demuxer", + "mpeggxf", + "MPEG-ES in GXF", + "Reimar Doeffinger", + "Mpeg", + DEMUXER_TYPE_MPEG_GXF, + 0, // hack autodetection + NULL, + demux_mpg_gxf_fill_buffer, + demux_mpg_gxf_open, + NULL, + NULL, + NULL +}; + +const demuxer_desc_t demuxer_desc_mpeg_es = { + "MPEG ES demuxer", + "mpeges", + "MPEG-ES", + "Arpi?", + "Mpeg", + DEMUXER_TYPE_MPEG_ES, + 0, // hack autodetection + NULL, + demux_mpg_es_fill_buffer, + demux_mpg_es_open, + demux_close_mpg, + demux_seek_mpg, + demux_mpg_control, +}; + + +const demuxer_desc_t demuxer_desc_mpeg4_es = { + "MPEG4 ES demuxer", + "mpeg4es", + "MPEG-ES", + "Arpi?", + "Mpeg", + DEMUXER_TYPE_MPEG4_ES, + 0, // hack autodetection + NULL, + demux_mpg_es_fill_buffer, + demux_mpg_es_open, + demux_close_mpg, + demux_seek_mpg, + demux_mpg_control, +}; + + +const demuxer_desc_t demuxer_desc_h264_es = { + "H.264 ES demuxer", + "h264es", + "H264-ES", + "Arpi?", + "Mpeg", + DEMUXER_TYPE_H264_ES, + 0, // hack autodetection + NULL, + demux_mpg_es_fill_buffer, + demux_mpg_es_open, + demux_close_mpg, + demux_seek_mpg, + demux_mpg_control, +}; diff --git a/demux/demux_packet.h b/demux/demux_packet.h new file mode 100644 index 0000000000..d64291f4e0 --- /dev/null +++ b/demux/demux_packet.h @@ -0,0 +1,40 @@ +/* + * This file is part of mplayer2. + * + * mplayer2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mplayer2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_DEMUX_PACKET_H +#define MPLAYER_DEMUX_PACKET_H + +#include <stdbool.h> +#include <sys/types.h> + +// Holds one packet/frame/whatever +typedef struct demux_packet { + int len; + double pts; + double duration; + double stream_pts; + off_t pos; // position in index (AVI) or file (MPG) + unsigned char *buffer; + bool keyframe; + int refcount; // counter for the master packet, if 0, buffer can be free()d + struct demux_packet *master; //in clones, pointer to the master packet + struct demux_packet *next; + struct AVPacket *avpacket; // original libavformat packet (demux_lavf) +} demux_packet_t; + +#endif /* MPLAYER_DEMUX_PACKET_H */ diff --git a/demux/demux_rawaudio.c b/demux/demux_rawaudio.c new file mode 100644 index 0000000000..2a8fea05f0 --- /dev/null +++ b/demux/demux_rawaudio.c @@ -0,0 +1,127 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#include "m_option.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "libaf/format.h" + + +static int channels = 2; +static int samplerate = 44100; +static int format = AF_FORMAT_S16_NE; + +const m_option_t demux_rawaudio_opts[] = { + { "channels", &channels, CONF_TYPE_INT,CONF_RANGE,1,8, NULL }, + { "rate", &samplerate, CONF_TYPE_INT,CONF_RANGE,1000,8*48000, NULL }, + { "format", &format, CONF_TYPE_AFMT, 0, 0, 0, NULL }, + {NULL, NULL, 0, 0, 0, 0, NULL} +}; + + +static demuxer_t* demux_rawaudio_open(demuxer_t* demuxer) { + sh_audio_t* sh_audio; + WAVEFORMATEX* w; + + if ((format & AF_FORMAT_SPECIAL_MASK) != 0) + return NULL; + + sh_audio = new_sh_audio(demuxer,0); + sh_audio->wf = w = malloc(sizeof(*w)); + // Not a WAVEFORMATEX format; just abuse it to pass the internal mplayer + // format to ad_pcm.c + w->wFormatTag = format; + sh_audio->format = MKTAG('M', 'P', 'a', 'f'); + w->nChannels = sh_audio->channels = channels; + w->nSamplesPerSec = sh_audio->samplerate = samplerate; + sh_audio->samplesize = (af_fmt2bits(format) + 7) / 8; + w->nAvgBytesPerSec = samplerate * sh_audio->samplesize * channels; + w->nBlockAlign = channels * sh_audio->samplesize; + w->wBitsPerSample = 8 * sh_audio->samplesize; + w->cbSize = 0; + + demuxer->movi_start = demuxer->stream->start_pos; + demuxer->movi_end = demuxer->stream->end_pos; + + demuxer->audio->id = 0; + demuxer->audio->sh = sh_audio; + sh_audio->ds = demuxer->audio; + sh_audio->needs_parsing = 1; + + return demuxer; +} + +static int demux_rawaudio_fill_buffer(demuxer_t* demuxer, demux_stream_t *ds) { + sh_audio_t* sh_audio = demuxer->audio->sh; + int l = sh_audio->wf->nAvgBytesPerSec; + off_t spos = stream_tell(demuxer->stream); + demux_packet_t* dp; + + if(demuxer->stream->eof) + return 0; + + dp = new_demux_packet(l); + dp->pts = (spos - demuxer->movi_start) / (float)(sh_audio->wf->nAvgBytesPerSec); + dp->pos = (spos - demuxer->movi_start); + + l = stream_read(demuxer->stream,dp->buffer,l); + resize_demux_packet(dp, l); + ds_add_packet(ds,dp); + + return 1; +} + +static void demux_rawaudio_seek(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags){ + stream_t* s = demuxer->stream; + sh_audio_t* sh_audio = demuxer->audio->sh; + off_t base,pos; + + base = (flags & SEEK_ABSOLUTE) ? demuxer->movi_start : stream_tell(s); + if(flags & SEEK_FACTOR) + pos = base + ((demuxer->movi_end - demuxer->movi_start)*rel_seek_secs); + else + pos = base + (rel_seek_secs*sh_audio->i_bps); + + pos -= (pos % (sh_audio->channels * sh_audio->samplesize) ); + stream_seek(s,pos); +// printf("demux_rawaudio: streamtell=%d\n",(int)stream_tell(demuxer->stream)); +} + +const demuxer_desc_t demuxer_desc_rawaudio = { + "Raw audio demuxer", + "rawaudio", + "rawaudio", + "?", + "", + DEMUXER_TYPE_RAWAUDIO, + 0, // no autodetect + NULL, + demux_rawaudio_fill_buffer, + demux_rawaudio_open, + NULL, + demux_rawaudio_seek, +}; diff --git a/demux/demux_rawvideo.c b/demux/demux_rawvideo.c new file mode 100644 index 0000000000..e84caafd01 --- /dev/null +++ b/demux/demux_rawvideo.c @@ -0,0 +1,172 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#include "m_option.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" + +#include "libmpcodecs/img_format.h" + +static int format = IMGFMT_I420; +static int size_id = 0; +static int width = 0; +static int height = 0; +static float fps = 25; +static int imgsize=0; + +const m_option_t demux_rawvideo_opts[] = { + // size: + { "w", &width, CONF_TYPE_INT,CONF_RANGE,1,8192, NULL }, + { "h", &height, CONF_TYPE_INT,CONF_RANGE,1,8192, NULL }, + { "sqcif", &size_id, CONF_TYPE_FLAG,0,0,1, NULL }, + { "qcif", &size_id, CONF_TYPE_FLAG,0,0,2, NULL }, + { "cif", &size_id, CONF_TYPE_FLAG,0,0,3, NULL }, + { "4cif", &size_id, CONF_TYPE_FLAG,0,0,4, NULL }, + { "pal", &size_id, CONF_TYPE_FLAG,0,0,5, NULL }, + { "ntsc", &size_id, CONF_TYPE_FLAG,0,0,6, NULL }, + { "16cif", &size_id, CONF_TYPE_FLAG,0,0,7, NULL }, + { "sif", &size_id, CONF_TYPE_FLAG,0,0,8, NULL }, + // format: + { "format", &format, CONF_TYPE_IMGFMT, 0, 0 , 0, NULL }, + // below options are obsolete + { "i420", &format, CONF_TYPE_FLAG, 0, 0 , IMGFMT_I420, NULL }, + { "yv12", &format, CONF_TYPE_FLAG, 0, 0 , IMGFMT_YV12, NULL }, + { "nv12", &format, CONF_TYPE_FLAG, 0, 0 , IMGFMT_NV12, NULL }, + { "hm12", &format, CONF_TYPE_FLAG, 0, 0 , IMGFMT_HM12, NULL }, + { "yuy2", &format, CONF_TYPE_FLAG, 0, 0 , IMGFMT_YUY2, NULL }, + { "uyvy", &format, CONF_TYPE_FLAG, 0, 0 , IMGFMT_UYVY, NULL }, + { "y8", &format, CONF_TYPE_FLAG, 0, 0 , IMGFMT_Y8, NULL }, + // misc: + { "fps", &fps, CONF_TYPE_FLOAT,CONF_RANGE,0.001,1000, NULL }, + { "size", &imgsize, CONF_TYPE_INT, CONF_RANGE, 1 , 8192*8192*4, NULL }, + + {NULL, NULL, 0, 0, 0, 0, NULL} +}; + + +static demuxer_t* demux_rawvideo_open(demuxer_t* demuxer) { + sh_video_t* sh_video; + + switch(size_id){ + case 1: width=128; height=96; break; + case 2: width=176; height=144; break; + case 3: width=352; height=288; break; + case 4: width=704; height=576; break; + case 5: width=720; height=576; break; + case 6: width=720; height=480; break; + case 7: width=1408;height=1152;break; + case 8: width=352; height=240; break; + } + if(!width || !height){ + mp_msg(MSGT_DEMUX,MSGL_ERR,"rawvideo: width or height not specified!\n"); + return 0; + } + + if(!imgsize) + switch(format){ + case IMGFMT_I420: + case IMGFMT_IYUV: + case IMGFMT_NV12: + case IMGFMT_HM12: + case IMGFMT_YV12: imgsize=width*height+2*(width>>1)*(height>>1);break; + case IMGFMT_YUY2: + case IMGFMT_UYVY: imgsize=width*height*2;break; + case IMGFMT_Y800: + case IMGFMT_Y8: imgsize=width*height;break; + default: + if (IMGFMT_IS_RGB(format)) + imgsize = width * height * ((IMGFMT_RGB_DEPTH(format) + 7) >> 3); + else if (IMGFMT_IS_BGR(format)) + imgsize = width * height * ((IMGFMT_BGR_DEPTH(format) + 7) >> 3); + else { + mp_msg(MSGT_DEMUX,MSGL_ERR,"rawvideo: img size not specified and unknown format!\n"); + return 0; + } + } + + sh_video = new_sh_video(demuxer,0); + sh_video->format=format; + sh_video->fps=fps; + sh_video->frametime=1.0/fps; + sh_video->disp_w=width; + sh_video->disp_h=height; + sh_video->i_bps=fps*imgsize; + + demuxer->movi_start = demuxer->stream->start_pos; + demuxer->movi_end = demuxer->stream->end_pos; + + demuxer->video->sh = sh_video; + sh_video->ds = demuxer->video; + + return demuxer; +} + +static int demux_rawvideo_fill_buffer(demuxer_t* demuxer, demux_stream_t *ds) { + sh_video_t* sh = demuxer->video->sh; + off_t pos; + if(demuxer->stream->eof) return 0; + if(ds!=demuxer->video) return 0; + pos = stream_tell(demuxer->stream); + ds_read_packet(ds,demuxer->stream,imgsize,(pos/imgsize)*sh->frametime,pos,0x10); + return 1; +} + +static void demux_rawvideo_seek(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags){ + stream_t* s = demuxer->stream; + sh_video_t* sh_video = demuxer->video->sh; + off_t pos; + + pos = (flags & SEEK_ABSOLUTE) ? demuxer->movi_start : stream_tell(s); + if(flags & SEEK_FACTOR) + pos += ((demuxer->movi_end - demuxer->movi_start)*rel_seek_secs); + else + pos += (rel_seek_secs*sh_video->i_bps); + if(pos < 0) pos = 0; + if(demuxer->movi_end && pos > demuxer->movi_end) pos = (demuxer->movi_end-imgsize); + pos/=imgsize; + stream_seek(s,pos*imgsize); + //sh_video->timer=pos * sh_video->frametime; + demuxer->video->pts = pos * sh_video->frametime; +// printf("demux_rawvideo: streamtell=%d\n",(int)stream_tell(demuxer->stream)); +} + + +const demuxer_desc_t demuxer_desc_rawvideo = { + "Raw video demuxer", + "rawvideo", + "rawvideo", + "?", + "", + DEMUXER_TYPE_RAWVIDEO, + 0, // no autodetect + NULL, + demux_rawvideo_fill_buffer, + demux_rawvideo_open, + NULL, + demux_rawvideo_seek, + NULL +}; diff --git a/demux/demux_ts.c b/demux/demux_ts.c new file mode 100644 index 0000000000..b59066327d --- /dev/null +++ b/demux/demux_ts.c @@ -0,0 +1,3537 @@ +/* + * Demultiplexer for MPEG2 Transport Streams. + * + * Written by Nico <nsabbi@libero.it> + * Kind feedback is appreciated; 'sucks' and alike is not. + * Originally based on demux_pva.c written by Matteo Giani and FFmpeg (libavformat) sources + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "mp_msg.h" +#include "options.h" + +#include "libmpcodecs/dec_audio.h" +#include "stream/stream.h" +#include "demuxer.h" +#include "parse_es.h" +#include "stheader.h" +#include "ms_hdr.h" +#include "mpeg_hdr.h" +#include "demux_ts.h" + +#define TS_PH_PACKET_SIZE 192 +#define TS_FEC_PACKET_SIZE 204 +#define TS_PACKET_SIZE 188 +#define NB_PID_MAX 8192 + +#define MAX_HEADER_SIZE 6 /* enough for PES header + length */ +#define MAX_CHECK_SIZE 65535 +#define NUM_CONSECUTIVE_TS_PACKETS 32 +#define NUM_CONSECUTIVE_AUDIO_PACKETS 348 +#define MAX_A52_FRAME_SIZE 3840 + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif + +#define TYPE_AUDIO 1 +#define TYPE_VIDEO 2 +#define TYPE_SUB 3 + +int ts_prog; +int ts_keep_broken=0; +off_t ts_probe = 0; +int audio_substream_id = -1; + +typedef enum +{ + UNKNOWN = -1, + VIDEO_MPEG1 = 0x10000001, + VIDEO_MPEG2 = 0x10000002, + VIDEO_MPEG4 = 0x10000004, + VIDEO_H264 = 0x10000005, + VIDEO_AVC = mmioFOURCC('a', 'v', 'c', '1'), + VIDEO_DIRAC = mmioFOURCC('d', 'r', 'a', 'c'), + VIDEO_VC1 = mmioFOURCC('W', 'V', 'C', '1'), + AUDIO_MP2 = 0x50, + AUDIO_A52 = 0x2000, + AUDIO_DTS = 0x2001, + AUDIO_LPCM_BE = 0x10001, + AUDIO_AAC = mmioFOURCC('M', 'P', '4', 'A'), + AUDIO_AAC_LATM = mmioFOURCC('M', 'P', '4', 'L'), + AUDIO_TRUEHD = mmioFOURCC('T', 'R', 'H', 'D'), + AUDIO_S302M = mmioFOURCC('B', 'S', 'S', 'D'), + AUDIO_PCM_BR = mmioFOURCC('B', 'P', 'C', 'M'), + SPU_DVD = 0x3000000, + SPU_DVB = 0x3000001, + SPU_TELETEXT = 0x3000002, + SPU_PGS = 0x3000003, + PES_PRIVATE1 = 0xBD00000, + SL_PES_STREAM = 0xD000000, + SL_SECTION = 0xD100000, + MP4_OD = 0xD200000, +} es_stream_type_t; + +typedef struct { + uint8_t *buffer; + uint16_t buffer_len; +} ts_section_t; + +typedef struct { + int size; + unsigned char *start; + uint16_t payload_size; + es_stream_type_t type, subtype; + double pts, last_pts; + int pid; + char lang[4]; + int last_cc; // last cc code (-1 if first packet) + int is_synced; + ts_section_t section; + uint8_t *extradata; + int extradata_alloc, extradata_len; + struct { + uint8_t au_start, au_end, last_au_end; + } sl; +} ES_stream_t; + +typedef struct { + void *sh; + int id; + int type; +} sh_av_t; + +typedef struct MpegTSContext { + int packet_size; // raw packet size, including FEC if present e.g. 188 bytes + ES_stream_t *pids[NB_PID_MAX]; + sh_av_t streams[NB_PID_MAX]; +} MpegTSContext; + + +typedef struct { + demux_stream_t *ds; + demux_packet_t *pack; + int offset, buffer_size; +} av_fifo_t; + +#define MAX_EXTRADATA_SIZE 64*1024 +typedef struct { + int32_t object_type; //aka codec used + int32_t stream_type; //video, audio etc. + uint8_t buf[MAX_EXTRADATA_SIZE]; + uint16_t buf_size; + uint8_t szm1; +} mp4_decoder_config_t; + +typedef struct { + //flags + uint8_t flags; + uint8_t au_start; + uint8_t au_end; + uint8_t random_accesspoint; + uint8_t random_accesspoint_only; + uint8_t padding; + uint8_t use_ts; + uint8_t idle; + uint8_t duration; + + uint32_t ts_resolution, ocr_resolution; + uint8_t ts_len, ocr_len, au_len, instant_bitrate_len, degr_len, au_seqnum_len, packet_seqnum_len; + uint32_t timescale; + uint16_t au_duration, cts_duration; + uint64_t ocr, dts, cts; +} mp4_sl_config_t; + +typedef struct { + uint16_t id; + uint8_t flags; + mp4_decoder_config_t decoder; + mp4_sl_config_t sl; +} mp4_es_descr_t; + +typedef struct { + uint16_t id; + uint8_t flags; + mp4_es_descr_t *es; + uint16_t es_cnt; +} mp4_od_t; + +typedef struct { + uint8_t skip; + uint8_t table_id; + uint8_t ssi; + uint16_t section_length; + uint16_t ts_id; + uint8_t version_number; + uint8_t curr_next; + uint8_t section_number; + uint8_t last_section_number; + struct pat_progs_t { + uint16_t id; + uint16_t pmt_pid; + } *progs; + uint16_t progs_cnt; + ts_section_t section; +} pat_t; + +typedef struct { + uint16_t progid; + uint8_t skip; + uint8_t table_id; + uint8_t ssi; + uint16_t section_length; + uint8_t version_number; + uint8_t curr_next; + uint8_t section_number; + uint8_t last_section_number; + uint16_t PCR_PID; + uint16_t prog_descr_length; + ts_section_t section; + uint16_t es_cnt; + struct pmt_es_t { + uint16_t pid; + uint32_t type; //it's 8 bit long, but cast to the right type as FOURCC + uint16_t descr_length; + uint8_t format_descriptor[5]; + uint8_t lang[4]; + uint16_t mp4_es_id; + } *es; + mp4_od_t iod, *od; + mp4_es_descr_t *mp4es; + int od_cnt, mp4es_cnt; +} pmt_t; + +typedef struct { + uint64_t size; + float duration; + double first_pts; + double last_pts; +} TS_stream_info; + +typedef struct { + MpegTSContext ts; + int last_pid; + av_fifo_t fifo[3]; //0 for audio, 1 for video, 2 for subs + pat_t pat; + pmt_t *pmt; + uint16_t pmt_cnt; + uint32_t prog; + uint32_t vbitrate; + int keep_broken; + int last_aid; + int last_vid; + int last_sid; + char packet[TS_FEC_PACKET_SIZE]; + TS_stream_info vstr, astr; +} ts_priv_t; + + +typedef struct { + es_stream_type_t type; + ts_section_t section; +} TS_pids_t; + + +static int IS_AUDIO(es_stream_type_t type) +{ + switch (type) { + case AUDIO_MP2: + case AUDIO_A52: + case AUDIO_LPCM_BE: + case AUDIO_PCM_BR: + case AUDIO_AAC: + case AUDIO_AAC_LATM: + case AUDIO_DTS: + case AUDIO_TRUEHD: + case AUDIO_S302M: + return 1; + } + return 0; +} + +static int IS_VIDEO(es_stream_type_t type) +{ + switch (type) { + case VIDEO_MPEG1: + case VIDEO_MPEG2: + case VIDEO_MPEG4: + case VIDEO_H264: + case VIDEO_AVC: + case VIDEO_DIRAC: + case VIDEO_VC1: + return 1; + } + return 0; +} + +static int IS_SUB(es_stream_type_t type) +{ + switch (type) { + case SPU_DVD: + case SPU_DVB: + case SPU_PGS: + case SPU_TELETEXT: + return 1; + } + return 0; +} + +static int ts_parse(demuxer_t *demuxer, ES_stream_t *es, unsigned char *packet, int probe); + +static uint8_t get_packet_size(const unsigned char *buf, int size) +{ + int i; + + if (size < (TS_FEC_PACKET_SIZE * NUM_CONSECUTIVE_TS_PACKETS)) + return 0; + + for(i=0; i<NUM_CONSECUTIVE_TS_PACKETS; i++) + { + if (buf[i * TS_PACKET_SIZE] != 0x47) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "GET_PACKET_SIZE, pos %d, char: %2x\n", i, buf[i * TS_PACKET_SIZE]); + goto try_fec; + } + } + return TS_PACKET_SIZE; + +try_fec: + for(i=0; i<NUM_CONSECUTIVE_TS_PACKETS; i++) + { + if (buf[i * TS_FEC_PACKET_SIZE] != 0x47){ + mp_msg(MSGT_DEMUX, MSGL_DBG2, "GET_PACKET_SIZE, pos %d, char: %2x\n", i, buf[i * TS_PACKET_SIZE]); + goto try_philips; + } + } + return TS_FEC_PACKET_SIZE; + + try_philips: + for(i=0; i<NUM_CONSECUTIVE_TS_PACKETS; i++) + { + if (buf[i * TS_PH_PACKET_SIZE] != 0x47) + return 0; + } + return TS_PH_PACKET_SIZE; +} + +static int parse_avc_sps(uint8_t *buf, int len, int *w, int *h); +static uint8_t *pid_lang_from_pmt(ts_priv_t *priv, int pid); + +static void ts_add_stream(demuxer_t * demuxer, ES_stream_t *es) +{ + int i; + ts_priv_t *priv = (ts_priv_t*) demuxer->priv; + + if(priv->ts.streams[es->pid].sh) + return; + + if((IS_AUDIO(es->type) || IS_AUDIO(es->subtype)) && priv->last_aid+1 < MAX_A_STREAMS) + { + sh_audio_t *sh = new_sh_audio_aid(demuxer, priv->last_aid, es->pid); + if(sh) + { + const char *lang = pid_lang_from_pmt(priv, es->pid); + sh->needs_parsing = 1; + sh->format = IS_AUDIO(es->type) ? es->type : es->subtype; + sh->ds = demuxer->audio; + + priv->ts.streams[es->pid].id = priv->last_aid; + priv->ts.streams[es->pid].sh = sh; + priv->ts.streams[es->pid].type = TYPE_AUDIO; + mp_msg(MSGT_DEMUX, MSGL_V, "\r\nADDED AUDIO PID %d, type: %x stream n. %d\r\n", es->pid, sh->format, priv->last_aid); + if (lang && lang[0]) + mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_AID_%d_LANG=%s\n", es->pid, lang); + priv->last_aid++; + } + + if(es->extradata && es->extradata_len) + { + sh->wf = malloc(sizeof(*sh->wf) + es->extradata_len); + sh->wf->cbSize = es->extradata_len; + memcpy(sh->wf + 1, es->extradata, es->extradata_len); + } + } + + if((IS_VIDEO(es->type) || IS_VIDEO(es->subtype)) && priv->last_vid+1 < MAX_V_STREAMS) + { + sh_video_t *sh = new_sh_video_vid(demuxer, priv->last_vid, es->pid); + if(sh) + { + sh->format = IS_VIDEO(es->type) ? es->type : es->subtype; + sh->ds = demuxer->video; + + priv->ts.streams[es->pid].id = priv->last_vid; + priv->ts.streams[es->pid].sh = sh; + priv->ts.streams[es->pid].type = TYPE_VIDEO; + mp_msg(MSGT_DEMUX, MSGL_V, "\r\nADDED VIDEO PID %d, type: %x stream n. %d\r\n", es->pid, sh->format, priv->last_vid); + priv->last_vid++; + + + if(sh->format == VIDEO_AVC && es->extradata && es->extradata_len) + { + int w = 0, h = 0; + sh->bih = calloc(1, sizeof(*sh->bih) + es->extradata_len); + sh->bih->biSize= sizeof(*sh->bih) + es->extradata_len; + sh->bih->biCompression = sh->format; + memcpy(sh->bih + 1, es->extradata, es->extradata_len); + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "EXTRADATA(%d BYTES): \n", es->extradata_len); + for(i = 0;i < es->extradata_len; i++) + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "%02x ", (int) es->extradata[i]); + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"\n"); + if(parse_avc_sps(es->extradata, es->extradata_len, &w, &h)) + { + sh->bih->biWidth = w; + sh->bih->biHeight = h; + } + } + } + } + + if(IS_SUB(es->type) && priv->last_sid+1 < MAX_S_STREAMS) + { + sh_sub_t *sh = new_sh_sub_sid_lang(demuxer, priv->last_sid, es->pid, pid_lang_from_pmt(priv, es->pid)); + if (sh) { + switch (es->type) { + case SPU_DVB: + sh->type = 'b'; break; + case SPU_DVD: + sh->type = 'v'; break; + case SPU_PGS: + sh->type = 'p'; break; + } + priv->ts.streams[es->pid].id = priv->last_sid; + priv->ts.streams[es->pid].sh = sh; + priv->ts.streams[es->pid].type = TYPE_SUB; + priv->last_sid++; + } + } +} + +static int ts_check_file(demuxer_t * demuxer) +{ + const int buf_size = (TS_FEC_PACKET_SIZE * NUM_CONSECUTIVE_TS_PACKETS); + unsigned char buf[TS_FEC_PACKET_SIZE * NUM_CONSECUTIVE_TS_PACKETS], done = 0, *ptr; + uint32_t _read, i, count = 0, is_ts; + int cc[NB_PID_MAX], last_cc[NB_PID_MAX], pid, cc_ok, c, good, bad; + uint8_t size = 0; + off_t pos = 0; + off_t init_pos; + + mp_msg(MSGT_DEMUX, MSGL_V, "Checking for MPEG-TS...\n"); + + init_pos = stream_tell(demuxer->stream); + is_ts = 0; + while(! done) + { + i = 1; + c = 0; + + while(((c=stream_read_char(demuxer->stream)) != 0x47) + && (c >= 0) + && (i < MAX_CHECK_SIZE) + && ! demuxer->stream->eof + ) i++; + + + if(c != 0x47) + { + mp_msg(MSGT_DEMUX, MSGL_V, "THIS DOESN'T LOOK LIKE AN MPEG-TS FILE!\n"); + is_ts = 0; + done = 1; + continue; + } + + pos = stream_tell(demuxer->stream) - 1; + buf[0] = c; + _read = stream_read(demuxer->stream, &buf[1], buf_size-1); + + if(_read < buf_size-1) + { + mp_msg(MSGT_DEMUX, MSGL_V, "COULDN'T READ ENOUGH DATA, EXITING TS_CHECK\n"); + stream_reset(demuxer->stream); + return 0; + } + + size = get_packet_size(buf, buf_size); + if(size) + { + done = 1; + is_ts = 1; + } + + if(pos - init_pos >= MAX_CHECK_SIZE) + { + done = 1; + is_ts = 0; + } + } + + mp_msg(MSGT_DEMUX, MSGL_V, "TRIED UP TO POSITION %"PRIu64", FOUND %x, packet_size= %d, SEEMS A TS? %d\n", (uint64_t) pos, c, size, is_ts); + stream_seek(demuxer->stream, pos); + + if(! is_ts) + return 0; + + //LET'S CHECK continuity counters + good = bad = 0; + for(count = 0; count < NB_PID_MAX; count++) + { + cc[count] = last_cc[count] = -1; + } + + for(count = 0; count < NUM_CONSECUTIVE_TS_PACKETS; count++) + { + ptr = &(buf[size * count]); + pid = ((ptr[1] & 0x1f) << 8) | ptr[2]; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "BUF: %02x %02x %02x %02x, PID %d, SIZE: %d \n", + ptr[0], ptr[1], ptr[2], ptr[3], pid, size); + + if((pid == 8191) || (pid < 16)) + continue; + + cc[pid] = (ptr[3] & 0xf); + cc_ok = (last_cc[pid] < 0) || ((((last_cc[pid] + 1) & 0x0f) == cc[pid])); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "PID %d, COMPARE CC %d AND LAST_CC %d\n", pid, cc[pid], last_cc[pid]); + if(! cc_ok) + //return 0; + bad++; + else + good++; + + last_cc[pid] = cc[pid]; + } + + mp_msg(MSGT_DEMUX, MSGL_V, "GOOD CC: %d, BAD CC: %d\n", good, bad); + + if(good >= bad) + return size; + else + return 0; +} + + +static int32_t progid_idx_in_pmt(ts_priv_t *priv, uint16_t progid) +{ + int x; + + if(priv->pmt == NULL) + return -1; + + for(x = 0; x < priv->pmt_cnt; x++) + { + if(priv->pmt[x].progid == progid) + return x; + } + + return -1; +} + + +static int32_t progid_for_pid(ts_priv_t *priv, int pid, int32_t req) //finds the first program listing a pid +{ + int i, j; + pmt_t *pmt; + + + if(priv->pmt == NULL) + return -1; + + + for(i=0; i < priv->pmt_cnt; i++) + { + pmt = &(priv->pmt[i]); + + if(pmt->es == NULL) + return -1; + + for(j = 0; j < pmt->es_cnt; j++) + { + if(pmt->es[j].pid == pid) + { + if((req == 0) || (req == pmt->progid)) + return pmt->progid; + } + } + + } + return -1; +} + +static int32_t prog_pcr_pid(ts_priv_t *priv, int progid) +{ + int i; + + if(priv->pmt == NULL) + return -1; + for(i=0; i < priv->pmt_cnt; i++) + { + if(priv->pmt[i].progid == progid) + return priv->pmt[i].PCR_PID; + } + return -1; +} + + +static int pid_match_lang(ts_priv_t *priv, uint16_t pid, char *lang) +{ + uint16_t i, j; + pmt_t *pmt; + + if(priv->pmt == NULL) + return -1; + + for(i=0; i < priv->pmt_cnt; i++) + { + pmt = &(priv->pmt[i]); + + if(pmt->es == NULL) + return -1; + + for(j = 0; j < pmt->es_cnt; j++) + { + if(pmt->es[j].pid != pid) + continue; + + mp_msg(MSGT_DEMUXER, MSGL_V, "CMP LANG %s AND %s, pids: %d %d\n",pmt->es[j].lang, lang, pmt->es[j].pid, pid); + if(strncmp(pmt->es[j].lang, lang, 3) == 0) + { + return 1; + } + } + + } + + return -1; +} + +typedef struct { + int32_t atype, vtype, stype; //types + int32_t apid, vpid, spid; //stream ids + char alang[4]; //languages + uint16_t prog; + off_t probe; +} tsdemux_init_t; + +//second stage: returns the count of A52 syncwords found +static int a52_check(char *buf, int len) +{ + int cnt, frame_length = 0, ok, srate; + + cnt = ok = 0; + if(len < 8) + return 0; + + while(cnt < len - 7) + { + if(buf[cnt] == 0x0B && buf[cnt+1] == 0x77) + { + frame_length = mp_a52_framesize(&buf[cnt], &srate); + if(frame_length>=7 && frame_length<=3840) + { + cnt += frame_length; + ok++; + } + else + cnt++; + } + else + cnt++; + } + + mp_msg(MSGT_DEMUXER, MSGL_V, "A52_CHECK(%d input bytes), found %d frame syncwords of %d bytes length\n", len, ok, frame_length); + return ok; +} + + +static off_t ts_detect_streams(demuxer_t *demuxer, tsdemux_init_t *param) +{ + int video_found = 0, audio_found = 0, i, num_packets = 0, req_apid, req_vpid, req_spid; + int is_audio, is_video, is_sub, has_tables; + int32_t p, chosen_pid = 0; + off_t pos=0, ret = 0, init_pos, end_pos; + ES_stream_t es; + unsigned char tmp[TS_FEC_PACKET_SIZE]; + ts_priv_t *priv = (ts_priv_t*) demuxer->priv; + struct { + char *buf; + int pos; + } pes_priv1[8192], *pptr; + char *tmpbuf; + + priv->last_pid = 8192; //invalid pid + + req_apid = param->apid; + req_vpid = param->vpid; + req_spid = param->spid; + + has_tables = 0; + memset(pes_priv1, 0, sizeof(pes_priv1)); + init_pos = stream_tell(demuxer->stream); + mp_msg(MSGT_DEMUXER, MSGL_V, "PROBING UP TO %"PRIu64", PROG: %d\n", (uint64_t) param->probe, param->prog); + end_pos = init_pos + (param->probe ? param->probe : TS_MAX_PROBE_SIZE); + while(1) + { + pos = stream_tell(demuxer->stream); + if(pos > end_pos || demuxer->stream->eof) + break; + + if(ts_parse(demuxer, &es, tmp, 1)) + { + //Non PES-aligned A52 audio may escape detection if PMT is not present; + //in this case we try to find at least 3 A52 syncwords + if((es.type == PES_PRIVATE1) && (! audio_found) && req_apid > -2) + { + pptr = &pes_priv1[es.pid]; + if(pptr->pos < 64*1024) + { + tmpbuf = realloc(pptr->buf, pptr->pos + es.size); + if(tmpbuf != NULL) + { + pptr->buf = tmpbuf; + memcpy(&(pptr->buf[ pptr->pos ]), es.start, es.size); + pptr->pos += es.size; + if(a52_check(pptr->buf, pptr->pos) > 2) + { + param->atype = AUDIO_A52; + param->apid = es.pid; + es.type = AUDIO_A52; + } + } + } + } + + is_audio = IS_AUDIO(es.type) || ((es.type==SL_PES_STREAM) && IS_AUDIO(es.subtype)); + is_video = IS_VIDEO(es.type) || ((es.type==SL_PES_STREAM) && IS_VIDEO(es.subtype)); + is_sub = IS_SUB(es.type); + + + if((! is_audio) && (! is_video) && (! is_sub)) + continue; + if(is_audio && req_apid==-2) + continue; + + if(is_video) + { + chosen_pid = (req_vpid == es.pid); + if((! chosen_pid) && (req_vpid > 0)) + continue; + } + else if(is_audio) + { + if(req_apid > 0) + { + chosen_pid = (req_apid == es.pid); + if(! chosen_pid) + continue; + } + else if(param->alang[0] > 0 && es.lang[0] > 0) + { + if(pid_match_lang(priv, es.pid, param->alang) == -1) + continue; + + chosen_pid = 1; + param->apid = req_apid = es.pid; + } + } + else if(is_sub) + { + chosen_pid = (req_spid == es.pid); + if((! chosen_pid) && (req_spid > 0)) + continue; + } + + if(req_apid < 0 && (param->alang[0] == 0) && req_vpid < 0 && req_spid < 0) + chosen_pid = 1; + + if((ret == 0) && chosen_pid) + { + ret = stream_tell(demuxer->stream); + } + + p = progid_for_pid(priv, es.pid, param->prog); + if(p != -1) + { + has_tables++; + if(!param->prog && chosen_pid) + param->prog = p; + } + + if((param->prog > 0) && (param->prog != p)) + { + if(audio_found) + { + if(is_video && (req_vpid == es.pid)) + { + param->vtype = IS_VIDEO(es.type) ? es.type : es.subtype; + param->vpid = es.pid; + video_found = 1; + break; + } + } + + if(video_found) + { + if(is_audio && (req_apid == es.pid)) + { + param->atype = IS_AUDIO(es.type) ? es.type : es.subtype; + param->apid = es.pid; + audio_found = 1; + break; + } + } + + + continue; + } + + + mp_msg(MSGT_DEMUXER, MSGL_DBG2, "TYPE: %x, PID: %d, PROG FOUND: %d\n", es.type, es.pid, param->prog); + + + if(is_video) + { + if((req_vpid == -1) || (req_vpid == es.pid)) + { + param->vtype = IS_VIDEO(es.type) ? es.type : es.subtype; + param->vpid = es.pid; + video_found = 1; + } + } + + + if(((req_vpid == -2) || (num_packets >= NUM_CONSECUTIVE_AUDIO_PACKETS)) && audio_found && !param->probe) + { + //novideo or we have at least 348 audio packets (64 KB) without video (TS with audio only) + param->vtype = 0; + break; + } + + if(is_sub) + { + if((req_spid == -1) || (req_spid == es.pid)) + { + param->stype = es.type; + param->spid = es.pid; + } + } + + if(is_audio) + { + if((req_apid == -1) || (req_apid == es.pid)) + { + param->atype = IS_AUDIO(es.type) ? es.type : es.subtype; + param->apid = es.pid; + audio_found = 1; + } + } + + if(audio_found && (param->apid == es.pid) && (! video_found)) + num_packets++; + + if((has_tables==0) && (video_found && audio_found) && (pos >= 1000000)) + break; + } + } + + for(i=0; i<8192; i++) + { + if(pes_priv1[i].buf != NULL) + { + free(pes_priv1[i].buf); + pes_priv1[i].buf = NULL; + pes_priv1[i].pos = 0; + } + } + + if(video_found) + { + if(param->vtype == VIDEO_MPEG1) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO MPEG1(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_MPEG2) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO MPEG2(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_MPEG4) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO MPEG4(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_H264) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO H264(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_VC1) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO VC1(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_AVC) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO AVC(NAL-H264, pid=%d) ", param->vpid); + } + else + { + param->vtype = UNKNOWN; + //WE DIDN'T MATCH ANY VIDEO STREAM + mp_msg(MSGT_DEMUXER, MSGL_INFO, "NO VIDEO! "); + } + + if(param->atype == AUDIO_MP2) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO MPA(pid=%d)", param->apid); + else if(param->atype == AUDIO_A52) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO A52(pid=%d)", param->apid); + else if(param->atype == AUDIO_DTS) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO DTS(pid=%d)", param->apid); + else if(param->atype == AUDIO_LPCM_BE) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO LPCM(pid=%d)", param->apid); + else if(param->atype == AUDIO_PCM_BR) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO PCMBR(pid=%d)", param->apid); + else if(param->atype == AUDIO_AAC) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO AAC(pid=%d)", param->apid); + else if(param->atype == AUDIO_AAC_LATM) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO AAC LATM(pid=%d)", param->apid); + else if(param->atype == AUDIO_TRUEHD) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO TRUEHD(pid=%d)", param->apid); + else if(param->atype == AUDIO_S302M) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO S302M(pid=%d)", param->apid); + else + { + audio_found = 0; + param->atype = UNKNOWN; + //WE DIDN'T MATCH ANY AUDIO STREAM, SO WE FORCE THE DEMUXER TO IGNORE AUDIO + mp_msg(MSGT_DEMUXER, MSGL_INFO, "NO AUDIO! (try increasing -tsprobe)"); + } + + if(IS_SUB(param->stype)) + mp_msg(MSGT_DEMUXER, MSGL_INFO, " SUB %s(pid=%d) ", (param->stype==SPU_DVD ? "DVD" : param->stype==SPU_DVB ? "DVB" : "Teletext"), param->spid); + else + { + param->stype = UNKNOWN; + mp_msg(MSGT_DEMUXER, MSGL_INFO, " NO SUBS (yet)! "); + } + + if(video_found || audio_found) + { + if(!param->prog) + { + p = progid_for_pid(priv, video_found ? param->vpid : param->apid, 0); + if(p != -1) + param->prog = p; + } + + if(demuxer->stream->eof && (ret == 0)) + ret = init_pos; + mp_msg(MSGT_DEMUXER, MSGL_INFO, " PROGRAM N. %d\n", param->prog); + } + else + mp_msg(MSGT_DEMUXER, MSGL_INFO, "\n"); + + + for(i=0; i<NB_PID_MAX; i++) + { + if(priv->ts.pids[i] != NULL) + { + priv->ts.pids[i]->payload_size = 0; + priv->ts.pids[i]->pts = priv->ts.pids[i]->last_pts = 0; + priv->ts.pids[i]->last_cc = -1; + priv->ts.pids[i]->is_synced = 0; + } + } + + return ret; +} + +static int parse_avc_sps(uint8_t *buf, int len, int *w, int *h) +{ + int sps, sps_len; + unsigned char *ptr; + mp_mpeg_header_t picture; + if(len < 6) + return 0; + sps = buf[5] & 0x1f; + if(!sps) + return 0; + sps_len = (buf[6] << 8) | buf[7]; + if(!sps_len || (sps_len > len - 8)) + return 0; + ptr = &(buf[8]); + picture.display_picture_width = picture.display_picture_height = 0; + h264_parse_sps(&picture, ptr, len - 8); + if(!picture.display_picture_width || !picture.display_picture_height) + return 0; + *w = picture.display_picture_width; + *h = picture.display_picture_height; + return 1; +} + +static demuxer_t *demux_open_ts(demuxer_t * demuxer) +{ + int i; + uint8_t packet_size; + sh_video_t *sh_video; + sh_audio_t *sh_audio; + off_t start_pos; + tsdemux_init_t params; + ts_priv_t * priv = demuxer->priv; + + mp_msg(MSGT_DEMUX, MSGL_V, "DEMUX OPEN, AUDIO_ID: %d, VIDEO_ID: %d, SUBTITLE_ID: %d,\n", + demuxer->audio->id, demuxer->video->id, demuxer->sub->id); + + + demuxer->type= DEMUXER_TYPE_MPEG_TS; + + + stream_reset(demuxer->stream); + + packet_size = ts_check_file(demuxer); + if(!packet_size) + return NULL; + + priv = calloc(1, sizeof(ts_priv_t)); + if(priv == NULL) + { + mp_msg(MSGT_DEMUX, MSGL_FATAL, "DEMUX_OPEN_TS, couldn't allocate enough memory for ts->priv, exit\n"); + return NULL; + } + + for(i=0; i < NB_PID_MAX; i++) + { + priv->ts.pids[i] = NULL; + priv->ts.streams[i].id = -3; + } + priv->pat.progs = NULL; + priv->pat.progs_cnt = 0; + priv->pat.section.buffer = NULL; + priv->pat.section.buffer_len = 0; + + priv->pmt = NULL; + priv->pmt_cnt = 0; + + priv->keep_broken = ts_keep_broken; + priv->ts.packet_size = packet_size; + + + demuxer->priv = priv; + if(demuxer->stream->type != STREAMTYPE_FILE) + demuxer->seekable = 1; + else + demuxer->seekable = 1; + + + params.atype = params.vtype = params.stype = UNKNOWN; + params.apid = demuxer->audio->id; + params.vpid = demuxer->video->id; + params.spid = demuxer->sub->id; + params.prog = ts_prog; + params.probe = ts_probe; + + if(demuxer->opts->audio_lang != NULL) + { + strncpy(params.alang, demuxer->opts->audio_lang[0], 3); + params.alang[3] = 0; + } + else + memset(params.alang, 0, 4); + + start_pos = ts_detect_streams(demuxer, ¶ms); + + demuxer->sub->id = params.spid; + priv->prog = params.prog; + + if(params.vtype != UNKNOWN) + { + ts_add_stream(demuxer, priv->ts.pids[params.vpid]); + sh_video = priv->ts.streams[params.vpid].sh; + demuxer->video->id = priv->ts.streams[params.vpid].id; + sh_video->ds = demuxer->video; + sh_video->format = params.vtype; + demuxer->video->sh = sh_video; + } + + if(params.atype != UNKNOWN) + { + ES_stream_t *es = priv->ts.pids[params.apid]; + + if(!IS_AUDIO(es->type) && !IS_AUDIO(es->subtype) && IS_AUDIO(params.atype)) es->subtype = params.atype; + ts_add_stream(demuxer, priv->ts.pids[params.apid]); + sh_audio = priv->ts.streams[params.apid].sh; + demuxer->audio->id = priv->ts.streams[params.apid].id; + sh_audio->ds = demuxer->audio; + sh_audio->format = params.atype; + demuxer->audio->sh = sh_audio; + } + + + mp_msg(MSGT_DEMUXER,MSGL_V, "Opened TS demuxer, audio: %x(pid %d), video: %x(pid %d)...POS=%"PRIu64", PROBE=%"PRIu64"\n", params.atype, demuxer->audio->id, params.vtype, demuxer->video->id, (uint64_t) start_pos, ts_probe); + + + start_pos = start_pos <= priv->ts.packet_size ? + demuxer->stream->start_pos : + start_pos - priv->ts.packet_size; + demuxer->movi_start = start_pos; + demuxer->reference_clock = MP_NOPTS_VALUE; + stream_reset(demuxer->stream); + stream_seek(demuxer->stream, start_pos); //IF IT'S FROM A PIPE IT WILL FAIL, BUT WHO CARES? + + + priv->last_pid = 8192; //invalid pid + + for(i = 0; i < 3; i++) + { + priv->fifo[i].pack = NULL; + priv->fifo[i].offset = 0; + } + priv->fifo[0].ds = demuxer->audio; + priv->fifo[1].ds = demuxer->video; + priv->fifo[2].ds = demuxer->sub; + + priv->fifo[0].buffer_size = 1536; + priv->fifo[1].buffer_size = 32767; + priv->fifo[2].buffer_size = 32767; + + priv->pat.section.buffer_len = 0; + for(i = 0; i < priv->pmt_cnt; i++) + priv->pmt[i].section.buffer_len = 0; + + demuxer->filepos = stream_tell(demuxer->stream); + return demuxer; +} + +static void demux_close_ts(demuxer_t * demuxer) +{ + uint16_t i; + ts_priv_t *priv = (ts_priv_t*) demuxer->priv; + + if(priv) + { + free(priv->pat.section.buffer); + free(priv->pat.progs); + + if(priv->pmt) + { + for(i = 0; i < priv->pmt_cnt; i++) + { + free(priv->pmt[i].section.buffer); + free(priv->pmt[i].es); + } + free(priv->pmt); + } + for (i = 0; i < NB_PID_MAX; i++) + { + free(priv->ts.pids[i]); + priv->ts.pids[i] = NULL; + } + for (i = 0; i < 3; i++) + { + if (priv->fifo[i].pack) + free_demux_packet(priv->fifo[i].pack); + priv->fifo[i].pack = NULL; + } + free(priv); + } + demuxer->priv=NULL; +} + + +#define getbits mp_getbits + +static int mp4_parse_sl_packet(pmt_t *pmt, uint8_t *buf, uint16_t packet_len, int pid, ES_stream_t *pes_es) +{ + int i, n, m, mp4_es_id = -1; + uint64_t v = 0; + uint32_t pl_size = 0; + int deg_flag = 0; + mp4_es_descr_t *es = NULL; + mp4_sl_config_t *sl = NULL; + uint8_t au_start = 0, au_end = 0, rap_flag = 0, ocr_flag = 0, padding = 0, padding_bits = 0, idle = 0; + + pes_es->is_synced = 0; + mp_msg(MSGT_DEMUXER,MSGL_V, "mp4_parse_sl_packet, pid: %d, pmt: %pm, packet_len: %d\n", pid, pmt, packet_len); + if(! pmt || !packet_len) + return 0; + + for(i = 0; i < pmt->es_cnt; i++) + { + if(pmt->es[i].pid == pid) + mp4_es_id = pmt->es[i].mp4_es_id; + } + if(mp4_es_id < 0) + return -1; + + for(i = 0; i < pmt->mp4es_cnt; i++) + { + if(pmt->mp4es[i].id == mp4_es_id) + es = &(pmt->mp4es[i]); + } + if(! es) + return -1; + + pes_es->subtype = es->decoder.object_type; + + sl = &(es->sl); + if(!sl) + return -1; + + //now es is the complete es_descriptor of out mp4 ES stream + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "ID: %d, FLAGS: 0x%x, subtype: %x\n", es->id, sl->flags, pes_es->subtype); + + n = 0; + if(sl->au_start) + pes_es->sl.au_start = au_start = getbits(buf, n++, 1); + else + pes_es->sl.au_start = (pes_es->sl.last_au_end ? 1 : 0); + if(sl->au_end) + pes_es->sl.au_end = au_end = getbits(buf, n++, 1); + + if(!sl->au_start && !sl->au_end) + { + pes_es->sl.au_start = pes_es->sl.au_end = au_start = au_end = 1; + } + pes_es->sl.last_au_end = pes_es->sl.au_end; + + + if(sl->ocr_len > 0) + ocr_flag = getbits(buf, n++, 1); + if(sl->idle) + idle = getbits(buf, n++, 1); + if(sl->padding) + padding = getbits(buf, n++, 1); + if(padding) + { + padding_bits = getbits(buf, n, 3); + n += 3; + } + + if(idle || (padding && !padding_bits)) + { + pes_es->payload_size = 0; + return -1; + } + + //(! idle && (!padding || padding_bits != 0)) is true + n += sl->packet_seqnum_len; + if(sl->degr_len) + deg_flag = getbits(buf, n++, 1); + if(deg_flag) + n += sl->degr_len; + + if(ocr_flag) + { + n += sl->ocr_len; + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "OCR: %d bits\n", sl->ocr_len); + } + + if(packet_len * 8 <= n) + return -1; + + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "\nAU_START: %d, AU_END: %d\n", au_start, au_end); + if(au_start) + { + int dts_flag = 0, cts_flag = 0, ib_flag = 0; + + if(sl->random_accesspoint) + rap_flag = getbits(buf, n++, 1); + + //check commented because it seems it's rarely used, and we need this flag set in case of au_start + //the decoder will eventually discard the payload if it can't decode it + //if(rap_flag || sl->random_accesspoint_only) + pes_es->is_synced = 1; + + n += sl->au_seqnum_len; + if(packet_len * 8 <= n+8) + return -1; + if(sl->use_ts) + { + dts_flag = getbits(buf, n++, 1); + cts_flag = getbits(buf, n++, 1); + } + if(sl->instant_bitrate_len) + ib_flag = getbits(buf, n++, 1); + if(packet_len * 8 <= n+8) + return -1; + if(dts_flag && (sl->ts_len > 0)) + { + n += sl->ts_len; + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "DTS: %d bits\n", sl->ts_len); + } + if(packet_len * 8 <= n+8) + return -1; + if(cts_flag && (sl->ts_len > 0)) + { + int i = 0, m; + + while(i < sl->ts_len) + { + m = FFMIN(8, sl->ts_len - i); + v |= getbits(buf, n, m); + if(sl->ts_len - i > 8) + v <<= 8; + i += m; + n += m; + if(packet_len * 8 <= n+8) + return -1; + } + + pes_es->pts = (double) v / (double) sl->ts_resolution; + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "CTS: %d bits, value: %"PRIu64"/%d = %.3f\n", sl->ts_len, v, sl->ts_resolution, pes_es->pts); + } + + + i = 0; + pl_size = 0; + while(i < sl->au_len) + { + m = FFMIN(8, sl->au_len - i); + pl_size |= getbits(buf, n, m); + if(sl->au_len - i > 8) + pl_size <<= 8; + i += m; + n += m; + if(packet_len * 8 <= n+8) + return -1; + } + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "AU_LEN: %u (%d bits)\n", pl_size, sl->au_len); + if(ib_flag) + n += sl->instant_bitrate_len; + } + + m = (n+7)/8; + if(0 < pl_size && pl_size < pes_es->payload_size) + pes_es->payload_size = pl_size; + + mp_msg(MSGT_DEMUXER,MSGL_V, "mp4_parse_sl_packet, n=%d, m=%d, size from pes hdr: %u, sl hdr size: %u, RAP FLAGS: %d/%d\n", + n, m, pes_es->payload_size, pl_size, (int) rap_flag, (int) sl->random_accesspoint_only); + + return m; +} + +//this function parses the extension fields in the PES header and returns the substream_id, or -1 in case of errors +static int parse_pes_extension_fields(unsigned char *p, int pkt_len) +{ + int skip; + unsigned char flags; + + if(!(p[7] & 0x1)) //no extension_field + return -1; + skip = 9; + if(p[7] & 0x80) + { + skip += 5; + if(p[7] & 0x40) + skip += 5; + } + if(p[7] & 0x20) //escr_flag + skip += 6; + if(p[7] & 0x10) //es_rate_flag + skip += 3; + if(p[7] & 0x08)//dsm_trick_mode is unsupported, skip + { + skip = 0;//don't let's parse the extension fields + } + if(p[7] & 0x04) //additional_copy_info + skip += 1; + if(p[7] & 0x02) //pes_crc_flag + skip += 2; + if(skip >= pkt_len) //too few bytes + return -1; + flags = p[skip]; + skip++; + if(flags & 0x80) //pes_private_data_flag + skip += 16; + if(skip >= pkt_len) + return -1; + if(flags & 0x40) //pack_header_field_flag + { + unsigned char l = p[skip]; + skip += l; + } + if(flags & 0x20) //program_packet_sequence_counter + skip += 2; + if(flags & 0x10) //p_std + skip += 2; + if(skip >= pkt_len) + return -1; + if(flags & 0x01) //finally the long desired pes_extension2 + { + unsigned char l = p[skip]; //ext2 flag+len + skip++; + if((l == 0x81) && (skip < pkt_len)) + { + int ssid = p[skip]; + mp_msg(MSGT_IDENTIFY, MSGL_V, "SUBSTREAM_ID=%d (0x%02X)\n", ssid, ssid); + return ssid; + } + } + + return -1; +} + +static int pes_parse2(unsigned char *buf, uint16_t packet_len, ES_stream_t *es, int32_t type_from_pmt, pmt_t *pmt, int pid) +{ + unsigned char *p; + uint32_t header_len; + int64_t pts; + uint32_t stream_id; + uint32_t pkt_len, pes_is_aligned; + + //Here we are always at the start of a PES packet + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2(%p, %d): \n", buf, (uint32_t) packet_len); + + if(packet_len == 0 || packet_len > 184) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2, BUFFER LEN IS TOO SMALL OR TOO BIG: %d EXIT\n", packet_len); + return 0; + } + + p = buf; + pkt_len = packet_len; + + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2: HEADER %02x %02x %02x %02x\n", p[0], p[1], p[2], p[3]); + if (p[0] || p[1] || (p[2] != 1)) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2: error HEADER %02x %02x %02x (should be 0x000001) \n", p[0], p[1], p[2]); + return 0 ; + } + + packet_len -= 6; + if(packet_len==0) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2: packet too short: %d, exit\n", packet_len); + return 0; + } + + es->payload_size = (p[4] << 8 | p[5]); + pes_is_aligned = (p[6] & 4); + + stream_id = p[3]; + + + if (p[7] & 0x80) + { /* pts available */ + pts = (int64_t)(p[9] & 0x0E) << 29 ; + pts |= p[10] << 22 ; + pts |= (p[11] & 0xFE) << 14 ; + pts |= p[12] << 7 ; + pts |= (p[13] & 0xFE) >> 1 ; + + es->pts = pts / 90000.0; + } + else + es->pts = 0.0; + + + header_len = p[8]; + + + if (header_len + 9 > pkt_len) //9 are the bytes read up to the header_length field + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "demux_ts: illegal value for PES_header_data_length (0x%02x)\n", header_len); + return 0; + } + + if(stream_id==0xfd) + { + int ssid = parse_pes_extension_fields(p, pkt_len); + if((audio_substream_id!=-1) && (ssid != audio_substream_id)) + return 0; + if(ssid == 0x72 && type_from_pmt != AUDIO_DTS && type_from_pmt != SPU_PGS) + es->type = type_from_pmt = AUDIO_TRUEHD; + } + + p += header_len + 9; + packet_len -= header_len + 3; + + if(es->payload_size) + es->payload_size -= header_len + 3; + + + es->is_synced = 1; //only for SL streams we have to make sure it's really true, see below + if (stream_id == 0xbd) + { + mp_msg(MSGT_DEMUX, MSGL_DBG3, "pes_parse2: audio buf = %02X %02X %02X %02X %02X %02X %02X %02X, 80: %d\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[0] & 0x80); + + + /* + * we check the descriptor tag first because some stations + * do not include any of the A52 header info in their audio tracks + * these "raw" streams may begin with a byte that looks like a stream type. + */ + + + if(type_from_pmt == SPU_PGS) + { + es->start = p; + es->size = packet_len; + es->type = SPU_PGS; + es->payload_size -= packet_len; + return 1; + } + if( + (type_from_pmt == AUDIO_A52) || /* A52 - raw */ + (packet_len >= 2 && p[0] == 0x0B && p[1] == 0x77) /* A52 - syncword */ + ) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "A52 RAW OR SYNCWORD\n"); + es->start = p; + es->size = packet_len; + es->type = AUDIO_A52; + es->payload_size -= packet_len; + + return 1; + } + /* SPU SUBS */ + else if(type_from_pmt == SPU_DVB || + (packet_len >= 2 && (p[0] == 0x20) && pes_is_aligned)) // && p[1] == 0x00)) + { + // offset/length fiddling to make decoding with lavc possible + es->start = p + 2; + es->size = packet_len - 2; + es->type = SPU_DVB; + es->payload_size -= packet_len; + + return 1; + } + else if (pes_is_aligned && packet_len >= 1 && ((p[0] & 0xE0) == 0x20)) //SPU_DVD + { + //DVD SUBS + es->start = p+1; + es->size = packet_len-1; + es->type = SPU_DVD; + es->payload_size -= packet_len; + + return 1; + } + else if (pes_is_aligned && packet_len >= 4 && (p[0] & 0xF8) == 0x80) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "A52 WITH HEADER\n"); + es->start = p+4; + es->size = packet_len - 4; + es->type = AUDIO_A52; + es->payload_size -= packet_len; + + return 1; + } + else if (pes_is_aligned && packet_len >= 1 && ((p[0]&0xf0) == 0xa0)) + { + int pcm_offset; + + for (pcm_offset=0; ++pcm_offset < packet_len-1 ; ) + { + if (p[pcm_offset] == 0x01 && p[pcm_offset+1] == 0x80) + { /* START */ + pcm_offset += 2; + break; + } + } + + es->start = p + pcm_offset; + es->size = packet_len - pcm_offset; + es->type = AUDIO_LPCM_BE; + es->payload_size -= packet_len; + + return 1; + } + else + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "PES_PRIVATE1\n"); + es->start = p; + es->size = packet_len; + es->type = (type_from_pmt == UNKNOWN ? PES_PRIVATE1 : type_from_pmt); + es->payload_size -= packet_len; + + return 1; + } + } + else if((stream_id >= 0xe0 && stream_id <= 0xef) || (stream_id == 0xfd && type_from_pmt != UNKNOWN)) + { + es->start = p; + es->size = packet_len; + if(type_from_pmt != UNKNOWN) + es->type = type_from_pmt; + else + es->type = VIDEO_MPEG2; + if(es->payload_size) + es->payload_size -= packet_len; + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2: M2V size %d\n", es->size); + return 1; + } + else if (stream_id == 0xfa) + { + int l; + + es->is_synced = 0; + if(type_from_pmt != UNKNOWN) //MP4 A/V or SL + { + es->start = p; + es->size = packet_len; + es->type = type_from_pmt; + + if(type_from_pmt == SL_PES_STREAM) + { + //if(pes_is_aligned) + //{ + l = mp4_parse_sl_packet(pmt, p, packet_len, pid, es); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "L=%d, TYPE=%x\n", l, type_from_pmt); + if(l < 0) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2: couldn't parse SL header, passing along full PES payload\n"); + l = 0; + } + //} + + es->start += l; + es->size -= l; + } + + if(es->payload_size) + es->payload_size -= packet_len; + return 1; + } + } + else if ((stream_id & 0xe0) == 0xc0) + { + es->start = p; + es->size = packet_len; + + if(type_from_pmt != UNKNOWN) + es->type = type_from_pmt; + else + es->type = AUDIO_MP2; + + es->payload_size -= packet_len; + + return 1; + } + else if (type_from_pmt != -1) //as a last resort here we trust the PMT, if present + { + es->start = p; + es->size = packet_len; + es->type = type_from_pmt; + es->payload_size -= packet_len; + + return 1; + } + else + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "pes_parse2: unknown packet, id: %x\n", stream_id); + } + + es->is_synced = 0; + return 0; +} + + + + +static int ts_sync(stream_t *stream) +{ + mp_msg(MSGT_DEMUX, MSGL_DBG3, "TS_SYNC \n"); + + while (!stream->eof) + if (stream_read_char(stream) == 0x47) + return 1; + + return 0; +} + + +static void ts_dump_streams(ts_priv_t *priv) +{ + int i; + + for(i = 0; i < 3; i++) + { + if((priv->fifo[i].pack != NULL) && (priv->fifo[i].offset != 0)) + { + resize_demux_packet(priv->fifo[i].pack, priv->fifo[i].offset); + ds_add_packet(priv->fifo[i].ds, priv->fifo[i].pack); + priv->fifo[i].offset = 0; + priv->fifo[i].pack = NULL; + } + } +} + + +static int32_t prog_idx_in_pat(ts_priv_t *priv, uint16_t progid) +{ + int x; + + if(priv->pat.progs == NULL) + return -1; + + for(x = 0; x < priv->pat.progs_cnt; x++) + { + if(priv->pat.progs[x].id == progid) + return x; + } + + return -1; +} + + +static int32_t prog_id_in_pat(ts_priv_t *priv, uint16_t pid) +{ + int x; + + if(priv->pat.progs == NULL) + return -1; + + for(x = 0; x < priv->pat.progs_cnt; x++) + { + if(priv->pat.progs[x].pmt_pid == pid) + return priv->pat.progs[x].id; + } + + return -1; +} + +static int collect_section(ts_section_t *section, int is_start, unsigned char *buff, int size) +{ + uint8_t *ptr; + uint16_t tlen; + int skip, tid; + + mp_msg(MSGT_DEMUX, MSGL_V, "COLLECT_SECTION, start: %d, size: %d, collected: %d\n", is_start, size, section->buffer_len); + if(! is_start && !section->buffer_len) + return 0; + + if(is_start) + { + if(! section->buffer) + { + section->buffer = malloc(4096 + 256); + if(section->buffer == NULL) + return 0; + } + section->buffer_len = 0; + } + + if(size + section->buffer_len > 4096+256) + { + mp_msg(MSGT_DEMUX, MSGL_V, "COLLECT_SECTION, excessive len: %d + %d\n", section->buffer_len, size); + return 0; + } + + memcpy(&(section->buffer[section->buffer_len]), buff, size); + section->buffer_len += size; + + if(section->buffer_len < 3) + return 0; + + skip = section->buffer[0]; + if(skip + 4 > section->buffer_len) + return 0; + + ptr = &(section->buffer[skip + 1]); + tid = ptr[0]; + tlen = ((ptr[1] & 0x0f) << 8) | ptr[2]; + mp_msg(MSGT_DEMUX, MSGL_V, "SKIP: %d+1, TID: %d, TLEN: %d, COLLECTED: %d\n", skip, tid, tlen, section->buffer_len); + if(section->buffer_len < (skip+1+3+tlen)) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "DATA IS NOT ENOUGH, NEXT TIME\n"); + return 0; + } + + return skip+1; +} + +static int parse_pat(ts_priv_t * priv, int is_start, unsigned char *buff, int size) +{ + int skip; + unsigned char *ptr; + unsigned char *base; + int entries, i; + uint16_t progid; + ts_section_t *section; + + section = &(priv->pat.section); + skip = collect_section(section, is_start, buff, size); + if(! skip) + return 0; + + ptr = &(section->buffer[skip]); + //PARSING + priv->pat.table_id = ptr[0]; + if(priv->pat.table_id != 0) + return 0; + priv->pat.ssi = (ptr[1] >> 7) & 0x1; + priv->pat.curr_next = ptr[5] & 0x01; + priv->pat.ts_id = (ptr[3] << 8 ) | ptr[4]; + priv->pat.version_number = (ptr[5] >> 1) & 0x1F; + priv->pat.section_length = ((ptr[1] & 0x03) << 8 ) | ptr[2]; + priv->pat.section_number = ptr[6]; + priv->pat.last_section_number = ptr[7]; + + //check_crc32(0xFFFFFFFFL, ptr, priv->pat.buffer_len - 4, &ptr[priv->pat.buffer_len - 4]); + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_PAT: section_len: %d, section %d/%d\n", priv->pat.section_length, priv->pat.section_number, priv->pat.last_section_number); + + entries = (int) (priv->pat.section_length - 9) / 4; //entries per section + + for(i=0; i < entries; i++) + { + int32_t idx; + base = &ptr[8 + i*4]; + progid = (base[0] << 8) | base[1]; + + if((idx = prog_idx_in_pat(priv, progid)) == -1) + { + priv->pat.progs = realloc_struct(priv->pat.progs, priv->pat.progs_cnt+1, sizeof(struct pat_progs_t)); + if(!priv->pat.progs) + { + int sz = sizeof(struct pat_progs_t) * (priv->pat.progs_cnt+1); + priv->pat.progs_cnt = 0; + mp_msg(MSGT_DEMUX, MSGL_ERR, "PARSE_PAT: COULDN'T REALLOC %d bytes, NEXT\n", sz); + break; + } + idx = priv->pat.progs_cnt; + priv->pat.progs_cnt++; + } + + priv->pat.progs[idx].id = progid; + priv->pat.progs[idx].pmt_pid = ((base[2] & 0x1F) << 8) | base[3]; + mp_msg(MSGT_DEMUX, MSGL_V, "PROG: %d (%d-th of %d), PMT: %d\n", priv->pat.progs[idx].id, i+1, entries, priv->pat.progs[idx].pmt_pid); + mp_msg(MSGT_IDENTIFY, MSGL_V, "PROGRAM_ID=%d (0x%02X), PMT_PID: %d(0x%02X)\n", + progid, progid, priv->pat.progs[idx].pmt_pid, priv->pat.progs[idx].pmt_pid); + } + + return 1; +} + + +static int32_t es_pid_in_pmt(pmt_t * pmt, uint16_t pid) +{ + uint16_t i; + + if(pmt == NULL) + return -1; + + if(pmt->es == NULL) + return -1; + + for(i = 0; i < pmt->es_cnt; i++) + { + if(pmt->es[i].pid == pid) + return (int32_t) i; + } + + return -1; +} + + +static uint16_t get_mp4_desc_len(uint8_t *buf, int *len) +{ + //uint16_t i = 0, size = 0; + int i = 0, j, size = 0; + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "PARSE_MP4_DESC_LEN(%d), bytes: ", *len); + j = FFMIN(*len, 4); + while(i < j) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, " %x ", buf[i]); + size |= (buf[i] & 0x7f); + if(!(buf[i] & 0x80)) + break; + size <<= 7; + i++; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, ", SIZE=%d\n", size); + + *len = i+1; + return size; +} + + +static uint16_t parse_mp4_slconfig_descriptor(uint8_t *buf, int len, void *elem) +{ + int i = 0; + mp4_es_descr_t *es; + mp4_sl_config_t *sl; + + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4_SLCONFIG_DESCRIPTOR(%d)\n", len); + es = (mp4_es_descr_t *) elem; + if(!es) + { + mp_msg(MSGT_DEMUX, MSGL_V, "argh! NULL elem passed, skip\n"); + return len; + } + sl = &(es->sl); + + sl->ts_len = sl->ocr_len = sl->au_len = sl->instant_bitrate_len = sl->degr_len = sl->au_seqnum_len = sl->packet_seqnum_len = 0; + sl->ocr = sl->dts = sl->cts = 0; + + if(buf[0] == 0) + { + i++; + sl->flags = buf[i]; + i++; + sl->ts_resolution = (buf[i] << 24) | (buf[i+1] << 16) | (buf[i+2] << 8) | buf[i+3]; + i += 4; + sl->ocr_resolution = (buf[i] << 24) | (buf[i+1] << 16) | (buf[i+2] << 8) | buf[i+3]; + i += 4; + sl->ts_len = buf[i]; + i++; + sl->ocr_len = buf[i]; + i++; + sl->au_len = buf[i]; + i++; + sl->instant_bitrate_len = buf[i]; + i++; + sl->degr_len = (buf[i] >> 4) & 0x0f; + sl->au_seqnum_len = ((buf[i] & 0x0f) << 1) | ((buf[i+1] >> 7) & 0x01); + i++; + sl->packet_seqnum_len = ((buf[i] >> 2) & 0x1f); + i++; + + } + else if(buf[0] == 1) + { + sl->flags = 0; + sl->ts_resolution = 1000; + sl->ts_len = 32; + i++; + } + else if(buf[0] == 2) + { + sl->flags = 4; + i++; + } + else + { + sl->flags = 0; + i++; + } + + sl->au_start = (sl->flags >> 7) & 0x1; + sl->au_end = (sl->flags >> 6) & 0x1; + sl->random_accesspoint = (sl->flags >> 5) & 0x1; + sl->random_accesspoint_only = (sl->flags >> 4) & 0x1; + sl->padding = (sl->flags >> 3) & 0x1; + sl->use_ts = (sl->flags >> 2) & 0x1; + sl->idle = (sl->flags >> 1) & 0x1; + sl->duration = sl->flags & 0x1; + + if(sl->duration) + { + sl->timescale = (buf[i] << 24) | (buf[i+1] << 16) | (buf[i+2] << 8) | buf[i+3]; + i += 4; + sl->au_duration = (buf[i] << 8) | buf[i+1]; + i += 2; + sl->cts_duration = (buf[i] << 8) | buf[i+1]; + i += 2; + } + else //no support for fixed durations atm + sl->timescale = sl->au_duration = sl->cts_duration = 0; + + mp_msg(MSGT_DEMUX, MSGL_V, "MP4SLCONFIG(len=0x%x), predef: %d, flags: %x, use_ts: %d, tslen: %d, timescale: %d, dts: %"PRIu64", cts: %"PRIu64"\n", + len, buf[0], sl->flags, sl->use_ts, sl->ts_len, sl->timescale, (uint64_t) sl->dts, (uint64_t) sl->cts); + + return len; +} + +static int parse_mp4_descriptors(pmt_t *pmt, uint8_t *buf, int len, void *elem); + +static uint16_t parse_mp4_decoder_config_descriptor(pmt_t *pmt, uint8_t *buf, int len, void *elem) +{ + int i = 0, j; + mp4_es_descr_t *es; + mp4_decoder_config_t *dec; + + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4_DECODER_CONFIG_DESCRIPTOR(%d)\n", len); + es = (mp4_es_descr_t *) elem; + if(!es) + { + mp_msg(MSGT_DEMUX, MSGL_V, "argh! NULL elem passed, skip\n"); + return len; + } + dec = (mp4_decoder_config_t*) &(es->decoder); + + dec->object_type = buf[i]; + dec->stream_type = (buf[i+1]>>2) & 0x3f; + + if(dec->object_type == 1 && dec->stream_type == 1) + { + dec->object_type = MP4_OD; + dec->stream_type = MP4_OD; + } + else if(dec->stream_type == 4) + { + if(dec->object_type == 0x6a) + dec->object_type = VIDEO_MPEG1; + if(dec->object_type >= 0x60 && dec->object_type <= 0x65) + dec->object_type = VIDEO_MPEG2; + else if(dec->object_type == 0x20) + dec->object_type = VIDEO_MPEG4; + else if(dec->object_type == 0x21) + dec->object_type = VIDEO_AVC; + /*else if(dec->object_type == 0x22) + fprintf(stderr, "TYPE 0x22\n");*/ + else dec->object_type = UNKNOWN; + } + else if(dec->stream_type == 5) + { + if(dec->object_type == 0x40) + dec->object_type = AUDIO_AAC; + else if(dec->object_type == 0x6b) + dec->object_type = AUDIO_MP2; + else if(dec->object_type >= 0x66 && dec->object_type <= 0x69) + dec->object_type = AUDIO_MP2; + else + dec->object_type = UNKNOWN; + } + else + dec->object_type = dec->stream_type = UNKNOWN; + + if(dec->object_type != UNKNOWN) + { + //update the type of the current stream + for(j = 0; j < pmt->es_cnt; j++) + { + if(pmt->es[j].mp4_es_id == es->id) + { + pmt->es[j].type = SL_PES_STREAM; + } + } + } + + if(len > 13) + parse_mp4_descriptors(pmt, &buf[13], len-13, dec); + + mp_msg(MSGT_DEMUX, MSGL_V, "MP4DECODER(0x%x), object_type: 0x%x, stream_type: 0x%x\n", len, dec->object_type, dec->stream_type); + + return len; +} + +static uint16_t parse_mp4_decoder_specific_descriptor(uint8_t *buf, int len, void *elem) +{ + int i; + mp4_decoder_config_t *dec; + + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4_DECODER_SPECIFIC_DESCRIPTOR(%d)\n", len); + dec = (mp4_decoder_config_t *) elem; + if(!dec) + { + mp_msg(MSGT_DEMUX, MSGL_V, "argh! NULL elem passed, skip\n"); + return len; + } + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "MP4 SPECIFIC INFO BYTES: \n"); + for(i=0; i<len; i++) + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%02x ", buf[i]); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "\n"); + + if(len > MAX_EXTRADATA_SIZE) + { + mp_msg(MSGT_DEMUX, MSGL_ERR, "DEMUX_TS, EXTRADATA SUSPICIOUSLY BIG: %d, REFUSED\r\n", len); + return len; + } + memcpy(dec->buf, buf, len); + dec->buf_size = len; + + return len; +} + +static uint16_t parse_mp4_es_descriptor(pmt_t *pmt, uint8_t *buf, int len) +{ + int i = 0, j = 0, k, found; + uint8_t flag; + mp4_es_descr_t es, *target_es = NULL; + + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4ES: len=%d\n", len); + memset(&es, 0, sizeof(mp4_es_descr_t)); + while(i < len) + { + es.id = (buf[i] << 8) | buf[i+1]; + mp_msg(MSGT_DEMUX, MSGL_V, "MP4ES_ID: %d\n", es.id); + i += 2; + flag = buf[i]; + i++; + if(flag & 0x80) + i += 2; + if(flag & 0x40) + i += buf[i]+1; + if(flag & 0x20) //OCR, maybe we need it + i += 2; + + j = parse_mp4_descriptors(pmt, &buf[i], len-i, &es); + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4ES, types after parse_mp4_descriptors: 0x%x, 0x%x\n", es.decoder.object_type, es.decoder.stream_type); + if(es.decoder.object_type != UNKNOWN && es.decoder.stream_type != UNKNOWN) + { + found = 0; + //search this ES_ID if we already have it + for(k=0; k < pmt->mp4es_cnt; k++) + { + if(pmt->mp4es[k].id == es.id) + { + target_es = &(pmt->mp4es[k]); + found = 1; + } + } + + if(! found) + { + pmt->mp4es = realloc_struct(pmt->mp4es, pmt->mp4es_cnt+1, sizeof(mp4_es_descr_t)); + if(!pmt->mp4es) + { + pmt->mp4es_cnt = 0; + fprintf(stderr, "CAN'T REALLOC MP4_ES_DESCR\n"); + continue; + } + target_es = &(pmt->mp4es[pmt->mp4es_cnt]); + pmt->mp4es_cnt++; + } + memcpy(target_es, &es, sizeof(mp4_es_descr_t)); + mp_msg(MSGT_DEMUX, MSGL_V, "MP4ES_CNT: %d, ID=%d\n", pmt->mp4es_cnt, target_es->id); + } + + i += j; + } + + return len; +} + +static void parse_mp4_object_descriptor(pmt_t *pmt, uint8_t *buf, int len, void *elem) +{ + int i, j = 0, id; + + i=0; + id = (buf[0] << 2) | ((buf[1] & 0xc0) >> 6); + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4_OBJECT_DESCRIPTOR: len=%d, OD_ID=%d\n", len, id); + if(buf[1] & 0x20) + { + i += buf[2] + 1; //url + mp_msg(MSGT_DEMUX, MSGL_V, "URL\n"); + } + else + { + i = 2; + + while(i < len) + { + j = parse_mp4_descriptors(pmt, &(buf[i]), len-i, elem); + mp_msg(MSGT_DEMUX, MSGL_V, "OBJD, NOW i = %d, j=%d, LEN=%d\n", i, j, len); + i += j; + } + } +} + + +static void parse_mp4_iod(pmt_t *pmt, uint8_t *buf, int len, void *elem) +{ + int i, j = 0; + mp4_od_t *iod = &(pmt->iod); + + iod->id = (buf[0] << 2) | ((buf[1] & 0xc0) >> 6); + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4_IOD: len=%d, IOD_ID=%d\n", len, iod->id); + i = 2; + if(buf[1] & 0x20) + { + i += buf[2] + 1; //url + mp_msg(MSGT_DEMUX, MSGL_V, "URL\n"); + } + else + { + i = 7; + while(i < len) + { + j = parse_mp4_descriptors(pmt, &(buf[i]), len-i, elem); + mp_msg(MSGT_DEMUX, MSGL_V, "IOD, NOW i = %d, j=%d, LEN=%d\n", i, j, len); + i += j; + } + } +} + +static int parse_mp4_descriptors(pmt_t *pmt, uint8_t *buf, int len, void *elem) +{ + int tag, descr_len, i = 0, j = 0; + + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_MP4_DESCRIPTORS, len=%d\n", len); + if(! len) + return len; + + while(i < len) + { + tag = buf[i]; + j = len - i -1; + descr_len = get_mp4_desc_len(&(buf[i+1]), &j); + mp_msg(MSGT_DEMUX, MSGL_V, "TAG=%d (0x%x), DESCR_len=%d, len=%d, j=%d\n", tag, tag, descr_len, len, j); + if(descr_len > len - j+1) + { + mp_msg(MSGT_DEMUX, MSGL_V, "descriptor is too long, exit\n"); + return len; + } + i += j+1; + + switch(tag) + { + case 0x1: + parse_mp4_object_descriptor(pmt, &(buf[i]), descr_len, elem); + break; + case 0x2: + parse_mp4_iod(pmt, &(buf[i]), descr_len, elem); + break; + case 0x3: + parse_mp4_es_descriptor(pmt, &(buf[i]), descr_len); + break; + case 0x4: + parse_mp4_decoder_config_descriptor(pmt, &buf[i], descr_len, elem); + break; + case 0x05: + parse_mp4_decoder_specific_descriptor(&buf[i], descr_len, elem); + break; + case 0x6: + parse_mp4_slconfig_descriptor(&buf[i], descr_len, elem); + break; + default: + mp_msg(MSGT_DEMUX, MSGL_V, "Unsupported mp4 descriptor 0x%x\n", tag); + } + i += descr_len; + } + + return len; +} + +static ES_stream_t *new_pid(ts_priv_t *priv, int pid) +{ + ES_stream_t *tss; + + tss = calloc(sizeof(*tss), 1); + if(! tss) + return NULL; + tss->pid = pid; + tss->last_cc = -1; + tss->type = UNKNOWN; + tss->subtype = UNKNOWN; + tss->is_synced = 0; + tss->extradata = NULL; + tss->extradata_alloc = tss->extradata_len = 0; + priv->ts.pids[pid] = tss; + + return tss; +} + + +static int parse_program_descriptors(pmt_t *pmt, uint8_t *buf, uint16_t len) +{ + uint16_t i = 0, k, olen = len; + + while(len > 0) + { + mp_msg(MSGT_DEMUX, MSGL_V, "PROG DESCR, TAG=%x, LEN=%d(%x)\n", buf[i], buf[i+1], buf[i+1]); + if(buf[i+1] > len-2) + { + mp_msg(MSGT_DEMUX, MSGL_V, "ERROR, descriptor len is too long, skipping\n"); + return olen; + } + + if(buf[i] == 0x1d) + { + if(buf[i+3] == 2) //buggy versions of vlc muxer make this non-standard mess (missing iod_scope) + k = 3; + else + k = 4; //this is standard compliant + parse_mp4_descriptors(pmt, &buf[i+k], (int) buf[i+1]-(k-2), NULL); + } + + len -= 2 + buf[i+1]; + } + + return olen; +} + +static int parse_descriptors(struct pmt_es_t *es, uint8_t *ptr) +{ + int j, descr_len, len; + + j = 0; + len = es->descr_length; + while(len > 2) + { + descr_len = ptr[j+1]; + mp_msg(MSGT_DEMUX, MSGL_V, "...descr id: 0x%x, len=%d\n", ptr[j], descr_len); + if(descr_len > len) + { + mp_msg(MSGT_DEMUX, MSGL_ERR, "INVALID DESCR LEN for tag %02x: %d vs %d max, EXIT LOOP\n", ptr[j], descr_len, len); + return -1; + } + + + if(ptr[j] == 0x6a || ptr[j] == 0x7a) //A52 Descriptor + { + if(es->type == 0x6) + { + es->type = AUDIO_A52; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "DVB A52 Descriptor\n"); + } + } + else if(ptr[j] == 0x7b) //DVB DTS Descriptor + { + if(es->type == 0x6) + { + es->type = AUDIO_DTS; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "DVB DTS Descriptor\n"); + } + } + else if(ptr[j] == 0x56) // Teletext + { + if(descr_len >= 5) { + memcpy(es->lang, ptr+j+2, 3); + es->lang[3] = 0; + } + es->type = SPU_TELETEXT; + } + else if(ptr[j] == 0x59) //Subtitling Descriptor + { + uint8_t subtype; + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "Subtitling Descriptor\n"); + if(descr_len < 8) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "Descriptor length too short for DVB Subtitle Descriptor: %d, SKIPPING\n", descr_len); + } + else + { + memcpy(es->lang, &ptr[j+2], 3); + es->lang[3] = 0; + subtype = ptr[j+5]; + if( + (subtype >= 0x10 && subtype <= 0x13) || + (subtype >= 0x20 && subtype <= 0x23) + ) + { + es->type = SPU_DVB; + //page parameters: compo page 2 bytes, ancillary page 2 bytes + } + else + es->type = UNKNOWN; + } + } + else if(ptr[j] == 0x50) //Component Descriptor + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "Component Descriptor\n"); + memcpy(es->lang, &ptr[j+5], 3); + es->lang[3] = 0; + } + else if(ptr[j] == 0xa) //Language Descriptor + { + memcpy(es->lang, &ptr[j+2], 3); + es->lang[3] = 0; + mp_msg(MSGT_DEMUX, MSGL_V, "Language Descriptor: %s\n", es->lang); + } + else if(ptr[j] == 0x5) //Registration Descriptor (looks like e fourCC :) ) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "Registration Descriptor\n"); + if(descr_len < 4) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "Registration Descriptor length too short: %d, SKIPPING\n", descr_len); + } + else + { + char *d; + memcpy(es->format_descriptor, &ptr[j+2], 4); + es->format_descriptor[4] = 0; + + d = &ptr[j+2]; + if(d[0] == 'A' && d[1] == 'C' && d[2] == '-' && d[3] == '3') + { + es->type = AUDIO_A52; + } + else if(d[0] == 'D' && d[1] == 'T' && d[2] == 'S' && d[3] == '1') + { + es->type = AUDIO_DTS; + } + else if(d[0] == 'D' && d[1] == 'T' && d[2] == 'S' && d[3] == '2') + { + es->type = AUDIO_DTS; + } + else if(d[0] == 'V' && d[1] == 'C' && d[2] == '-' && d[3] == '1') + { + es->type = VIDEO_VC1; + } + else if(d[0] == 'd' && d[1] == 'r' && d[2] == 'a' && d[3] == 'c') + { + es->type = VIDEO_DIRAC; + } + else if(d[0] == 'B' && d[1] == 'S' && d[2] == 'S' && d[3] == 'D') + { + es->type = AUDIO_S302M; + } + else + es->type = UNKNOWN; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "FORMAT %s\n", es->format_descriptor); + } + } + else if(ptr[j] == 0x1e || ptr[j] == 0x1f) + { + // 0x1f is FMC, but currently it is easiest to handle them the same way + es->mp4_es_id = (ptr[j+2] << 8) | ptr[j+3]; + mp_msg(MSGT_DEMUX, MSGL_V, "SL Descriptor: ES_ID: %d(%x), pid: %d\n", es->mp4_es_id, es->mp4_es_id, es->pid); + } + else + mp_msg(MSGT_DEMUX, MSGL_DBG2, "Unknown descriptor 0x%x, SKIPPING\n", ptr[j]); + + len -= 2 + descr_len; + j += 2 + descr_len; + } + + return 1; +} + +static int parse_sl_section(pmt_t *pmt, ts_section_t *section, int is_start, unsigned char *buff, int size) +{ + int tid, len, skip; + uint8_t *ptr; + skip = collect_section(section, is_start, buff, size); + if(! skip) + return 0; + + ptr = &(section->buffer[skip]); + tid = ptr[0]; + len = ((ptr[1] & 0x0f) << 8) | ptr[2]; + mp_msg(MSGT_DEMUX, MSGL_V, "TABLEID: %d (av. %d), skip=%d, LEN: %d\n", tid, section->buffer_len, skip, len); + if(len > 4093 || section->buffer_len < len || tid != 5) + { + mp_msg(MSGT_DEMUX, MSGL_V, "SECTION TOO LARGE or wrong section type, EXIT\n"); + return 0; + } + + if(! (ptr[5] & 1)) + return 0; + + //8 is the current position, len - 9 is the amount of data available + parse_mp4_descriptors(pmt, &ptr[8], len - 9, NULL); + + return 1; +} + +static int parse_pmt(ts_priv_t * priv, uint16_t progid, uint16_t pid, int is_start, unsigned char *buff, int size) +{ + unsigned char *base, *es_base; + pmt_t *pmt; + int32_t idx, es_count, section_bytes; + uint8_t m=0; + int skip; + ts_section_t *section; + ES_stream_t *tss; + int i; + + idx = progid_idx_in_pmt(priv, progid); + + if(idx == -1) + { + priv->pmt = realloc_struct(priv->pmt, priv->pmt_cnt + 1, sizeof(pmt_t)); + if(!priv->pmt) + { + int sz = (priv->pmt_cnt + 1) * sizeof(pmt_t); + priv->pmt_cnt = 0; + mp_msg(MSGT_DEMUX, MSGL_ERR, "PARSE_PMT: COULDN'T REALLOC %d bytes, NEXT\n", sz); + return 0; + } + idx = priv->pmt_cnt; + memset(&(priv->pmt[idx]), 0, sizeof(pmt_t)); + priv->pmt_cnt++; + priv->pmt[idx].progid = progid; + } + + pmt = &(priv->pmt[idx]); + + section = &(pmt->section); + skip = collect_section(section, is_start, buff, size); + if(! skip) + return 0; + + base = &(section->buffer[skip]); + + mp_msg(MSGT_DEMUX, MSGL_V, "FILL_PMT(prog=%d), PMT_len: %d, IS_START: %d, TS_PID: %d, SIZE=%d, M=%d, ES_CNT=%d, IDX=%d, PMT_PTR=%p\n", + progid, pmt->section.buffer_len, is_start, pid, size, m, pmt->es_cnt, idx, pmt); + + pmt->table_id = base[0]; + if(pmt->table_id != 2) + return -1; + pmt->ssi = base[1] & 0x80; + pmt->section_length = (((base[1] & 0xf) << 8 ) | base[2]); + pmt->version_number = (base[5] >> 1) & 0x1f; + pmt->curr_next = (base[5] & 1); + pmt->section_number = base[6]; + pmt->last_section_number = base[7]; + pmt->PCR_PID = ((base[8] & 0x1f) << 8 ) | base[9]; + pmt->prog_descr_length = ((base[10] & 0xf) << 8 ) | base[11]; + if(pmt->prog_descr_length > pmt->section_length - 9) + { + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_PMT, INVALID PROG_DESCR LENGTH (%d vs %d)\n", pmt->prog_descr_length, pmt->section_length - 9); + return -1; + } + + if(pmt->prog_descr_length) + parse_program_descriptors(pmt, &base[12], pmt->prog_descr_length); + + es_base = &base[12 + pmt->prog_descr_length]; //the beginning of th ES loop + + section_bytes= pmt->section_length - 13 - pmt->prog_descr_length; + es_count = 0; + + while(section_bytes >= 5) + { + int es_pid, es_type; + + es_type = es_base[0]; + es_pid = ((es_base[1] & 0x1f) << 8) | es_base[2]; + + idx = es_pid_in_pmt(pmt, es_pid); + if(idx == -1) + { + pmt->es = realloc_struct(pmt->es, pmt->es_cnt + 1, sizeof(struct pmt_es_t)); + if(!pmt->es) + { + int sz = sizeof(struct pmt_es_t) * (pmt->es_cnt + 1); + pmt->es_cnt = 0; + mp_msg(MSGT_DEMUX, MSGL_ERR, "PARSE_PMT, COULDN'T ALLOCATE %d bytes for PMT_ES\n", sz); + continue; + } + idx = pmt->es_cnt; + memset(&(pmt->es[idx]), 0, sizeof(struct pmt_es_t)); + pmt->es_cnt++; + } + + pmt->es[idx].descr_length = ((es_base[3] & 0xf) << 8) | es_base[4]; + + + if(pmt->es[idx].descr_length > section_bytes - 5) + { + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_PMT, ES_DESCR_LENGTH TOO LARGE %d > %d, EXIT\n", + pmt->es[idx].descr_length, section_bytes - 5); + return -1; + } + + + pmt->es[idx].pid = es_pid; + if(es_type != 0x6) + pmt->es[idx].type = UNKNOWN; + else + pmt->es[idx].type = es_type; + + parse_descriptors(&pmt->es[idx], &es_base[5]); + + switch(es_type) + { + case 1: + pmt->es[idx].type = VIDEO_MPEG1; + break; + case 2: + pmt->es[idx].type = VIDEO_MPEG2; + break; + case 3: + case 4: + pmt->es[idx].type = AUDIO_MP2; + break; + case 6: + if(pmt->es[idx].type == 0x6) //this could have been ovrwritten by parse_descriptors + pmt->es[idx].type = UNKNOWN; + break; + case 0x10: + pmt->es[idx].type = VIDEO_MPEG4; + break; + case 0x0f: + pmt->es[idx].type = AUDIO_AAC; + break; + case 0x11: + pmt->es[idx].type = AUDIO_AAC_LATM; + for (i = 0; i < pmt->mp4es_cnt; i++) + if (pmt->mp4es[i].id == pmt->es[idx].mp4_es_id && + pmt->mp4es[i].decoder.object_type == AUDIO_AAC) + pmt->es[idx].type = AUDIO_AAC; + break; + case 0x1b: + pmt->es[idx].type = VIDEO_H264; + break; + case 0x12: + pmt->es[idx].type = SL_PES_STREAM; + break; + case 0x13: + pmt->es[idx].type = SL_SECTION; + break; + case 0x80: + pmt->es[idx].type = AUDIO_PCM_BR; + break; + case 0x81: + pmt->es[idx].type = AUDIO_A52; + break; + case 0x8A: + case 0x82: + case 0x85: + case 0x86: + pmt->es[idx].type = AUDIO_DTS; + break; + case 0x90: + pmt->es[idx].type = SPU_PGS; + break; + case 0xD1: + pmt->es[idx].type = VIDEO_DIRAC; + break; + case 0xEA: + pmt->es[idx].type = VIDEO_VC1; + break; + default: + mp_msg(MSGT_DEMUX, MSGL_DBG2, "UNKNOWN ES TYPE=0x%x\n", es_type); + pmt->es[idx].type = UNKNOWN; + } + + tss = priv->ts.pids[es_pid]; //an ES stream + if(tss == NULL) + { + tss = new_pid(priv, es_pid); + if(tss) + tss->type = pmt->es[idx].type; + } + + section_bytes -= 5 + pmt->es[idx].descr_length; + mp_msg(MSGT_DEMUX, MSGL_V, "PARSE_PMT(%d INDEX %d), STREAM: %d, FOUND pid=0x%x (%d), type=0x%x, ES_DESCR_LENGTH: %d, bytes left: %d\n", + progid, idx, es_count, pmt->es[idx].pid, pmt->es[idx].pid, pmt->es[idx].type, pmt->es[idx].descr_length, section_bytes); + + + es_base += 5 + pmt->es[idx].descr_length; + + es_count++; + } + + mp_msg(MSGT_DEMUX, MSGL_V, "----------------------------\n"); + return 1; +} + +static pmt_t* pmt_of_pid(ts_priv_t *priv, int pid, mp4_decoder_config_t **mp4_dec) +{ + int32_t i, j, k; + + if(priv->pmt) + { + for(i = 0; i < priv->pmt_cnt; i++) + { + if(priv->pmt[i].es && priv->pmt[i].es_cnt) + { + for(j = 0; j < priv->pmt[i].es_cnt; j++) + { + if(priv->pmt[i].es[j].pid == pid) + { + //search mp4_es_id + if(priv->pmt[i].es[j].mp4_es_id) + { + for(k = 0; k < priv->pmt[i].mp4es_cnt; k++) + { + if(priv->pmt[i].mp4es[k].id == priv->pmt[i].es[j].mp4_es_id) + { + *mp4_dec = &(priv->pmt[i].mp4es[k].decoder); + break; + } + } + } + + return &(priv->pmt[i]); + } + } + } + } + } + + return NULL; +} + + +static int32_t pid_type_from_pmt(ts_priv_t *priv, int pid) +{ + int32_t pmt_idx, pid_idx, i, j; + + pmt_idx = progid_idx_in_pmt(priv, priv->prog); + + if(pmt_idx != -1) + { + pid_idx = es_pid_in_pmt(&(priv->pmt[pmt_idx]), pid); + if(pid_idx != -1) + return priv->pmt[pmt_idx].es[pid_idx].type; + } + //else + //{ + for(i = 0; i < priv->pmt_cnt; i++) + { + pmt_t *pmt = &(priv->pmt[i]); + for(j = 0; j < pmt->es_cnt; j++) + if(pmt->es[j].pid == pid) + return pmt->es[j].type; + } + //} + + return UNKNOWN; +} + + +static uint8_t *pid_lang_from_pmt(ts_priv_t *priv, int pid) +{ + int32_t pmt_idx, pid_idx, i, j; + + pmt_idx = progid_idx_in_pmt(priv, priv->prog); + + if(pmt_idx != -1) + { + pid_idx = es_pid_in_pmt(&(priv->pmt[pmt_idx]), pid); + if(pid_idx != -1) + return priv->pmt[pmt_idx].es[pid_idx].lang; + } + else + { + for(i = 0; i < priv->pmt_cnt; i++) + { + pmt_t *pmt = &(priv->pmt[i]); + for(j = 0; j < pmt->es_cnt; j++) + if(pmt->es[j].pid == pid) + return pmt->es[j].lang; + } + } + + return NULL; +} + + +static int fill_packet(demuxer_t *demuxer, demux_stream_t *ds, demux_packet_t **dp, int *dp_offset, TS_stream_info *si) +{ + int ret = 0; + + if(*dp && *dp_offset <= 0) + { + free_demux_packet(*dp); + *dp = NULL; + } + if(*dp) + { + ret = *dp_offset; + resize_demux_packet(*dp, ret); //shrinked to the right size + ds_add_packet(ds, *dp); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "ADDED %d bytes to %s fifo, PTS=%.3f\n", ret, (ds == demuxer->audio ? "audio" : (ds == demuxer->video ? "video" : "sub")), (*dp)->pts); + if(si) + { + float diff = (*dp)->pts - si->last_pts; + float dur; + + if(abs(diff) > 1) //1 second, there's a discontinuity + { + si->duration += si->last_pts - si->first_pts; + si->first_pts = si->last_pts = (*dp)->pts; + } + else + { + si->last_pts = (*dp)->pts; + } + si->size += ret; + dur = si->duration + (si->last_pts - si->first_pts); + + if(dur > 0 && ds == demuxer->video) + { + ts_priv_t * priv = (ts_priv_t*) demuxer->priv; + if(dur > 1) //otherwise it may be unreliable + priv->vbitrate = (uint32_t) ((float) si->size / dur); + } + } + } + + *dp = NULL; + *dp_offset = 0; + + return ret; +} + +static int fill_extradata(mp4_decoder_config_t * mp4_dec, ES_stream_t *tss) +{ + uint8_t *tmp; + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "MP4_dec: %p, pid: %d\n", mp4_dec, tss->pid); + + if(mp4_dec->buf_size > tss->extradata_alloc) + { + tmp = realloc(tss->extradata, mp4_dec->buf_size); + if(!tmp) + return 0; + tss->extradata = tmp; + tss->extradata_alloc = mp4_dec->buf_size; + } + memcpy(tss->extradata, mp4_dec->buf, mp4_dec->buf_size); + tss->extradata_len = mp4_dec->buf_size; + mp_msg(MSGT_DEMUX, MSGL_V, "EXTRADATA: %p, alloc=%d, len=%d\n", tss->extradata, tss->extradata_alloc, tss->extradata_len); + + return tss->extradata_len; +} + +// 0 = EOF or no stream found +// else = [-] number of bytes written to the packet +static int ts_parse(demuxer_t *demuxer , ES_stream_t *es, unsigned char *packet, int probe) +{ + ES_stream_t *tss; + int buf_size, is_start, pid, base; + int len, cc, cc_ok av_unused, afc, retv = 0, is_video, is_audio, is_sub; + ts_priv_t * priv = (ts_priv_t*) demuxer->priv; + stream_t *stream = demuxer->stream; + char *p; + demux_stream_t *ds = NULL; + demux_packet_t **dp = NULL; + int *dp_offset = 0, *buffer_size = 0; + int32_t progid, pid_type, bad, ts_error; + int junk = 0, rap_flag = 0; + pmt_t *pmt; + mp4_decoder_config_t *mp4_dec; + TS_stream_info *si; + + + memset(es, 0, sizeof(*es)); + while(1) + { + bad = ts_error = 0; + ds = NULL; + dp = NULL; + dp_offset = buffer_size = NULL; + rap_flag = 0; + mp4_dec = NULL; + es->is_synced = 0; + es->lang[0] = 0; + si = NULL; + + junk = priv->ts.packet_size - TS_PACKET_SIZE; + buf_size = priv->ts.packet_size - junk; + + if(stream_eof(stream)) + { + if(! probe) + { + ts_dump_streams(priv); + demuxer->filepos = stream_tell(demuxer->stream); + } + + return 0; + } + + + if(! ts_sync(stream)) + { + mp_msg(MSGT_DEMUX, MSGL_INFO, "TS_PARSE: COULDN'T SYNC\n"); + return 0; + } + + len = stream_read(stream, &packet[1], 3); + if (len != 3) + return 0; + buf_size -= 4; + + if((packet[1] >> 7) & 0x01) //transport error + ts_error = 1; + + + is_start = packet[1] & 0x40; + pid = ((packet[1] & 0x1f) << 8) | packet[2]; + + tss = priv->ts.pids[pid]; //an ES stream + if(tss == NULL) + { + tss = new_pid(priv, pid); + if(tss == NULL) + continue; + } + + cc = (packet[3] & 0xf); + cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc)); + tss->last_cc = cc; + + bad = ts_error; // || (! cc_ok); + if(bad) + { + if(priv->keep_broken == 0) + { + stream_skip(stream, buf_size-1+junk); + continue; + } + + is_start = 0; //queued to the packet data + } + + if(is_start) + tss->is_synced = 1; + + if((!is_start && !tss->is_synced) || ((pid > 1) && (pid < 16)) || (pid == 8191)) //invalid pid + { + stream_skip(stream, buf_size-1+junk); + continue; + } + + + afc = (packet[3] >> 4) & 3; + if(! (afc % 2)) //no payload in this TS packet + { + stream_skip(stream, buf_size-1+junk); + continue; + } + + if(afc > 1) + { + int c; + c = stream_read_char(stream); + buf_size--; + if(c < 0 || c > 183) //broken from the stream layer or invalid + { + stream_skip(stream, buf_size-1+junk); + continue; + } + + //c==0 is allowed! + if(c > 0) + { + uint8_t pcrbuf[188]; + int flags = stream_read_char(stream); + int has_pcr; + rap_flag = (flags & 0x40) >> 6; + has_pcr = flags & 0x10; + + buf_size--; + c--; + stream_read(stream, pcrbuf, c); + + if(has_pcr) + { + int pcr_pid = prog_pcr_pid(priv, priv->prog); + if(pcr_pid == pid) + { + uint64_t pcr, pcr_ext; + + pcr = (int64_t)(pcrbuf[0]) << 25; + pcr |= pcrbuf[1] << 17 ; + pcr |= (pcrbuf[2]) << 9; + pcr |= pcrbuf[3] << 1 ; + pcr |= (pcrbuf[4] & 0x80) >> 7; + + pcr_ext = (pcrbuf[4] & 0x01) << 8; + pcr_ext |= pcrbuf[5]; + + pcr = pcr * 300 + pcr_ext; + + demuxer->reference_clock = (double)pcr/(double)27000000.0; + } + } + + buf_size -= c; + if(buf_size == 0) + continue; + } + } + + //find the program that the pid belongs to; if (it's the right one or -1) && pid_type==SL_SECTION + //call parse_sl_section() + pmt = pmt_of_pid(priv, pid, &mp4_dec); + if(mp4_dec) + { + fill_extradata(mp4_dec, tss); + if(IS_VIDEO(mp4_dec->object_type) || IS_AUDIO(mp4_dec->object_type)) + { + tss->type = SL_PES_STREAM; + tss->subtype = mp4_dec->object_type; + } + } + + + //TABLE PARSING + + base = priv->ts.packet_size - buf_size; + + priv->last_pid = pid; + + is_video = IS_VIDEO(tss->type) || (tss->type==SL_PES_STREAM && IS_VIDEO(tss->subtype)); + is_audio = IS_AUDIO(tss->type) || (tss->type==SL_PES_STREAM && IS_AUDIO(tss->subtype)) || (tss->type == PES_PRIVATE1); + is_sub = IS_SUB(tss->type); + pid_type = pid_type_from_pmt(priv, pid); + + // PES CONTENT STARTS HERE + if(! probe) + { + if((is_video || is_audio || is_sub) && is_start) + ts_add_stream(demuxer, tss); + + if(is_video && (demuxer->video->id == priv->ts.streams[pid].id)) + { + ds = demuxer->video; + + dp = &priv->fifo[1].pack; + dp_offset = &priv->fifo[1].offset; + buffer_size = &priv->fifo[1].buffer_size; + si = &priv->vstr; + } + else if(is_audio && (demuxer->audio->id == priv->ts.streams[pid].id)) + { + ds = demuxer->audio; + + dp = &priv->fifo[0].pack; + dp_offset = &priv->fifo[0].offset; + buffer_size = &priv->fifo[0].buffer_size; + si = &priv->astr; + } + else if(is_sub) + { + sh_sub_t *sh_sub = demuxer->sub->sh; + + if(sh_sub && sh_sub->sid == tss->pid) + { + ds = demuxer->sub; + + dp = &priv->fifo[2].pack; + dp_offset = &priv->fifo[2].offset; + buffer_size = &priv->fifo[2].buffer_size; + } + else + { + stream_skip(stream, buf_size+junk); + continue; + } + } + + //IS IT TIME TO QUEUE DATA to the dp_packet? + if(is_start && (dp != NULL)) + { + retv = fill_packet(demuxer, ds, dp, dp_offset, si); + } + + + if(dp && *dp == NULL) + { + if(*buffer_size > MAX_PACK_BYTES) + *buffer_size = MAX_PACK_BYTES; + *dp = new_demux_packet(*buffer_size); //es->size + *dp_offset = 0; + if(! *dp) + { + fprintf(stderr, "fill_buffer, NEW_ADD_PACKET(%d)FAILED\n", *buffer_size); + continue; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, "CREATED DP(%d)\n", *buffer_size); + } + } + + + if(probe || !dp) //dp is NULL for tables and sections + { + p = &packet[base]; + } + else //feeding + { + if(*dp_offset + buf_size > *buffer_size) + { + *buffer_size = *dp_offset + buf_size + TS_FEC_PACKET_SIZE; + resize_demux_packet(*dp, *buffer_size); + } + p = &((*dp)->buffer[*dp_offset]); + } + + len = stream_read(stream, p, buf_size); + if(len < buf_size) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "\r\nts_parse() couldn't read enough data: %d < %d\r\n", len, buf_size); + continue; + } + stream_skip(stream, junk); + + if(pid == 0) + { + parse_pat(priv, is_start, p, buf_size); + continue; + } + else if((tss->type == SL_SECTION) && pmt) + { + int k, mp4_es_id = -1; + ts_section_t *section; + for(k = 0; k < pmt->mp4es_cnt; k++) + { + if(pmt->mp4es[k].decoder.object_type == MP4_OD && pmt->mp4es[k].decoder.stream_type == MP4_OD) + mp4_es_id = pmt->mp4es[k].id; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, "MP4ESID: %d\n", mp4_es_id); + for(k = 0; k < pmt->es_cnt; k++) + { + if(pmt->es[k].mp4_es_id == mp4_es_id) + { + section = &(tss->section); + parse_sl_section(pmt, section, is_start, &packet[base], buf_size); + } + } + continue; + } + else + { + progid = prog_id_in_pat(priv, pid); + if(progid != -1) + { + if(pid != demuxer->video->id && pid != demuxer->audio->id && pid != demuxer->sub->id) + { + parse_pmt(priv, progid, pid, is_start, &packet[base], buf_size); + continue; + } + else + mp_msg(MSGT_DEMUX, MSGL_ERR, "Argh! Data pid %d used in the PMT, Skipping PMT parsing!\n", pid); + } + } + + if(!probe && !dp) + continue; + + if(is_start) + { + uint8_t *lang = NULL; + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "IS_START\n"); + + len = pes_parse2(p, buf_size, es, pid_type, pmt, pid); + if(! len) + { + tss->is_synced = 0; + continue; + } + es->pid = tss->pid; + tss->is_synced |= es->is_synced || rap_flag; + tss->payload_size = es->payload_size; + + if((is_sub || is_audio) && (lang = pid_lang_from_pmt(priv, es->pid))) + { + memcpy(es->lang, lang, 3); + es->lang[3] = 0; + } + else + es->lang[0] = 0; + + if(probe) + { + if(es->type == UNKNOWN) + return 0; + + tss->type = es->type; + tss->subtype = es->subtype; + + return 1; + } + else + { + if(es->pts == 0.0) + es->pts = tss->pts = tss->last_pts; + else + tss->pts = tss->last_pts = es->pts; + + mp_msg(MSGT_DEMUX, MSGL_DBG2, "ts_parse, NEW pid=%d, PSIZE: %u, type=%X, start=%p, len=%d\n", + es->pid, es->payload_size, es->type, es->start, es->size); + + demuxer->filepos = stream_tell(demuxer->stream) - es->size; + + if(es->size < 0 || es->size > buf_size) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "Broken ES packet size\n"); + es->size = 0; + } + memmove(p, es->start, es->size); + *dp_offset += es->size; + (*dp)->keyframe = 0; + (*dp)->pos = stream_tell(demuxer->stream); + (*dp)->pts = es->pts; + // subtitle packets must be returned immediately if possible + if (is_sub && !tss->payload_size) + retv = fill_packet(demuxer, ds, dp, dp_offset, si); + + if(retv > 0) + return retv; + else + continue; + } + } + else + { + uint16_t sz; + + es->pid = tss->pid; + es->type = tss->type; + es->subtype = tss->subtype; + es->pts = tss->pts = tss->last_pts; + es->start = &packet[base]; + + + if(tss->payload_size > 0) + { + sz = FFMIN(tss->payload_size, buf_size); + tss->payload_size -= sz; + es->size = sz; + } + else + { + if(is_video) + { + sz = es->size = buf_size; + } + else + { + continue; + } + } + + + if(! probe) + { + *dp_offset += sz; + + // subtitle packets must be returned immediately if possible + if(*dp_offset >= MAX_PACK_BYTES || (is_sub && !tss->payload_size)) + { + (*dp)->pts = tss->last_pts; + retv = fill_packet(demuxer, ds, dp, dp_offset, si); + return 1; + } + + continue; + } + else + { + memmove(es->start, p, sz); + + if(es->size) + return es->size; + else + continue; + } + } + } + + return 0; +} + + +static void reset_fifos(demuxer_t *demuxer, int a, int v, int s) +{ + ts_priv_t* priv = demuxer->priv; + if(a) + { + if(priv->fifo[0].pack != NULL) + { + free_demux_packet(priv->fifo[0].pack); + priv->fifo[0].pack = NULL; + } + priv->fifo[0].offset = 0; + } + + if(v) + { + if(priv->fifo[1].pack != NULL) + { + free_demux_packet(priv->fifo[1].pack); + priv->fifo[1].pack = NULL; + } + priv->fifo[1].offset = 0; + } + + if(s) + { + if(priv->fifo[2].pack != NULL) + { + free_demux_packet(priv->fifo[2].pack); + priv->fifo[2].pack = NULL; + } + priv->fifo[2].offset = 0; + } + demuxer->reference_clock = MP_NOPTS_VALUE; +} + + +static void demux_seek_ts(demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int flags) +{ + demux_stream_t *d_audio=demuxer->audio; + demux_stream_t *d_video=demuxer->video; + sh_audio_t *sh_audio=d_audio->sh; + sh_video_t *sh_video=d_video->sh; + ts_priv_t * priv = (ts_priv_t*) demuxer->priv; + int i, video_stats; + off_t newpos; + + //================= seek in MPEG-TS ========================== + + ts_dump_streams(demuxer->priv); + reset_fifos(demuxer, sh_audio != NULL, sh_video != NULL, demuxer->sub->id > 0); + + demux_flush(demuxer); + + + + video_stats = (sh_video != NULL); + if(video_stats) + { + mp_msg(MSGT_DEMUX, MSGL_V, "IBPS: %d, vb: %d\r\n", sh_video->i_bps, priv->vbitrate); + if(priv->vbitrate) + video_stats = priv->vbitrate; + else + video_stats = sh_video->i_bps; + } + + newpos = (flags & SEEK_ABSOLUTE) ? demuxer->movi_start : demuxer->filepos; + if(flags & SEEK_FACTOR) // float seek 0..1 + newpos+=(demuxer->movi_end-demuxer->movi_start)*rel_seek_secs; + else + { + // time seek (secs) + if(! video_stats) // unspecified or VBR + newpos += 2324*75*rel_seek_secs; // 174.3 kbyte/sec + else + newpos += video_stats*rel_seek_secs; + } + + + if(newpos < demuxer->movi_start) + newpos = demuxer->movi_start; //begininng of stream + + stream_seek(demuxer->stream, newpos); + for(i = 0; i < NB_PID_MAX; i++) + if(priv->ts.pids[i] != NULL) + priv->ts.pids[i]->is_synced = 0; + + videobuf_code_len = 0; + + if(sh_video != NULL) + ds_fill_buffer(d_video); + + if(sh_audio != NULL) + { + ds_fill_buffer(d_audio); + } + + while(sh_video != NULL) + { + if(sh_audio && !d_audio->eof && d_video->pts && d_audio->pts) + { + double a_pts=d_audio->pts; + a_pts+=(ds_tell_pts(d_audio)-sh_audio->a_in_buffer_len)/(double)sh_audio->i_bps; + if(d_video->pts > a_pts) + { + skip_audio_frame(sh_audio); // sync audio + continue; + } + } + + + i = sync_video_packet(d_video); + if((sh_video->format == VIDEO_MPEG1) || (sh_video->format == VIDEO_MPEG2)) + { + if(i==0x1B3 || i==0x1B8) break; // found it! + } + else if((sh_video->format == VIDEO_MPEG4) && (i==0x1B6)) + break; + else if(sh_video->format == VIDEO_VC1 && (i==0x10E || i==0x10F)) + break; + else //H264 + { + if((i & ~0x60) == 0x105 || (i & ~0x60) == 0x107) break; + } + + if(!i || !skip_video_packet(d_video)) break; // EOF? + } +} + + +static int demux_ts_fill_buffer(demuxer_t * demuxer, demux_stream_t *ds) +{ + ES_stream_t es; + ts_priv_t *priv = (ts_priv_t *)demuxer->priv; + + return -ts_parse(demuxer, &es, priv->packet, 0); +} + + +static int ts_check_file_dmx(demuxer_t *demuxer) +{ + return ts_check_file(demuxer) ? DEMUXER_TYPE_MPEG_TS : 0; +} + +static int is_usable_program(ts_priv_t *priv, pmt_t *pmt) +{ + int j; + + for(j = 0; j < pmt->es_cnt; j++) + { + if(priv->ts.pids[pmt->es[j].pid] == NULL || priv->ts.streams[pmt->es[j].pid].sh == NULL) + continue; + if( + priv->ts.streams[pmt->es[j].pid].type == TYPE_VIDEO || + priv->ts.streams[pmt->es[j].pid].type == TYPE_AUDIO + ) + return 1; + } + + return 0; +} + +static int demux_ts_control(demuxer_t *demuxer, int cmd, void *arg) +{ + ts_priv_t* priv = (ts_priv_t *)demuxer->priv; + + switch(cmd) + { + case DEMUXER_CTRL_SWITCH_AUDIO: + case DEMUXER_CTRL_SWITCH_VIDEO: + { + void *sh = NULL; + int i, n; + int reftype, areset = 0, vreset = 0; + demux_stream_t *ds; + + if(cmd == DEMUXER_CTRL_SWITCH_VIDEO) + { + reftype = TYPE_VIDEO; + ds = demuxer->video; + vreset = 1; + } + else + { + reftype = TYPE_AUDIO; + ds = demuxer->audio; + areset = 1; + } + n = *((int*)arg); + if(n == -2) + { + reset_fifos(demuxer, areset, vreset, 0); + ds->id = -2; + ds->sh = NULL; + ds_free_packs(ds); + *((int*)arg) = ds->id; + return DEMUXER_CTRL_OK; + } + + if(n < 0) + { + for(i = 0; i < 8192; i++) + { + if(priv->ts.streams[i].id == ds->id && priv->ts.streams[i].type == reftype) + break; + } + + while(!sh) + { + i = (i+1) % 8192; + if(priv->ts.streams[i].type == reftype) + { + if(priv->ts.streams[i].id == ds->id) //we made a complete loop + break; + sh = priv->ts.streams[i].sh; + } + } + } + else //audio track <n> + { + if (n >= 8192 || priv->ts.streams[n].type != reftype) return DEMUXER_CTRL_NOTIMPL; + i = n; + sh = priv->ts.streams[i].sh; + } + + if(sh) + { + if(ds->id != priv->ts.streams[i].id) + reset_fifos(demuxer, areset, vreset, 0); + ds->id = priv->ts.streams[i].id; + ds->sh = sh; + ds_free_packs(ds); + mp_msg(MSGT_DEMUX, MSGL_V, "\r\ndemux_ts, switched to audio pid %d, id: %d, sh: %p\r\n", i, ds->id, sh); + } + + *((int*)arg) = ds->id; + return DEMUXER_CTRL_OK; + } + + case DEMUXER_CTRL_IDENTIFY_PROGRAM: //returns in prog->{aid,vid} the new ids that comprise a program + { + int i, j, cnt=0; + int vid_done=0, aid_done=0; + pmt_t *pmt = NULL; + demux_program_t *prog = arg; + + if(priv->pmt_cnt < 2) + return DEMUXER_CTRL_NOTIMPL; + + if(prog->progid == -1) + { + int cur_pmt_idx = 0; + + for(i = 0; i < priv->pmt_cnt; i++) + if(priv->pmt[i].progid == priv->prog) + { + cur_pmt_idx = i; + break; + } + + i = (cur_pmt_idx + 1) % priv->pmt_cnt; + while(i != cur_pmt_idx) + { + pmt = &priv->pmt[i]; + cnt = is_usable_program(priv, pmt); + if(cnt) + break; + i = (i + 1) % priv->pmt_cnt; + } + } + else + { + for(i = 0; i < priv->pmt_cnt; i++) + if(priv->pmt[i].progid == prog->progid) + { + pmt = &priv->pmt[i]; //required program + cnt = is_usable_program(priv, pmt); + } + } + + if(!cnt) + return DEMUXER_CTRL_NOTIMPL; + + //finally some food + prog->aid = prog->vid = -2; //no audio and no video by default + for(j = 0; j < pmt->es_cnt; j++) + { + if(priv->ts.pids[pmt->es[j].pid] == NULL || priv->ts.streams[pmt->es[j].pid].sh == NULL) + continue; + + if(!vid_done && priv->ts.streams[pmt->es[j].pid].type == TYPE_VIDEO) + { + vid_done = 1; + prog->vid = pmt->es[j].pid; + } + else if(!aid_done && priv->ts.streams[pmt->es[j].pid].type == TYPE_AUDIO) + { + aid_done = 1; + prog->aid = pmt->es[j].pid; + } + } + + priv->prog = prog->progid = pmt->progid; + return DEMUXER_CTRL_OK; + } + + default: + return DEMUXER_CTRL_NOTIMPL; + } +} + + +const demuxer_desc_t demuxer_desc_mpeg_ts = { + "MPEG-TS demuxer", + "mpegts", + "TS", + "Nico Sabbi", + "", + DEMUXER_TYPE_MPEG_TS, + 0, // unsafe autodetect + ts_check_file_dmx, + demux_ts_fill_buffer, + demux_open_ts, + demux_close_ts, + demux_seek_ts, + demux_ts_control +}; diff --git a/demux/demux_ts.h b/demux/demux_ts.h new file mode 100644 index 0000000000..37bddb86da --- /dev/null +++ b/demux/demux_ts.h @@ -0,0 +1,24 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_DEMUX_TS_H +#define MPLAYER_DEMUX_TS_H + +#define TS_MAX_PROBE_SIZE 2000000 + +#endif /* MPLAYER_DEMUX_TS_H */ diff --git a/demux/ebml.c b/demux/ebml.c new file mode 100644 index 0000000000..fba8177805 --- /dev/null +++ b/demux/ebml.c @@ -0,0 +1,670 @@ +/* + * native ebml reader for the Matroska demuxer + * new parser copyright (c) 2010 Uoti Urpala + * copyright (c) 2004 Aurelien Jacobs <aurel@gnuage.org> + * based on the one written by Ronald Bultje for gstreamer + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdbool.h> +#include <inttypes.h> +#include <stddef.h> +#include <assert.h> + +#include <libavutil/intfloat.h> +#include <libavutil/common.h> +#include "talloc.h" +#include "ebml.h" +#include "stream/stream.h" +#include "mpbswap.h" +#include "mp_msg.h" + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif + +/* + * Read: the element content data ID. + * Return: the ID. + */ +uint32_t ebml_read_id(stream_t *s, int *length) +{ + int i, len_mask = 0x80; + uint32_t id; + + for (i = 0, id = stream_read_char(s); i < 4 && !(id & len_mask); i++) + len_mask >>= 1; + if (i >= 4) + return EBML_ID_INVALID; + if (length) + *length = i + 1; + while (i--) + id = (id << 8) | stream_read_char(s); + return id; +} + +/* + * Read a variable length unsigned int. + */ +uint64_t ebml_read_vlen_uint(uint8_t *buffer, int *length) +{ + int i, j, num_ffs = 0, len_mask = 0x80; + uint64_t num; + + for (i = 0, num = *buffer++; i < 8 && !(num & len_mask); i++) + len_mask >>= 1; + if (i >= 8) + return EBML_UINT_INVALID; + j = i + 1; + if (length) + *length = j; + if ((int) (num &= (len_mask - 1)) == len_mask - 1) + num_ffs++; + while (i--) { + num = (num << 8) | *buffer++; + if ((num & 0xFF) == 0xFF) + num_ffs++; + } + if (j == num_ffs) + return EBML_UINT_INVALID; + return num; +} + +/* + * Read a variable length signed int. + */ +int64_t ebml_read_vlen_int(uint8_t *buffer, int *length) +{ + uint64_t unum; + int l; + + /* read as unsigned number first */ + unum = ebml_read_vlen_uint(buffer, &l); + if (unum == EBML_UINT_INVALID) + return EBML_INT_INVALID; + if (length) + *length = l; + + return unum - ((1 << ((7 * l) - 1)) - 1); +} + +/* + * Read: element content length. + */ +uint64_t ebml_read_length(stream_t *s, int *length) +{ + int i, j, num_ffs = 0, len_mask = 0x80; + uint64_t len; + + for (i = 0, len = stream_read_char(s); i < 8 && !(len & len_mask); i++) + len_mask >>= 1; + if (i >= 8) + return EBML_UINT_INVALID; + j = i + 1; + if (length) + *length = j; + if ((int) (len &= (len_mask - 1)) == len_mask - 1) + num_ffs++; + while (i--) { + len = (len << 8) | stream_read_char(s); + if ((len & 0xFF) == 0xFF) + num_ffs++; + } + if (j == num_ffs) + return EBML_UINT_INVALID; + if (len >= 1ULL<<63) // Can happen if stream_read_char returns EOF + return EBML_UINT_INVALID; + return len; +} + +/* + * Read the next element as an unsigned int. + */ +uint64_t ebml_read_uint(stream_t *s, uint64_t *length) +{ + uint64_t len, value = 0; + int l; + + len = ebml_read_length(s, &l); + if (len == EBML_UINT_INVALID || len < 1 || len > 8) + return EBML_UINT_INVALID; + if (length) + *length = len + l; + + while (len--) + value = (value << 8) | stream_read_char(s); + + return value; +} + +/* + * Read the next element as a signed int. + */ +int64_t ebml_read_int(stream_t *s, uint64_t *length) +{ + int64_t value = 0; + uint64_t len; + int l; + + len = ebml_read_length(s, &l); + if (len == EBML_UINT_INVALID || len < 1 || len > 8) + return EBML_INT_INVALID; + if (length) + *length = len + l; + + len--; + l = stream_read_char(s); + if (l & 0x80) + value = -1; + value = (value << 8) | l; + while (len--) + value = (value << 8) | stream_read_char(s); + + return value; +} + +/* + * Read the next element as a float. + */ +double ebml_read_float(stream_t *s, uint64_t *length) +{ + double value; + uint64_t len; + int l; + + len = ebml_read_length(s, &l); + switch (len) { + case 4: + value = av_int2float(stream_read_dword(s)); + break; + + case 8: + value = av_int2double(stream_read_qword(s)); + break; + + default: + return EBML_FLOAT_INVALID; + } + + if (length) + *length = len + l; + + return value; +} + +/* + * Read the next element as an ASCII string. + */ +char *ebml_read_ascii(stream_t *s, uint64_t *length) +{ + uint64_t len; + char *str; + int l; + + len = ebml_read_length(s, &l); + if (len == EBML_UINT_INVALID) + return NULL; + if (len > SIZE_MAX - 1) + return NULL; + if (length) + *length = len + l; + + str = malloc(len + 1); + if (stream_read(s, str, len) != (int) len) { + free(str); + return NULL; + } + str[len] = '\0'; + + return str; +} + +/* + * Read the next element as a UTF-8 string. + */ +char *ebml_read_utf8(stream_t *s, uint64_t *length) +{ + return ebml_read_ascii(s, length); +} + +/* + * Skip the next element. + */ +int ebml_read_skip(stream_t *s, uint64_t *length) +{ + uint64_t len; + int l; + + len = ebml_read_length(s, &l); + if (len == EBML_UINT_INVALID) + return 1; + if (length) + *length = len + l; + + stream_skip(s, len); + + return 0; +} + +/* + * Read the next element, but only the header. The contents + * are supposed to be sub-elements which can be read separately. + */ +uint32_t ebml_read_master(stream_t *s, uint64_t *length) +{ + uint64_t len; + uint32_t id; + + id = ebml_read_id(s, NULL); + if (id == EBML_ID_INVALID) + return id; + + len = ebml_read_length(s, NULL); + if (len == EBML_UINT_INVALID) + return EBML_ID_INVALID; + if (length) + *length = len; + + return id; +} + + + +#define EVALARGS(F, ...) F(__VA_ARGS__) +#define E(str, N, type) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, type }; +#define E_SN(str, count, N) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, EBML_TYPE_SUBELEMENTS, sizeof(struct ebml_ ## N), count, (const struct ebml_field_desc[]){ +#define E_S(str, count) EVALARGS(E_SN, str, count, N) +#define FN(id, name, multiple, N) { id, multiple, offsetof(struct ebml_ ## N, name), offsetof(struct ebml_ ## N, n_ ## name), &ebml_##name##_desc}, +#define F(id, name, multiple) EVALARGS(FN, id, name, multiple, N) +#include "ebml_defs.c" +#undef EVALARGS +#undef SN +#undef S +#undef FN +#undef F + +// Used to read/write pointers to different struct types +struct generic; +#define generic_struct struct generic + +static uint32_t ebml_parse_id(uint8_t *data, int *length) +{ + int len = 1; + uint32_t id = *data++; + for (int len_mask = 0x80; !(id & len_mask); len_mask >>= 1) { + len++; + if (len > 4) { + *length = -1; + return EBML_ID_INVALID; + } + } + *length = len; + while (--len) + id = (id << 8) | *data++; + return id; +} + +static uint64_t parse_vlen(uint8_t *data, int *length, bool is_length) +{ + uint64_t r = *data++; + int len = 1; + int len_mask; + for (len_mask = 0x80; !(r & len_mask); len_mask >>= 1) { + len++; + if (len > 8) { + *length = -1; + return -1; + } + } + r &= len_mask - 1; + + int num_allones = 0; + if (r == len_mask - 1) + num_allones++; + for (int i = 1; i < len; i++) { + if (*data == 255) + num_allones++; + r = (r << 8) | *data++; + } + if (is_length && num_allones == len) { + // According to Matroska specs this means "unknown length" + // Could be supported if there are any actual files using it + *length = -1; + return -1; + } + *length = len; + return r; +} + +static uint64_t ebml_parse_length(uint8_t *data, int *length) +{ + return parse_vlen(data, length, true); +} + +static uint64_t ebml_parse_uint(uint8_t *data, int length) +{ + assert(length >= 1 && length <= 8); + uint64_t r = 0; + while (length--) + r = (r << 8) + *data++; + return r; +} + +static int64_t ebml_parse_sint(uint8_t *data, int length) +{ + assert(length >=1 && length <= 8); + int64_t r = 0; + if (*data & 0x80) + r = -1; + while (length--) + r = (r << 8) | *data++; + return r; +} + +static double ebml_parse_float(uint8_t *data, int length) +{ + assert(length == 4 || length == 8); + uint64_t i = ebml_parse_uint(data, length); + if (length == 4) + return av_int2float(i); + else + return av_int2double(i); +} + + +// target must be initialized to zero +static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target, + uint8_t *data, int size, + const struct ebml_elem_desc *type, int level) +{ + assert(type->type == EBML_TYPE_SUBELEMENTS); + assert(level < 8); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Parsing element %s\n", + level, " ", type->name); + + char *s = target; + int len; + uint8_t *end = data + size; + uint8_t *p = data; + int num_elems[MAX_EBML_SUBELEMENTS] = {}; + while (p < end) { + uint8_t *startp = p; + uint32_t id = ebml_parse_id(p, &len); + if (len > end - p) + goto past_end_error; + if (len < 0) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Error parsing subelement " + "id\n"); + goto other_error; + } + p += len; + uint64_t length = ebml_parse_length(p, &len); + if (len > end - p) + goto past_end_error; + if (len < 0) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Error parsing subelement " + "length\n"); + goto other_error; + } + p += len; + + int field_idx = -1; + for (int i = 0; i < type->field_count; i++) + if (type->fields[i].id == id) { + field_idx = i; + num_elems[i]++; + break; + } + + if (length > end - p) { + if (field_idx >= 0 && type->fields[field_idx].desc->type + != EBML_TYPE_SUBELEMENTS) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Subelement content goes " + "past end of containing element\n"); + goto other_error; + } + // Try to parse what is possible from inside this partial element + ctx->has_errors = true; + length = end - p; + } + p += length; + + continue; + + past_end_error: + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Subelement headers go " + "past end of containing element\n"); + other_error: + ctx->has_errors = true; + end = startp; + break; + } + + for (int i = 0; i < type->field_count; i++) + if (num_elems[i] && type->fields[i].multiple) { + char *ptr = s + type->fields[i].offset; + switch (type->fields[i].desc->type) { + case EBML_TYPE_SUBELEMENTS: + num_elems[i] = FFMIN(num_elems[i], + 1000000000 / type->fields[i].desc->size); + int size = num_elems[i] * type->fields[i].desc->size; + *(generic_struct **) ptr = talloc_zero_size(ctx->talloc_ctx, + size); + break; + case EBML_TYPE_UINT: + *(uint64_t **) ptr = talloc_zero_array(ctx->talloc_ctx, + uint64_t, num_elems[i]); + break; + case EBML_TYPE_SINT: + *(int64_t **) ptr = talloc_zero_array(ctx->talloc_ctx, + int64_t, num_elems[i]); + break; + case EBML_TYPE_FLOAT: + *(double **) ptr = talloc_zero_array(ctx->talloc_ctx, + double, num_elems[i]); + break; + case EBML_TYPE_STR: + case EBML_TYPE_BINARY: + *(struct bstr **) ptr = talloc_zero_array(ctx->talloc_ctx, + struct bstr, + num_elems[i]); + break; + case EBML_TYPE_EBML_ID: + *(int32_t **) ptr = talloc_zero_array(ctx->talloc_ctx, + uint32_t, num_elems[i]); + break; + default: + abort(); + } + } + + while (data < end) { + int len; + uint32_t id = ebml_parse_id(data, &len); + assert(len >= 0 && len <= end - data); + data += len; + uint64_t length = ebml_parse_length(data, &len); + assert(len >= 0 && len <= end - data); + data += len; + if (length > end - data) { + // Try to parse what is possible from inside this partial element + length = end - data; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Next subelement content goes " + "past end of containing element, will be truncated\n"); + } + int field_idx = -1; + for (int i = 0; i < type->field_count; i++) + if (type->fields[i].id == id) { + field_idx = i; + break; + } + if (field_idx < 0) { + if (id == 0xec) + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Ignoring Void element " + "size: %"PRIu64"\n", level+1, " ", length); + else if (id == 0xbf) + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Ignoring CRC-32 " + "element size: %"PRIu64"\n", level+1, " ", + length); + else + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Ignoring unrecognized " + "subelement. ID: %x size: %"PRIu64"\n", id, length); + data += length; + continue; + } + const struct ebml_field_desc *fd = &type->fields[field_idx]; + const struct ebml_elem_desc *ed = fd->desc; + bool multiple = fd->multiple; + int *countptr = (int *) (s + fd->count_offset); + if (*countptr >= num_elems[field_idx]) { + // Shouldn't happen with on any sane file without bugs + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Too many subelems?\n"); + ctx->has_errors = true; + data += length; + continue; + } + if (*countptr > 0 && !multiple) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Another subelement of type " + "%x %s (size: %"PRIu64"). Only one allowed. Ignoring.\n", + id, ed->name, length); + ctx->has_errors = true; + data += length; + continue; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Parsing %x %s size: %"PRIu64 + " value: ", level+1, " ", id, ed->name, length); + + char *fieldptr = s + fd->offset; + switch (ed->type) { + case EBML_TYPE_SUBELEMENTS: + mp_msg(MSGT_DEMUX, MSGL_DBG2, "subelements\n"); + char *subelptr; + if (multiple) { + char *array_start = (char *) *(generic_struct **) fieldptr; + subelptr = array_start + *countptr * ed->size; + } else + subelptr = fieldptr; + ebml_parse_element(ctx, subelptr, data, length, ed, level + 1); + break; + + case EBML_TYPE_UINT:; + uint64_t *uintptr; +#define GETPTR(subelptr, fieldtype) \ + if (multiple) \ + subelptr = *(fieldtype **) fieldptr + *countptr; \ + else \ + subelptr = (fieldtype *) fieldptr + GETPTR(uintptr, uint64_t); + if (length < 1 || length > 8) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "uint invalid length %"PRIu64 + "\n", length); + goto error; + } + *uintptr = ebml_parse_uint(data, length); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "uint %"PRIu64"\n", *uintptr); + break; + + case EBML_TYPE_SINT:; + int64_t *sintptr; + GETPTR(sintptr, int64_t); + if (length < 1 || length > 8) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "sint invalid length %"PRIu64 + "\n", length); + goto error; + } + *sintptr = ebml_parse_sint(data, length); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "sint %"PRId64"\n", *sintptr); + break; + + case EBML_TYPE_FLOAT:; + double *floatptr; + GETPTR(floatptr, double); + if (length != 4 && length != 8) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "float invalid length %"PRIu64 + "\n", length); + goto error; + } + *floatptr = ebml_parse_float(data, length); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "float %f\n", *floatptr); + break; + + case EBML_TYPE_STR: + case EBML_TYPE_BINARY:; + struct bstr *strptr; + GETPTR(strptr, struct bstr); + strptr->start = data; + strptr->len = length; + if (ed->type == EBML_TYPE_STR) + mp_msg(MSGT_DEMUX, MSGL_DBG2, "string \"%.*s\"\n", + BSTR_P(*strptr)); + else + mp_msg(MSGT_DEMUX, MSGL_DBG2, "binary %zd bytes\n", + strptr->len); + break; + + case EBML_TYPE_EBML_ID:; + uint32_t *idptr; + GETPTR(idptr, uint32_t); + *idptr = ebml_parse_id(data, &len); + if (len != length) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "ebml_id broken value\n"); + goto error; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, "ebml_id %x\n", (unsigned)*idptr); + break; + default: + abort(); + } + *countptr += 1; + error: + data += length; + } +} + +// target must be initialized to zero +int ebml_read_element(struct stream *s, struct ebml_parse_ctx *ctx, + void *target, const struct ebml_elem_desc *desc) +{ + ctx->has_errors = false; + int msglevel = ctx->no_error_messages ? MSGL_DBG2 : MSGL_WARN; + uint64_t length = ebml_read_length(s, &ctx->bytes_read); + if (s->eof) { + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Unexpected end of file " + "- partial or corrupt file?\n"); + return -1; + } + if (length > 1000000000) { + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Refusing to read element over " + "100 MB in size\n"); + return -1; + } + ctx->talloc_ctx = talloc_size(NULL, length + 8); + int read_len = stream_read(s, ctx->talloc_ctx, length); + ctx->bytes_read += read_len; + if (read_len < length) + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Unexpected end of file " + "- partial or corrupt file?\n"); + ebml_parse_element(ctx, target, ctx->talloc_ctx, read_len, desc, 0); + if (ctx->has_errors) + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Error parsing element %s\n", + desc->name); + return 0; +} diff --git a/demux/ebml.h b/demux/ebml.h new file mode 100644 index 0000000000..866e620c61 --- /dev/null +++ b/demux/ebml.h @@ -0,0 +1,107 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_EBML_H +#define MPLAYER_EBML_H + +#include <inttypes.h> +#include <stddef.h> +#include <stdbool.h> + +#include "stream/stream.h" +#include "bstr.h" + + +/* EBML version supported */ +#define EBML_VERSION 1 + +enum ebml_elemtype { + EBML_TYPE_SUBELEMENTS, + EBML_TYPE_UINT, + EBML_TYPE_SINT, + EBML_TYPE_FLOAT, + EBML_TYPE_STR, + EBML_TYPE_BINARY, + EBML_TYPE_EBML_ID, +}; + +struct ebml_field_desc { + uint32_t id; + bool multiple; + int offset; + int count_offset; + const struct ebml_elem_desc *desc; +}; + +struct ebml_elem_desc { + char *name; + enum ebml_elemtype type; + int size; + int field_count; + const struct ebml_field_desc *fields; +}; + +struct ebml_parse_ctx { + void *talloc_ctx; + int bytes_read; + bool has_errors; + bool no_error_messages; +}; + +#include "ebml_types.h" + +#define EBML_ID_INVALID 0xffffffff + + +/* matroska track types */ +#define MATROSKA_TRACK_VIDEO 0x01 /* rectangle-shaped pictures aka video */ +#define MATROSKA_TRACK_AUDIO 0x02 /* anything you can hear */ +#define MATROSKA_TRACK_COMPLEX 0x03 /* audio+video in same track used by DV */ +#define MATROSKA_TRACK_LOGO 0x10 /* overlay-pictures displayed over video*/ +#define MATROSKA_TRACK_SUBTITLE 0x11 /* text-subtitles */ +#define MATROSKA_TRACK_CONTROL 0x20 /* control-codes for menu or other stuff*/ + +#ifndef UINT64_MAX +#define UINT64_MAX 18446744073709551615ULL +#endif + +#ifndef INT64_MAX +#define INT64_MAX 9223372036854775807LL +#endif + +#define EBML_UINT_INVALID UINT64_MAX +#define EBML_INT_INVALID INT64_MAX +#define EBML_FLOAT_INVALID -1000000000.0 + + +uint32_t ebml_read_id (stream_t *s, int *length); +uint64_t ebml_read_vlen_uint (uint8_t *buffer, int *length); +int64_t ebml_read_vlen_int (uint8_t *buffer, int *length); +uint64_t ebml_read_length (stream_t *s, int *length); +uint64_t ebml_read_uint (stream_t *s, uint64_t *length); +int64_t ebml_read_int (stream_t *s, uint64_t *length); +double ebml_read_float (stream_t *s, uint64_t *length); +char *ebml_read_ascii (stream_t *s, uint64_t *length); +char *ebml_read_utf8 (stream_t *s, uint64_t *length); +int ebml_read_skip (stream_t *s, uint64_t *length); +uint32_t ebml_read_master (stream_t *s, uint64_t *length); + +int ebml_read_element(struct stream *s, struct ebml_parse_ctx *ctx, + void *target, const struct ebml_elem_desc *desc); + +#endif /* MPLAYER_EBML_H */ diff --git a/demux/extension.c b/demux/extension.c new file mode 100644 index 0000000000..6845c36af3 --- /dev/null +++ b/demux/extension.c @@ -0,0 +1,104 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" + +#include "stream/stream.h" +#include "demuxer.h" + +/* + * An autodetection based on the extension is not a good idea, but we don't care ;-) + * + * You should not add anything here where autodetection can be easily fixed except in + * order to speed up auto-detection, in particular for formats that are often streamed. + * In particular you should not normally add any DEMUXER_TYPE_LAVF, adding the + * format to preferred_list in libmpdemux/demuxer_lavf.c will usually achieve + * the same effect in a much more reliable way. + */ +static struct { + const char *extension; + int demuxer_type; +} extensions_table[] = { + { "vob", DEMUXER_TYPE_MPEG_PS }, + { "m2v", DEMUXER_TYPE_MPEG_PS }, + { "avi", DEMUXER_TYPE_AVI }, + { "asx", DEMUXER_TYPE_ASF }, + { "asf", DEMUXER_TYPE_ASF }, + { "wmv", DEMUXER_TYPE_ASF }, + { "wma", DEMUXER_TYPE_ASF }, + { "rm", DEMUXER_TYPE_LAVF }, + { "rmvb", DEMUXER_TYPE_LAVF }, + { "ra", DEMUXER_TYPE_LAVF }, + { "y4m", DEMUXER_TYPE_Y4M }, + { "mp3", DEMUXER_TYPE_LAVF }, + { "wav", DEMUXER_TYPE_LAVF }, + { "flac", DEMUXER_TYPE_LAVF }, + { "fla", DEMUXER_TYPE_LAVF }, + { "ogg", DEMUXER_TYPE_LAVF }, + { "ogm", DEMUXER_TYPE_LAVF }, +// { "pls", DEMUXER_TYPE_PLAYLIST }, +// { "m3u", DEMUXER_TYPE_PLAYLIST }, + { "xm", DEMUXER_TYPE_LAVF }, + { "mod", DEMUXER_TYPE_LAVF }, + { "s3m", DEMUXER_TYPE_LAVF }, + { "it", DEMUXER_TYPE_LAVF }, + { "mid", DEMUXER_TYPE_LAVF }, + { "midi", DEMUXER_TYPE_LAVF }, + { "nsv", DEMUXER_TYPE_NSV }, + { "nsa", DEMUXER_TYPE_NSV }, + { "mpc", DEMUXER_TYPE_MPC }, +#ifdef CONFIG_WIN32DLL + { "avs", DEMUXER_TYPE_AVS }, +#endif + { "302", DEMUXER_TYPE_LAVF }, + { "264", DEMUXER_TYPE_LAVF }, + { "26l", DEMUXER_TYPE_LAVF }, + { "ac3", DEMUXER_TYPE_LAVF }, + { "ape", DEMUXER_TYPE_LAVF }, + { "apl", DEMUXER_TYPE_LAVF }, + { "eac3",DEMUXER_TYPE_LAVF }, + { "mac", DEMUXER_TYPE_LAVF }, + { "str", DEMUXER_TYPE_LAVF }, + { "cdg", DEMUXER_TYPE_LAVF }, + +// At least the following are hacks against broken autodetection +// that should not be there + +}; + +int demuxer_type_by_filename(char* filename){ + int i; + char* extension=strrchr(filename,'.'); + mp_msg(MSGT_OPEN, MSGL_V, "Searching demuxer type for filename %s ext: %s\n",filename,extension); + if(extension) { + ++extension; +// mp_msg(MSGT_CPLAYER,MSGL_DBG2,"Extension: %s\n", extension ); + // Look for the extension in the extensions table + for( i=0 ; i<(sizeof(extensions_table)/sizeof(extensions_table[0])) ; i++ ) { + if( !strcasecmp(extension, extensions_table[i].extension) ) { + mp_msg(MSGT_OPEN, MSGL_V, "Trying demuxer %d based on filename extension\n",extensions_table[i].demuxer_type); + return extensions_table[i].demuxer_type; + } + } + } + return DEMUXER_TYPE_UNKNOWN; +} diff --git a/demux/matroska.h b/demux/matroska.h new file mode 100644 index 0000000000..6d56008cb1 --- /dev/null +++ b/demux/matroska.h @@ -0,0 +1,86 @@ +/* + * CodecID definitions for Matroska files + * + * see http://cvs.corecodec.org/cgi-bin/cvsweb.cgi/~checkout~/matroska/doc/website/specs/codex.html?rev=HEAD&content-type=text/html + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_MATROSKA_H +#define MPLAYER_MATROSKA_H + +#define MKV_A_AAC_2MAIN "A_AAC/MPEG2/MAIN" +#define MKV_A_AAC_2LC "A_AAC/MPEG2/LC" +#define MKV_A_AAC_2SBR "A_AAC/MPEG2/LC/SBR" +#define MKV_A_AAC_2SSR "A_AAC/MPEG2/SSR" +#define MKV_A_AAC_4MAIN "A_AAC/MPEG4/MAIN" +#define MKV_A_AAC_4LC "A_AAC/MPEG4/LC" +#define MKV_A_AAC_4SBR "A_AAC/MPEG4/LC/SBR" +#define MKV_A_AAC_4SSR "A_AAC/MPEG4/SSR" +#define MKV_A_AAC_4LTP "A_AAC/MPEG4/LTP" +#define MKV_A_AAC "A_AAC" +#define MKV_A_AC3 "A_AC3" +#define MKV_A_DTS "A_DTS" +#define MKV_A_EAC3 "A_EAC3" +#define MKV_A_MP2 "A_MPEG/L2" +#define MKV_A_MP3 "A_MPEG/L3" +#define MKV_A_PCM "A_PCM/INT/LIT" +#define MKV_A_PCM_BE "A_PCM/INT/BIG" +#define MKV_A_VORBIS "A_VORBIS" +#define MKV_A_ACM "A_MS/ACM" +#define MKV_A_REAL28 "A_REAL/28_8" +#define MKV_A_REALATRC "A_REAL/ATRC" +#define MKV_A_REALCOOK "A_REAL/COOK" +#define MKV_A_REALDNET "A_REAL/DNET" +#define MKV_A_REALSIPR "A_REAL/SIPR" +#define MKV_A_QDMC "A_QUICKTIME/QDMC" +#define MKV_A_QDMC2 "A_QUICKTIME/QDM2" +#define MKV_A_FLAC "A_FLAC" +#define MKV_A_TTA1 "A_TTA1" +#define MKV_A_WAVPACK "A_WAVPACK4" +#define MKV_A_TRUEHD "A_TRUEHD" + +#define MKV_V_MSCOMP "V_MS/VFW/FOURCC" +#define MKV_V_REALV10 "V_REAL/RV10" +#define MKV_V_REALV20 "V_REAL/RV20" +#define MKV_V_REALV30 "V_REAL/RV30" +#define MKV_V_REALV40 "V_REAL/RV40" +#define MKV_V_SORENSONV1 "V_SORENSON/V1" +#define MKV_V_SORENSONV2 "V_SORENSON/V2" +#define MKV_V_SORENSONV3 "V_SORENSON/V3" +#define MKV_V_CINEPAK "V_CINEPAK" +#define MKV_V_QUICKTIME "V_QUICKTIME" +#define MKV_V_MPEG1 "V_MPEG1" +#define MKV_V_MPEG2 "V_MPEG2" +#define MKV_V_MPEG4_SP "V_MPEG4/ISO/SP" +#define MKV_V_MPEG4_ASP "V_MPEG4/ISO/ASP" +#define MKV_V_MPEG4_AP "V_MPEG4/ISO/AP" +#define MKV_V_MPEG4_AVC "V_MPEG4/ISO/AVC" +#define MKV_V_THEORA "V_THEORA" +#define MKV_V_VP8 "V_VP8" +#define MKV_V_MJPEG "V_MJPEG" + +#define MKV_S_TEXTASCII "S_TEXT/ASCII" +#define MKV_S_TEXTUTF8 "S_TEXT/UTF8" +#define MKV_S_TEXTSSA "S_TEXT/SSA" +#define MKV_S_TEXTASS "S_TEXT/ASS" +#define MKV_S_VOBSUB "S_VOBSUB" +#define MKV_S_PGS "S_HDMV/PGS" +#define MKV_S_SSA "S_SSA" // Deprecated +#define MKV_S_ASS "S_ASS" // Deprecated + +#endif /* MPLAYER_MATROSKA_H */ diff --git a/demux/mf.c b/demux/mf.c new file mode 100644 index 0000000000..d232944593 --- /dev/null +++ b/demux/mf.c @@ -0,0 +1,171 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> + +#include "osdep/io.h" + +#include "config.h" + +#ifdef HAVE_GLOB +#include <glob.h> +#else +#include "osdep/glob.h" +#endif +#include "osdep/strsep.h" + +#include "mp_msg.h" +#include "stream/stream.h" +#include "path.h" + +#include "mf.h" + +int mf_w = 0; //352; // let codecs to detect it +int mf_h = 0; //288; +double mf_fps = 25.0; +char * mf_type = NULL; //"jpg"; + +mf_t* open_mf(char * filename){ +#if defined(HAVE_GLOB) || defined(__MINGW32__) + glob_t gg; + int i; + char * fname; + mf_t * mf; + int error_count = 0; + int count = 0; + + mf=calloc( 1,sizeof( mf_t ) ); + + if( filename[0] == '@' ) + { + FILE *lst_f=fopen(filename + 1,"r"); + if ( lst_f ) + { + fname=malloc(MP_PATH_MAX); + while ( fgets( fname,MP_PATH_MAX,lst_f ) ) + { + /* remove spaces from end of fname */ + char *t=fname + strlen( fname ) - 1; + while ( t > fname && isspace( *t ) ) *(t--)=0; + if ( !mp_path_exists( fname ) ) + { + mp_msg( MSGT_STREAM,MSGL_V,"[mf] file not found: '%s'\n",fname ); + } + else + { + mf->names=realloc( mf->names,( mf->nr_of_files + 1 ) * sizeof( char* ) ); + mf->names[mf->nr_of_files]=strdup( fname ); + mf->nr_of_files++; + } + } + fclose( lst_f ); + + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] number of files: %d\n",mf->nr_of_files ); + goto exit_mf; + } + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] %s is not indirect filelist\n",filename+1 ); + } + + if( strchr( filename,',') ) + { + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] filelist: %s\n",filename ); + + while ( ( fname=strsep( &filename,"," ) ) ) + { + if ( !mp_path_exists( fname ) ) + { + mp_msg( MSGT_STREAM,MSGL_V,"[mf] file not found: '%s'\n",fname ); + } + else + { + mf->names=realloc( mf->names,( mf->nr_of_files + 1 ) * sizeof( char* ) ); + mf->names[mf->nr_of_files]=strdup( fname ); +// mp_msg( MSGT_STREAM,MSGL_V,"[mf] added file %d.: %s\n",mf->nr_of_files,mf->names[mf->nr_of_files] ); + mf->nr_of_files++; + } + } + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] number of files: %d\n",mf->nr_of_files ); + + goto exit_mf; + } + + fname=malloc( strlen( filename ) + 32 ); + + if ( !strchr( filename,'%' ) ) + { + strcpy( fname,filename ); + if ( !strchr( filename,'*' ) ) strcat( fname,"*" ); + + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] search expr: %s\n",fname ); + + if ( glob( fname,0,NULL,&gg ) ) + { free( mf ); free( fname ); return NULL; } + + mf->nr_of_files=gg.gl_pathc; + mf->names=calloc( gg.gl_pathc, sizeof( char* ) ); + + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] number of files: %d (%zd)\n",mf->nr_of_files, gg.gl_pathc * sizeof( char* ) ); + + for( i=0;i < gg.gl_pathc;i++ ) + { + if (mp_path_isdir(gg.gl_pathv[i])) + continue; + mf->names[i]=strdup( gg.gl_pathv[i] ); +// mp_msg( MSGT_STREAM,MSGL_DBG2,"[mf] added file %d.: %s\n",i,mf->names[i] ); + } + globfree( &gg ); + goto exit_mf; + } + + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] search expr: %s\n",filename ); + + while ( error_count < 5 ) + { + sprintf( fname,filename,count++ ); + if ( !mp_path_exists( fname ) ) + { + error_count++; + mp_msg( MSGT_STREAM,MSGL_V,"[mf] file not found: '%s'\n",fname ); + } + else + { + mf->names=realloc( mf->names,( mf->nr_of_files + 1 ) * sizeof( char* ) ); + mf->names[mf->nr_of_files]=strdup( fname ); +// mp_msg( MSGT_STREAM,MSGL_V,"[mf] added file %d.: %s\n",mf->nr_of_files,mf->names[mf->nr_of_files] ); + mf->nr_of_files++; + } + } + + mp_msg( MSGT_STREAM,MSGL_INFO,"[mf] number of files: %d\n",mf->nr_of_files ); + +exit_mf: + free( fname ); + return mf; +#else + mp_msg(MSGT_STREAM,MSGL_FATAL,"[mf] mf support is disabled on your os\n"); + return 0; +#endif +} diff --git a/demux/mf.h b/demux/mf.h new file mode 100644 index 0000000000..dd461bc97c --- /dev/null +++ b/demux/mf.h @@ -0,0 +1,36 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_MF_H +#define MPLAYER_MF_H + +extern int mf_w; +extern int mf_h; +extern double mf_fps; +extern char * mf_type; + +typedef struct +{ + int curr_frame; + int nr_of_files; + char ** names; +} mf_t; + +mf_t* open_mf(char * filename); + +#endif /* MPLAYER_MF_H */ diff --git a/demux/mp3_hdr.c b/demux/mp3_hdr.c new file mode 100644 index 0000000000..a4834f4f81 --- /dev/null +++ b/demux/mp3_hdr.c @@ -0,0 +1,144 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> + +#include "config.h" +#include "mp3_hdr.h" +#include "mp_msg.h" + +//----------------------- mp3 audio frame header parser ----------------------- + +static int tabsel_123[2][3][16] = { + { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, + {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0}, + {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0} }, + + { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0}, + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0}, + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0} } +}; + +static long freqs[9] = { 44100, 48000, 32000, // MPEG 1.0 + 22050, 24000, 16000, // MPEG 2.0 + 11025, 12000, 8000}; // MPEG 2.5 + +/* + * return frame size or -1 (bad frame) + */ +int mp_get_mp3_header(unsigned char* hbuf,int* chans, int* srate, int* spf, int* mpa_layer, int* br){ + int stereo,ssize,lsf,framesize,padding,bitrate_index,sampling_frequency, divisor; + int bitrate; + int layer, mult[3] = { 12000, 144000, 144000 }; + unsigned long newhead = + hbuf[0] << 24 | + hbuf[1] << 16 | + hbuf[2] << 8 | + hbuf[3]; + +// printf("head=0x%08X\n",newhead); + + // head_check: + if( (newhead & 0xffe00000) != 0xffe00000 ){ + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"head_check failed\n"); + return -1; + } + + layer = 4-((newhead>>17)&3); + if(layer==4){ + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"not layer-1/2/3\n"); + return -1; + } + + sampling_frequency = ((newhead>>10)&0x3); // valid: 0..2 + if(sampling_frequency==3){ + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"invalid sampling_frequency\n"); + return -1; + } + + if( newhead & ((long)1<<20) ) { + // MPEG 1.0 (lsf==0) or MPEG 2.0 (lsf==1) + lsf = (newhead & ((long)1<<19)) ? 0x0 : 0x1; + sampling_frequency += (lsf*3); + } else { + // MPEG 2.5 + lsf = 1; + sampling_frequency += 6; + } + +// crc = ((newhead>>16)&0x1)^0x1; + bitrate_index = ((newhead>>12)&0xf); // valid: 1..14 + padding = ((newhead>>9)&0x1); +// fr->extension = ((newhead>>8)&0x1); +// fr->mode = ((newhead>>6)&0x3); +// fr->mode_ext = ((newhead>>4)&0x3); +// fr->copyright = ((newhead>>3)&0x1); +// fr->original = ((newhead>>2)&0x1); +// fr->emphasis = newhead & 0x3; + + stereo = ( (((newhead>>6)&0x3)) == 3) ? 1 : 2; + +// !checked later through tabsel_123[]! +// if(!bitrate_index || bitrate_index==15){ +// mp_msg(MSGT_DEMUXER,MSGL_DBG2,"Free format not supported.\n"); +// return -1; +// } + + if(lsf) + ssize = (stereo == 1) ? 9 : 17; + else + ssize = (stereo == 1) ? 17 : 32; + if(!((newhead>>16)&0x1)) ssize += 2; // CRC + + bitrate = tabsel_123[lsf][layer-1][bitrate_index]; + framesize = bitrate * mult[layer-1]; + + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"FRAMESIZE: %d, layer: %d, bitrate: %d, mult: %d\n", + framesize, layer, tabsel_123[lsf][layer-1][bitrate_index], mult[layer-1]); + if(!framesize){ + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"invalid framesize/bitrate_index\n"); + return -1; + } + + divisor = (layer == 3 ? (freqs[sampling_frequency] << lsf) : freqs[sampling_frequency]); + framesize /= divisor; + if(layer==1) + framesize = (framesize+padding)*4; + else + framesize += padding; + +// if(framesize<=0 || framesize>MAXFRAMESIZE) return FALSE; + if(srate) { + *srate = freqs[sampling_frequency]; + if(spf) { + if(layer == 1) + *spf = 384; + else if(layer == 2) + *spf = 1152; + else if(*srate < 32000) + *spf = 576; + else + *spf = 1152; + } + } + if(mpa_layer) *mpa_layer = layer; + if(chans) *chans = stereo; + if(br) *br = bitrate; + + return framesize; +} diff --git a/demux/mp3_hdr.h b/demux/mp3_hdr.h new file mode 100644 index 0000000000..a9b34ac12c --- /dev/null +++ b/demux/mp3_hdr.h @@ -0,0 +1,36 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_MP3_HDR_H +#define MPLAYER_MP3_HDR_H + +#include <stddef.h> + +int mp_get_mp3_header(unsigned char* hbuf,int* chans, int* freq, int* spf, int* mpa_layer, int* br); + +#define mp_decode_mp3_header(hbuf) mp_get_mp3_header(hbuf,NULL,NULL,NULL,NULL,NULL) + +static inline int mp_check_mp3_header(unsigned int head){ + unsigned char tmp[4] = {head >> 24, head >> 16, head >> 8, head}; + if( (head & 0xffe00000) != 0xffe00000 || + (head & 0x00000c00) == 0x00000c00) return 0; + if(mp_decode_mp3_header(tmp)<=0) return 0; + return 1; +} + +#endif /* MPLAYER_MP3_HDR_H */ diff --git a/demux/mp_taglists.c b/demux/mp_taglists.c new file mode 100644 index 0000000000..afd1b971ce --- /dev/null +++ b/demux/mp_taglists.c @@ -0,0 +1,158 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <libavformat/avformat.h> + +#include "config.h" +#include "mp_taglists.h" + +struct tag { + enum CodecID id; + unsigned int tag; +}; + +static const struct tag mp_wav_tags[] = { + { CODEC_ID_ADPCM_4XM, MKTAG('4', 'X', 'M', 'A')}, + { CODEC_ID_ADPCM_ADX, MKTAG('S', 'a', 'd', 'x')}, + { CODEC_ID_ADPCM_EA, MKTAG('A', 'D', 'E', 'A')}, + { CODEC_ID_ADPCM_EA_MAXIS_XA, MKTAG('A', 'D', 'X', 'A')}, + { CODEC_ID_ADPCM_IMA_WS, MKTAG('A', 'I', 'W', 'S')}, + { CODEC_ID_ADPCM_THP, MKTAG('T', 'H', 'P', 'A')}, + { CODEC_ID_ADPCM_XA, MKTAG('P', 'S', 'X', 'A')}, + { CODEC_ID_AMR_NB, MKTAG('n', 'b', 0, 0)}, + { CODEC_ID_COOK, MKTAG('c', 'o', 'o', 'k')}, + { CODEC_ID_DSICINAUDIO, MKTAG('D', 'C', 'I', 'A')}, + { CODEC_ID_EAC3, MKTAG('E', 'A', 'C', '3')}, + { CODEC_ID_INTERPLAY_DPCM, MKTAG('I', 'N', 'P', 'A')}, + { CODEC_ID_MLP, MKTAG('M', 'L', 'P', ' ')}, + { CODEC_ID_MP1, 0x50}, + { CODEC_ID_MP4ALS, MKTAG('A', 'L', 'S', ' ')}, + { CODEC_ID_MUSEPACK7, MKTAG('M', 'P', 'C', ' ')}, + { CODEC_ID_MUSEPACK8, MKTAG('M', 'P', 'C', '8')}, + { CODEC_ID_NELLYMOSER, MKTAG('N', 'E', 'L', 'L')}, + { CODEC_ID_PCM_LXF, MKTAG('P', 'L', 'X', 'F')}, + { CODEC_ID_QCELP, MKTAG('Q', 'c', 'l', 'p')}, + { CODEC_ID_QDM2, MKTAG('Q', 'D', 'M', '2')}, + { CODEC_ID_RA_144, MKTAG('1', '4', '_', '4')}, + { CODEC_ID_RA_288, MKTAG('2', '8', '_', '8')}, + { CODEC_ID_ROQ_DPCM, MKTAG('R', 'o', 'Q', 'A')}, + { CODEC_ID_SHORTEN, MKTAG('s', 'h', 'r', 'n')}, + { CODEC_ID_SPEEX, MKTAG('s', 'p', 'x', ' ')}, + { CODEC_ID_TTA, MKTAG('T', 'T', 'A', '1')}, + { CODEC_ID_TWINVQ, MKTAG('T', 'W', 'I', '2')}, + { CODEC_ID_WAVPACK, MKTAG('W', 'V', 'P', 'K')}, + { CODEC_ID_WESTWOOD_SND1, MKTAG('S', 'N', 'D', '1')}, + { CODEC_ID_XAN_DPCM, MKTAG('A', 'x', 'a', 'n')}, + { 0, 0 }, +}; + +static const struct tag mp_codecid_override_tags[] = { + { CODEC_ID_AAC, MKTAG('M', 'P', '4', 'A')}, + { CODEC_ID_AAC_LATM, MKTAG('M', 'P', '4', 'L')}, + { CODEC_ID_AC3, 0x2000}, + { CODEC_ID_ADPCM_IMA_AMV, MKTAG('A', 'M', 'V', 'A')}, + { CODEC_ID_BINKAUDIO_DCT, MKTAG('B', 'A', 'U', '1')}, + { CODEC_ID_BINKAUDIO_RDFT, MKTAG('B', 'A', 'U', '2')}, + { CODEC_ID_DTS, 0x2001}, + { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'd')}, + { CODEC_ID_EAC3, MKTAG('E', 'A', 'C', '3')}, + { CODEC_ID_H264, MKTAG('H', '2', '6', '4')}, + { CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'V')}, + { CODEC_ID_PCM_BLURAY, MKTAG('B', 'P', 'C', 'M')}, + { CODEC_ID_PCM_S8, MKTAG('t', 'w', 'o', 's')}, + { CODEC_ID_PCM_U8, 1}, + { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's')}, + { CODEC_ID_PCM_S16LE, 1}, + { CODEC_ID_PCM_S24BE, MKTAG('i', 'n', '2', '4')}, + { CODEC_ID_PCM_S24LE, 1}, + { CODEC_ID_PCM_S32BE, MKTAG('i', 'n', '3', '2')}, + { CODEC_ID_PCM_S32LE, 1}, + { CODEC_ID_MP2, 0x50}, + { CODEC_ID_MPEG2VIDEO, MKTAG('M', 'P', 'G', '2')}, + { CODEC_ID_TRUEHD, MKTAG('T', 'R', 'H', 'D')}, + { 0, 0 }, +}; + +static const struct tag mp_bmp_tags[] = { + { CODEC_ID_AMV, MKTAG('A', 'M', 'V', 'V')}, + { CODEC_ID_ANM, MKTAG('A', 'N', 'M', ' ')}, + { CODEC_ID_AVS, MKTAG('A', 'V', 'S', ' ')}, + { CODEC_ID_BETHSOFTVID, MKTAG('B', 'E', 'T', 'H')}, + { CODEC_ID_BFI, MKTAG('B', 'F', 'I', 'V')}, + { CODEC_ID_C93, MKTAG('C', '9', '3', 'V')}, + { CODEC_ID_CDGRAPHICS, MKTAG('C', 'D', 'G', 'R')}, + { CODEC_ID_DNXHD, MKTAG('A', 'V', 'd', 'n')}, + { CODEC_ID_DSICINVIDEO, MKTAG('D', 'C', 'I', 'V')}, + { CODEC_ID_DXA, MKTAG('D', 'X', 'A', '1')}, + { CODEC_ID_FLIC, MKTAG('F', 'L', 'I', 'C')}, + { CODEC_ID_IDCIN, MKTAG('I', 'D', 'C', 'I')}, + { CODEC_ID_INTERPLAY_VIDEO, MKTAG('I', 'N', 'P', 'V')}, + { CODEC_ID_JV, MKTAG('F', 'F', 'J', 'V')}, + { CODEC_ID_MDEC, MKTAG('M', 'D', 'E', 'C')}, + { CODEC_ID_MOTIONPIXELS, MKTAG('M', 'V', 'I', '1')}, + { CODEC_ID_NUV, MKTAG('N', 'U', 'V', '1')}, + { CODEC_ID_RL2, MKTAG('R', 'L', '2', 'V')}, + { CODEC_ID_ROQ, MKTAG('R', 'o', 'Q', 'V')}, + { CODEC_ID_RV10, MKTAG('R', 'V', '1', '0')}, + { CODEC_ID_RV20, MKTAG('R', 'V', '2', '0')}, + { CODEC_ID_RV30, MKTAG('R', 'V', '3', '0')}, + { CODEC_ID_RV40, MKTAG('R', 'V', '4', '0')}, + { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3')}, + { CODEC_ID_TGV, MKTAG('f', 'V', 'G', 'T')}, + { CODEC_ID_THP, MKTAG('T', 'H', 'P', 'V')}, + { CODEC_ID_TIERTEXSEQVIDEO, MKTAG('T', 'S', 'E', 'Q')}, + { CODEC_ID_TXD, MKTAG('T', 'X', 'D', 'V')}, + { CODEC_ID_VP6A, MKTAG('V', 'P', '6', 'A')}, + { CODEC_ID_VMDVIDEO, MKTAG('V', 'M', 'D', 'V')}, + { CODEC_ID_WS_VQA, MKTAG('V', 'Q', 'A', 'V')}, + { CODEC_ID_XAN_WC3, MKTAG('W', 'C', '3', 'V')}, + { 0, 0 }, +}; + +static unsigned int codec_get_tag(const struct tag *tags, enum CodecID id) +{ + while (tags->id != CODEC_ID_NONE) { + if (tags->id == id) + return tags->tag; + tags++; + } + return 0; +} + +unsigned int mp_taglist_override(enum CodecID id) +{ + return codec_get_tag(mp_codecid_override_tags, id); +} + +unsigned int mp_taglist_video(enum CodecID id) +{ + const struct AVCodecTag *tags[] = {avformat_get_riff_video_tags(), NULL }; + unsigned int tag = av_codec_get_tag(tags, id); + if (tag) + return tag; + return codec_get_tag(mp_bmp_tags, id); +} + +unsigned int mp_taglist_audio(enum CodecID id) +{ + const struct AVCodecTag *tags[] = {avformat_get_riff_audio_tags(), NULL }; + unsigned int tag = av_codec_get_tag(tags, id); + if (tag) + return tag; + return codec_get_tag(mp_wav_tags, id); +} diff --git a/demux/mp_taglists.h b/demux/mp_taglists.h new file mode 100644 index 0000000000..d23a982a93 --- /dev/null +++ b/demux/mp_taglists.h @@ -0,0 +1,28 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_MP_TAGLISTS_H +#define MPLAYER_MP_TAGLISTS_H + +#include <libavcodec/avcodec.h> + +unsigned int mp_taglist_override(enum CodecID id); +unsigned int mp_taglist_video(enum CodecID id); +unsigned int mp_taglist_audio(enum CodecID id); + +#endif /* MPLAYER_MP_TAGLISTS_H */ diff --git a/demux/mpeg_hdr.c b/demux/mpeg_hdr.c new file mode 100644 index 0000000000..0c368aa7a2 --- /dev/null +++ b/demux/mpeg_hdr.c @@ -0,0 +1,539 @@ +/* + * based on libmpeg2/header.c by Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "mpeg_hdr.h" +#include "libavutil/attributes.h" +#include "mp_msg.h" + +static float frameratecode2framerate[16] = { + 0, + // Official mpeg1/2 framerates: (1-8) + 24000.0/1001, 24,25, + 30000.0/1001, 30,50, + 60000.0/1001, 60, + // Xing's 15fps: (9) + 15, + // libmpeg3's "Unofficial economy rates": (10-13) + 5,10,12,15, + // some invalid ones: (14-15) + 0,0 +}; + + +int mp_header_process_sequence_header (mp_mpeg_header_t * picture, const unsigned char * buffer) +{ + int height; + + if ((buffer[6] & 0x20) != 0x20){ + fprintf(stderr, "missing marker bit!\n"); + return 1; /* missing marker_bit */ + } + + height = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; + + picture->display_picture_width = height >> 12; + picture->display_picture_height = height & 0xfff; + + picture->aspect_ratio_information = buffer[3] >> 4; + picture->frame_rate_code = buffer[3] & 15; + picture->fps=frameratecode2framerate[picture->frame_rate_code]; + picture->bitrate = (buffer[4]<<10)|(buffer[5]<<2)|(buffer[6]>>6); + picture->mpeg1 = 1; + picture->picture_structure = 3; //FRAME_PICTURE; + picture->display_time=100; + picture->frame_rate_extension_n = 1; + picture->frame_rate_extension_d = 1; + return 0; +} + +static int header_process_sequence_extension (mp_mpeg_header_t * picture, + unsigned char * buffer) +{ + /* check chroma format, size extensions, marker bit */ + + if ( ((buffer[1] & 0x06) == 0x00) || + ((buffer[1] & 0x01) != 0x00) || (buffer[2] & 0xe0) || + ((buffer[3] & 0x01) != 0x01) ) + return 1; + + picture->progressive_sequence = (buffer[1] >> 3) & 1; + picture->frame_rate_extension_n = ((buffer[5] >> 5) & 3) + 1; + picture->frame_rate_extension_d = (buffer[5] & 0x1f) + 1; + + picture->mpeg1 = 0; + return 0; +} + +static int header_process_picture_coding_extension (mp_mpeg_header_t * picture, unsigned char * buffer) +{ + picture->picture_structure = buffer[2] & 3; + picture->top_field_first = buffer[3] >> 7; + picture->repeat_first_field = (buffer[3] >> 1) & 1; + picture->progressive_frame = buffer[4] >> 7; + + // repeat_first implementation by A'rpi/ESP-team, based on libmpeg3: + picture->display_time=100; + if(picture->repeat_first_field){ + if(picture->progressive_sequence){ + if(picture->top_field_first) + picture->display_time+=200; + else + picture->display_time+=100; + } else + if(picture->progressive_frame){ + picture->display_time+=50; + } + } + //temopral hack. We calc time on every field, so if we have 2 fields + // interlaced we'll end with double time for 1 frame + if( picture->picture_structure!=3 ) picture->display_time/=2; + return 0; +} + +int mp_header_process_extension (mp_mpeg_header_t * picture, unsigned char * buffer) +{ + switch (buffer[0] & 0xf0) { + case 0x10: /* sequence extension */ + return header_process_sequence_extension (picture, buffer); + case 0x80: /* picture coding extension */ + return header_process_picture_coding_extension (picture, buffer); + } + return 0; +} + +float mpeg12_aspect_info(mp_mpeg_header_t *picture) +{ + float aspect = 0.0; + + switch(picture->aspect_ratio_information) { + case 2: // PAL/NTSC SVCD/DVD 4:3 + case 8: // PAL VCD 4:3 + case 12: // NTSC VCD 4:3 + aspect=4.0/3.0; + break; + case 3: // PAL/NTSC Widescreen SVCD/DVD 16:9 + case 6: // (PAL?)/NTSC Widescreen SVCD 16:9 + aspect=16.0/9.0; + break; + case 4: // according to ISO-138182-2 Table 6.3 + aspect=2.21; + break; + case 1: // VGA 1:1 - do not prescale + case 9: // Movie Type ??? / 640x480 + aspect=0.0; + break; + default: + mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Detected unknown aspect_ratio_information in mpeg sequence header.\n" + "Please report the aspect value (%i) along with the movie type (VGA,PAL,NTSC," + "SECAM) and the movie resolution (720x576,352x240,480x480,...) to the MPlayer" + " developers, so that we can add support for it!\nAssuming 1:1 aspect for now.\n", + picture->aspect_ratio_information); + } + + return aspect; +} + +//MPEG4 HEADERS +unsigned char mp_getbits(unsigned char *buffer, unsigned int from, unsigned char len) +{ + unsigned int n; + unsigned char m, u, l, y; + + n = from / 8; + m = from % 8; + u = 8 - m; + l = (len > u ? len - u : 0); + + y = (buffer[n] << m); + if(8 > len) + y >>= (8-len); + if(l) + y |= (buffer[n+1] >> (8-l)); + + //fprintf(stderr, "GETBITS(%d -> %d): bytes=0x%x 0x%x, n=%d, m=%d, l=%d, u=%d, Y=%d\n", + // from, (int) len, (int) buffer[n],(int) buffer[n+1], n, (int) m, (int) l, (int) u, (int) y); + return y; +} + +static inline unsigned int mp_getbits16(unsigned char *buffer, unsigned int from, unsigned char len) +{ + if(len > 8) + return (mp_getbits(buffer, from, len - 8) << 8) | mp_getbits(buffer, from + len - 8, 8); + else + return mp_getbits(buffer, from, len); +} + +#define getbits mp_getbits +#define getbits16 mp_getbits16 + +static int read_timeinc(mp_mpeg_header_t * picture, unsigned char * buffer, int n) +{ + if(picture->timeinc_bits > 8) { + picture->timeinc_unit = getbits(buffer, n, picture->timeinc_bits - 8) << 8; + n += picture->timeinc_bits - 8; + picture->timeinc_unit |= getbits(buffer, n, 8); + n += 8; + } else { + picture->timeinc_unit = getbits(buffer, n, picture->timeinc_bits); + n += picture->timeinc_bits; + } + //fprintf(stderr, "TIMEINC2: %d, bits: %d\n", picture->timeinc_unit, picture->timeinc_bits); + return n; +} + +int mp4_header_process_vol(mp_mpeg_header_t * picture, unsigned char * buffer) +{ + unsigned int n, aspect=0, aspectw av_unused=0, aspecth av_unused=0, x=1, v; + + //begins with 0x0000012x + picture->fps = 0; + picture->timeinc_bits = picture->timeinc_resolution = picture->timeinc_unit = 0; + n = 9; + if(getbits(buffer, n, 1)) + n += 7; + n++; + aspect=getbits(buffer, n, 4); + n += 4; + if(aspect == 0x0f) { + aspectw = getbits(buffer, n, 8); + n += 8; + aspecth = getbits(buffer, n, 8); + n += 8; + } + + if(getbits(buffer, n, 1)) { + n += 4; + if(getbits(buffer, n, 1)) + n += 79; + n++; + } else n++; + + n+=3; + + picture->timeinc_resolution = getbits(buffer, n, 8) << 8; + n += 8; + picture->timeinc_resolution |= getbits(buffer, n, 8); + n += 8; + + picture->timeinc_bits = 0; + v = picture->timeinc_resolution - 1; + while(v && (x<16)) { + v>>=1; + picture->timeinc_bits++; + } + picture->timeinc_bits = (picture->timeinc_bits > 1 ? picture->timeinc_bits : 1); + + n++; //marker bit + + if(getbits(buffer, n++, 1)) { //fixed_vop_timeinc + n += read_timeinc(picture, buffer, n); + + if(picture->timeinc_unit) + picture->fps = (float) picture->timeinc_resolution / (float) picture->timeinc_unit; + } + + n++; //marker bit + picture->display_picture_width = getbits16(buffer, n, 13); + n += 13; + n++; //marker bit + picture->display_picture_height = getbits16(buffer, n, 13); + n += 13; + + //fprintf(stderr, "ASPECT: %d, PARW=%d, PARH=%d, TIMEINCRESOLUTION: %d, FIXED_TIMEINC: %d (number of bits: %d), FPS: %u\n", + // aspect, aspectw, aspecth, picture->timeinc_resolution, picture->timeinc_unit, picture->timeinc_bits, picture->fps); + + return 0; +} + +void mp4_header_process_vop(mp_mpeg_header_t * picture, unsigned char * buffer) +{ + int n; + n = 0; + picture->picture_type = getbits(buffer, n, 2); + n += 2; + while(getbits(buffer, n, 1)) + n++; + n++; + getbits(buffer, n, 1); + n++; + n += read_timeinc(picture, buffer, n); +} + +#define min(a, b) ((a) <= (b) ? (a) : (b)) + +static unsigned int read_golomb(unsigned char *buffer, unsigned int *init) +{ + unsigned int x, v = 0, v2 = 0, m, len = 0, n = *init; + + while(getbits(buffer, n++, 1) == 0) + len++; + + x = len + n; + while(n < x) + { + m = min(x - n, 8); + v |= getbits(buffer, n, m); + n += m; + if(x - n > 8) + v <<= 8; + } + + v2 = 1; + for(n = 0; n < len; n++) + v2 <<= 1; + v2 = (v2 - 1) + v; + + //fprintf(stderr, "READ_GOLOMB(%u), V=2^%u + %u-1 = %u\n", *init, len, v, v2); + *init = x; + return v2; +} + +inline static int read_golomb_s(unsigned char *buffer, unsigned int *init) +{ + unsigned int v = read_golomb(buffer, init); + return (v & 1) ? ((v + 1) >> 1) : -(v >> 1); +} + +static int h264_parse_vui(mp_mpeg_header_t * picture, unsigned char * buf, unsigned int n) +{ + unsigned int overscan, vsp_color, chroma, timing, fixed_fps; + + if(getbits(buf, n++, 1)) + { + picture->aspect_ratio_information = getbits(buf, n, 8); + n += 8; + if(picture->aspect_ratio_information == 255) + { + picture->display_picture_width = (getbits(buf, n, 8) << 8) | getbits(buf, n + 8, 8); + n += 16; + + picture->display_picture_height = (getbits(buf, n, 8) << 8) | getbits(buf, n + 8, 8); + n += 16; + } + } + + if((overscan=getbits(buf, n++, 1))) + n++; + if((vsp_color=getbits(buf, n++, 1))) + { + n += 4; + if(getbits(buf, n++, 1)) + n += 24; + } + if((chroma=getbits(buf, n++, 1))) + { + read_golomb(buf, &n); + read_golomb(buf, &n); + } + if((timing=getbits(buf, n++, 1))) + { + picture->timeinc_unit = (getbits(buf, n, 8) << 24) | (getbits(buf, n+8, 8) << 16) | (getbits(buf, n+16, 8) << 8) | getbits(buf, n+24, 8); + n += 32; + + picture->timeinc_resolution = (getbits(buf, n, 8) << 24) | (getbits(buf, n+8, 8) << 16) | (getbits(buf, n+16, 8) << 8) | getbits(buf, n+24, 8); + n += 32; + + fixed_fps = getbits(buf, n, 1); + + if(picture->timeinc_unit > 0 && picture->timeinc_resolution > 0) + picture->fps = (float) picture->timeinc_resolution / (float) picture->timeinc_unit; + if(fixed_fps) + picture->fps /= 2; + } + + //fprintf(stderr, "H264_PARSE_VUI, OVESCAN=%u, VSP_COLOR=%u, CHROMA=%u, TIMING=%u, DISPW=%u, DISPH=%u, TIMERES=%u, TIMEINC=%u, FIXED_FPS=%u\n", overscan, vsp_color, chroma, timing, picture->display_picture_width, picture->display_picture_height, + // picture->timeinc_resolution, picture->timeinc_unit, picture->timeinc_unit, fixed_fps); + + return n; +} + +static int mp_unescape03(unsigned char *buf, int len) +{ + unsigned char *dest; + int i, j, skip; + + dest = malloc(len); + if(! dest) + return 0; + + j = i = skip = 0; + while(i <= len-3) + { + if(buf[i] == 0 && buf[i+1] == 0 && buf[i+2] == 3) + { + dest[j] = dest[j+1] = 0; + j += 2; + i += 3; + skip++; + } + else + { + dest[j] = buf[i]; + j++; + i++; + } + } + dest[j] = buf[len-2]; + dest[j+1] = buf[len-1]; + len -= skip; + memcpy(buf, dest, len); + free(dest); + + return len; +} + +int h264_parse_sps(mp_mpeg_header_t * picture, unsigned char * buf, int len) +{ + unsigned int n = 0, v, i, k, mbh; + int frame_mbs_only; + + len = mp_unescape03(buf, len); + + picture->fps = picture->timeinc_unit = picture->timeinc_resolution = 0; + n = 24; + read_golomb(buf, &n); + if(buf[0] >= 100){ + if(read_golomb(buf, &n) == 3) + n++; + read_golomb(buf, &n); + read_golomb(buf, &n); + n++; + if(getbits(buf, n++, 1)){ + for(i = 0; i < 8; i++) + { // scaling list is skipped for now + if(getbits(buf, n++, 1)) + { + v = 8; + for(k = (i < 6 ? 16 : 64); k && v; k--) + v = (v + read_golomb_s(buf, &n)) & 255; + } + } + } + } + read_golomb(buf, &n); + v = read_golomb(buf, &n); + if(v == 0) + read_golomb(buf, &n); + else if(v == 1) + { + getbits(buf, n++, 1); + read_golomb(buf, &n); + read_golomb(buf, &n); + v = read_golomb(buf, &n); + for(i = 0; i < v; i++) + read_golomb(buf, &n); + } + read_golomb(buf, &n); + getbits(buf, n++, 1); + picture->display_picture_width = 16 *(read_golomb(buf, &n)+1); + mbh = read_golomb(buf, &n)+1; + frame_mbs_only = getbits(buf, n++, 1); + picture->display_picture_height = 16 * (2 - frame_mbs_only) * mbh; + if(!frame_mbs_only) + getbits(buf, n++, 1); + getbits(buf, n++, 1); + if(getbits(buf, n++, 1)) + { + read_golomb(buf, &n); + read_golomb(buf, &n); + read_golomb(buf, &n); + read_golomb(buf, &n); + } + if(getbits(buf, n++, 1)) + n = h264_parse_vui(picture, buf, n); + + return n; +} + +int mp_vc1_decode_sequence_header(mp_mpeg_header_t * picture, unsigned char * buf, int len) +{ + int n, x; + + len = mp_unescape03(buf, len); + + picture->display_picture_width = picture->display_picture_height = 0; + picture->fps = 0; + n = 0; + x = getbits(buf, n, 2); + n += 2; + if(x != 3) //not advanced profile + return 0; + + getbits16(buf, n, 14); + n += 14; + picture->display_picture_width = getbits16(buf, n, 12) * 2 + 2; + n += 12; + picture->display_picture_height = getbits16(buf, n, 12) * 2 + 2; + n += 12; + getbits(buf, n, 6); + n += 6; + x = getbits(buf, n, 1); + n += 1; + if(x) //display info + { + getbits16(buf, n, 14); + n += 14; + getbits16(buf, n, 14); + n += 14; + if(getbits(buf, n++, 1)) //aspect ratio + { + x = getbits(buf, n, 4); + n += 4; + if(x == 15) + { + getbits16(buf, n, 16); + n += 16; + } + } + + if(getbits(buf, n++, 1)) //framerates + { + int frexp=0, frnum=0, frden=0; + + if(getbits(buf, n++, 1)) + { + frexp = getbits16(buf, n, 16); + n += 16; + picture->fps = (double) (frexp+1) / 32.0; + } + else + { + float frates[] = {0, 24000, 25000, 30000, 50000, 60000, 48000, 72000, 0}; + float frdivs[] = {0, 1000, 1001, 0}; + + frnum = getbits(buf, n, 8); + n += 8; + frden = getbits(buf, n, 4); + n += 4; + if((frden == 1 || frden == 2) && (frnum < 8)) + picture->fps = frates[frnum] / frdivs[frden]; + } + } + } + + //free(dest); + return 1; +} diff --git a/demux/mpeg_hdr.h b/demux/mpeg_hdr.h new file mode 100644 index 0000000000..ccd84bcdb0 --- /dev/null +++ b/demux/mpeg_hdr.h @@ -0,0 +1,55 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_MPEG_HDR_H +#define MPLAYER_MPEG_HDR_H + +typedef struct { + // video info: + int mpeg1; // 0=mpeg2 1=mpeg1 + int display_picture_width; + int display_picture_height; + int aspect_ratio_information; + int frame_rate_code; + float fps; + int frame_rate_extension_n; + int frame_rate_extension_d; + int bitrate; // 0x3FFFF==VBR + // timing: + int picture_structure; + int progressive_sequence; + int repeat_first_field; + int progressive_frame; + int top_field_first; + int display_time; // secs*100 + //the following are for mpeg4 + unsigned int timeinc_resolution, timeinc_bits, timeinc_unit; + int picture_type; +} mp_mpeg_header_t; + +int mp_header_process_sequence_header (mp_mpeg_header_t * picture, const unsigned char * buffer); +int mp_header_process_extension (mp_mpeg_header_t * picture, unsigned char * buffer); +float mpeg12_aspect_info(mp_mpeg_header_t *picture); +int mp4_header_process_vol(mp_mpeg_header_t * picture, unsigned char * buffer); +void mp4_header_process_vop(mp_mpeg_header_t * picture, unsigned char * buffer); +int h264_parse_sps(mp_mpeg_header_t * picture, unsigned char * buf, int len); +int mp_vc1_decode_sequence_header(mp_mpeg_header_t * picture, unsigned char * buf, int len); + +unsigned char mp_getbits(unsigned char *buffer, unsigned int from, unsigned char len); + +#endif /* MPLAYER_MPEG_HDR_H */ diff --git a/demux/ms_hdr.h b/demux/ms_hdr.h new file mode 100644 index 0000000000..8e545635dd --- /dev/null +++ b/demux/ms_hdr.h @@ -0,0 +1,114 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_MS_HDR_H +#define MPLAYER_MS_HDR_H + +#include <sys/types.h> +#include "config.h" + +#ifndef _WAVEFORMATEX_ +#define _WAVEFORMATEX_ +typedef struct __attribute__((__packed__)) _WAVEFORMATEX { + unsigned short wFormatTag; + unsigned short nChannels; + unsigned int nSamplesPerSec; + unsigned int nAvgBytesPerSec; + unsigned short nBlockAlign; + unsigned short wBitsPerSample; + unsigned short cbSize; +} WAVEFORMATEX, *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX; +#endif /* _WAVEFORMATEX_ */ + +#ifndef _WAVEFORMATEXTENSIBLE_ +#define _WAVEFORMATEXTENSIBLE_ +typedef struct __attribute__((__packed__)) _WAVEFORMATEXTENSIBLE { + WAVEFORMATEX wf; + unsigned short wValidBitsPerSample; + unsigned int dwChannelMask; + unsigned int SubFormat; // Only interested in first 32 bits of guid + unsigned int _guid_remainder[3]; +} WAVEFORMATEXTENSIBLE; +#endif /* _WAVEFORMATEXTENSIBLE_ */ + +#ifndef _MPEGLAYER3WAVEFORMAT_ +#define _MPEGLAYER3WAVEFORMAT_ +typedef struct __attribute__((__packed__)) mpeglayer3waveformat_tag { + WAVEFORMATEX wf; + unsigned short wID; + unsigned int fdwFlags; + unsigned short nBlockSize; + unsigned short nFramesPerBlock; + unsigned short nCodecDelay; +} MPEGLAYER3WAVEFORMAT; +#endif /* _MPEGLAYER3WAVEFORMAT_ */ + +/* windows.h #includes wingdi.h on MinGW. */ +#if !defined(_BITMAPINFOHEADER_) && !defined(_WINGDI_) +#define _BITMAPINFOHEADER_ +typedef struct __attribute__((__packed__)) +{ + int biSize; + int biWidth; + int biHeight; + short biPlanes; + short biBitCount; + int biCompression; + int biSizeImage; + int biXPelsPerMeter; + int biYPelsPerMeter; + int biClrUsed; + int biClrImportant; +} BITMAPINFOHEADER, *PBITMAPINFOHEADER, *LPBITMAPINFOHEADER; +typedef struct { + BITMAPINFOHEADER bmiHeader; + int bmiColors[1]; +} BITMAPINFO, *LPBITMAPINFO; +#endif + +#ifndef le2me_BITMAPINFOHEADER +#if BYTE_ORDER == BIG_ENDIAN +#define le2me_BITMAPINFOHEADER(h) { \ + (h)->biSize = le2me_32((h)->biSize); \ + (h)->biWidth = le2me_32((h)->biWidth); \ + (h)->biHeight = le2me_32((h)->biHeight); \ + (h)->biPlanes = le2me_16((h)->biPlanes); \ + (h)->biBitCount = le2me_16((h)->biBitCount); \ + (h)->biCompression = le2me_32((h)->biCompression); \ + (h)->biSizeImage = le2me_32((h)->biSizeImage); \ + (h)->biXPelsPerMeter = le2me_32((h)->biXPelsPerMeter); \ + (h)->biYPelsPerMeter = le2me_32((h)->biYPelsPerMeter); \ + (h)->biClrUsed = le2me_32((h)->biClrUsed); \ + (h)->biClrImportant = le2me_32((h)->biClrImportant); \ +} +#define le2me_WAVEFORMATEX(h) { \ + (h)->wFormatTag = le2me_16((h)->wFormatTag); \ + (h)->nChannels = le2me_16((h)->nChannels); \ + (h)->nSamplesPerSec = le2me_32((h)->nSamplesPerSec); \ + (h)->nAvgBytesPerSec = le2me_32((h)->nAvgBytesPerSec); \ + (h)->nBlockAlign = le2me_16((h)->nBlockAlign); \ + (h)->wBitsPerSample = le2me_16((h)->wBitsPerSample); \ + (h)->cbSize = le2me_16((h)->cbSize); \ +} +#else +#define le2me_BITMAPINFOHEADER(h) /**/ +#define le2me_WAVEFORMATEX(h) /**/ +#endif +#endif + +#endif /* MPLAYER_MS_HDR_H */ diff --git a/demux/parse_es.c b/demux/parse_es.c new file mode 100644 index 0000000000..05507a495a --- /dev/null +++ b/demux/parse_es.c @@ -0,0 +1,158 @@ +/* + * MPEG-ES video parser + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "mp_msg.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "parse_es.h" + +//static unsigned char videobuffer[MAX_VIDEO_PACKET_SIZE]; +unsigned char* videobuffer=NULL; +int videobuf_len=0; +int next_nal = -1; +///! legacy variable, 4 if stream is synced, 0 if not +int videobuf_code_len=0; + +#define MAX_SYNCLEN (10 * 1024 * 1024) +// sync video stream, and returns next packet code +int sync_video_packet(demux_stream_t *ds){ + if (!videobuf_code_len) { + int skipped=0; + if (!demux_pattern_3(ds, NULL, MAX_SYNCLEN, &skipped, 0x100)) { + if (skipped == MAX_SYNCLEN) + mp_msg(MSGT_DEMUXER, MSGL_ERR, "parse_es: could not sync video stream!\n"); + goto eof_out; + } + next_nal = demux_getc(ds); + if (next_nal < 0) + goto eof_out; + videobuf_code_len = 4; + if(skipped) mp_dbg(MSGT_PARSEES,MSGL_DBG2,"videobuf: %d bytes skipped (next: 0x1%02X)\n",skipped,next_nal); + } + return 0x100|next_nal; + +eof_out: + next_nal = -1; + videobuf_code_len = 0; + return 0; +} + +// return: packet length +int read_video_packet(demux_stream_t *ds){ +int packet_start; + int res, read; + + if (VIDEOBUFFER_SIZE - videobuf_len < 5) + return 0; + // SYNC STREAM +// if(!sync_video_packet(ds)) return 0; // cannot sync (EOF) + + // COPY STARTCODE: + packet_start=videobuf_len; + videobuffer[videobuf_len+0]=0; + videobuffer[videobuf_len+1]=0; + videobuffer[videobuf_len+2]=1; + videobuffer[videobuf_len+3]=next_nal; + videobuf_len+=4; + + // READ PACKET: + res = demux_pattern_3(ds, &videobuffer[videobuf_len], + VIDEOBUFFER_SIZE - videobuf_len, &read, 0x100); + videobuf_len += read; + if (!res) + goto eof_out; + + videobuf_len-=3; + + mp_dbg(MSGT_PARSEES,MSGL_DBG2,"videobuf: packet 0x1%02X len=%d (total=%d)\n",videobuffer[packet_start+3],videobuf_len-packet_start,videobuf_len); + + // Save next packet code: + next_nal = demux_getc(ds); + if (next_nal < 0) + goto eof_out; + videobuf_code_len=4; + + return videobuf_len-packet_start; + +eof_out: + next_nal = -1; + videobuf_code_len = 0; + return videobuf_len - packet_start; +} + +// return: next packet code +int skip_video_packet(demux_stream_t *ds){ + + // SYNC STREAM +// if(!sync_video_packet(ds)) return 0; // cannot sync (EOF) + + videobuf_code_len=0; // force resync + + // SYNC AGAIN: + return sync_video_packet(ds); +} + +/* stripped down version of a52_syncinfo() from liba52 + * copyright belongs to Michel Lespinasse <walken@zoy.org> + * and Aaron Holtzman <aholtzma@ess.engr.uvic.ca> */ +int mp_a52_framesize(uint8_t * buf, int *srate) +{ + int rate[] = { 32, 40, 48, 56, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 320, 384, 448, + 512, 576, 640 + }; + uint8_t halfrate[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 }; + int frmsizecod, bitrate, half; + + if ((buf[0] != 0x0b) || (buf[1] != 0x77)) /* syncword */ + return 0; + + if (buf[5] >= 0x60) /* bsid >= 12 */ + return 0; + + half = halfrate[buf[5] >> 3]; + + frmsizecod = buf[4] & 63; + if (frmsizecod >= 38) + return 0; + + bitrate = rate[frmsizecod >> 1]; + + switch (buf[4] & 0xc0) { + case 0: /* 48 KHz */ + *srate = 48000 >> half; + return 4 * bitrate; + case 0x40: /* 44.1 KHz */ + *srate = 44100 >> half; + return 2 * (320 * bitrate / 147 + (frmsizecod & 1)); + case 0x80: /* 32 KHz */ + *srate = 32000 >> half; + return 6 * bitrate; + } + + return 0; +} diff --git a/demux/parse_es.h b/demux/parse_es.h new file mode 100644 index 0000000000..ed76593e50 --- /dev/null +++ b/demux/parse_es.h @@ -0,0 +1,45 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_PARSE_ES_H +#define MPLAYER_PARSE_ES_H + +#include <stdint.h> + +#include "demuxer.h" + +#define MAX_VIDEO_PACKET_SIZE (224*1024+4) +#define VIDEOBUFFER_SIZE 0x100000 + +extern unsigned char* videobuffer; +extern int videobuf_len; +extern unsigned char videobuf_code[4]; +extern int videobuf_code_len; + +// sync video stream, and returns next packet code +int sync_video_packet(demux_stream_t *ds); + +// return: packet length +int read_video_packet(demux_stream_t *ds); + +// return: next packet code +int skip_video_packet(demux_stream_t *ds); + +int mp_a52_framesize(uint8_t *buf, int *srate); + +#endif /* MPLAYER_PARSE_ES_H */ diff --git a/demux/stheader.h b/demux/stheader.h new file mode 100644 index 0000000000..b5f11c0ecc --- /dev/null +++ b/demux/stheader.h @@ -0,0 +1,195 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_STHEADER_H +#define MPLAYER_STHEADER_H + +#include <stdbool.h> + +#include "aviheader.h" +#include "ms_hdr.h" +struct MPOpts; +struct demuxer; + +enum stream_type { + STREAM_VIDEO, + STREAM_AUDIO, + STREAM_SUB, + STREAM_TYPE_COUNT, +}; + +// Stream headers: + +struct sh_stream { + enum stream_type type; + struct demuxer *demuxer; + // Index into demuxer->streams. + int index; + // The (possibly) type specific id, e.g. aid or sid. + int tid; + // Index into stream array (currently one array per type, e.g. a_streams). + int stream_index; + // Demuxer specific ID (always set, defaults to tid). + int demuxer_id; + // Abomination. + struct sh_common *common_header; + // One of these is non-NULL, the others are NULL, depending on the stream + // type. + struct sh_audio *audio; + struct sh_video *video; + struct sh_sub *sub; + + // Work around other hacks. + int lavf_codec_tag; + + char *title; + bool default_track; + + // shouldn't exist type of stuff + struct MPOpts *opts; +}; + + +#define SH_COMMON \ + struct sh_stream *gsh; \ + const char *demuxer_codecname; \ + struct MPOpts *opts; \ + struct demux_stream *ds; \ + struct codecs *codec; \ + unsigned int format; \ + int libav_codec_id; \ + int initialized; \ + /* number of seconds stream should be delayed \ + * (according to dwStart or similar) */ \ + float stream_delay; \ + /* things needed for parsing */ \ + bool needs_parsing; \ + struct AVCodecContext *avctx; \ + struct AVCodecParserContext *parser; \ + /* audio: last known pts value in output from decoder \ + * video: predicted/interpolated PTS of the current frame */ \ + double pts; \ + /* decoder context */ \ + void *context; \ + const char *codecname; \ + char *lang; /* track language */ \ + +typedef struct sh_common { + SH_COMMON +} sh_common_t; + +typedef struct sh_audio { + SH_COMMON + int aid; + // output format: + int sample_format; + int samplerate; + int container_out_samplerate; + int samplesize; + int channels; + int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec) + int i_bps; // == bitrate (compressed bytes/sec) + // in buffers: + int audio_in_minsize; // initial size to allocate for a_in_buffer if any + char *a_in_buffer; // input buffer used by some decoders + int a_in_buffer_len; + int a_in_buffer_size; + // decoder buffers: + int audio_out_minsize; // minimal output from decoder may be this much + char *a_buffer; // buffer for decoder output + int a_buffer_len; + int a_buffer_size; + struct af_stream *afilter; // the audio filter stream + const struct ad_functions *ad_driver; + // win32-compatible codec parameters: + AVIStreamHeader audio; + WAVEFORMATEX *wf; + // note codec extradata may be either under "wf" or "codecdata" + unsigned char *codecdata; + int codecdata_len; + int pts_bytes; // bytes output by decoder after last known pts +} sh_audio_t; + +typedef struct sh_video { + SH_COMMON + int vid; + float timer; // absolute time in video stream, since last start/seek + // frame counters: + float num_frames; // number of frames played + int num_frames_decoded; // number of frames decoded + double i_pts; // PTS for the _next_ I/P frame (internal mpeg demuxing) + float next_frame_time; + double last_pts; + double buffered_pts[32]; + int num_buffered_pts; + double codec_reordered_pts; + double prev_codec_reordered_pts; + int num_reordered_pts_problems; + double sorted_pts; + double prev_sorted_pts; + int num_sorted_pts_problems; + int pts_assoc_mode; + // output format: (set by demuxer) + float fps; // frames per second (set only if constant fps) + float frametime; // 1/fps + float aspect; // aspect ratio stored in the file (for prescaling) + float stream_aspect; // aspect ratio in media headers (DVD IFO files) + int i_bps; // == bitrate (compressed bytes/sec) + int disp_w, disp_h; // display size (filled by demuxer) + int colorspace; // mp_csp + int color_range; // mp_csp_levels + // output driver/filters: (set by libmpcodecs core) + unsigned int outfmt; + unsigned int outfmtidx; + struct vf_instance *vfilter; // video filter chain + int output_flags; // query_format() results for output filters+vo + const struct vd_functions *vd_driver; + int vf_initialized; // -1 failed, 0 not done, 1 done + // win32-compatible codec parameters: + AVIStreamHeader video; + BITMAPINFOHEADER *bih; +} sh_video_t; + +typedef struct sh_sub { + SH_COMMON + int sid; + char type; // t = text, v = VobSub, a = SSA/ASS, m, x, b, d, p + bool active; // after track switch decoder may stay initialized, not active + unsigned char *extradata; // extra header data passed from demuxer + int extradata_len; + const struct sd_functions *sd_driver; +} sh_sub_t; + +// demuxer.c: +#define new_sh_audio(d, i) new_sh_audio_aid(d, i, i) +struct sh_audio *new_sh_audio_aid(struct demuxer *demuxer, int id, int aid); +#define new_sh_video(d, i) new_sh_video_vid(d, i, i) +struct sh_video *new_sh_video_vid(struct demuxer *demuxer, int id, int vid); +#define new_sh_sub(d, i) new_sh_sub_sid(d, i, i) +struct sh_sub *new_sh_sub_sid(struct demuxer *demuxer, int id, int sid); +struct sh_sub *new_sh_sub_sid_lang(struct demuxer *demuxer, int id, int sid, + const char *lang); + +const char *sh_sub_type2str(int type); + +// video.c: +int video_read_properties(struct sh_video *sh_video); +int video_read_frame(struct sh_video *sh_video, float *frame_time_ptr, + unsigned char **start, int force_fps); + +#endif /* MPLAYER_STHEADER_H */ diff --git a/demux/video.c b/demux/video.c new file mode 100644 index 0000000000..11e512f119 --- /dev/null +++ b/demux/video.c @@ -0,0 +1,622 @@ +/* + * video frame reading + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libavutil/mem.h> + +#include "mp_msg.h" + +#include "stream/stream.h" +#include "demuxer.h" +#include "stheader.h" +#include "parse_es.h" +#include "mpeg_hdr.h" + +/* sub_cc (closed captions)*/ +//#include "sub/sub_cc.h" + +/* biCompression constant */ +#define BI_RGB 0L + +static mp_mpeg_header_t picture; + +static int telecine=0; +static float telecine_cnt=-2.5; + +typedef enum { + VIDEO_MPEG12, + VIDEO_MPEG4, + VIDEO_H264, + VIDEO_VC1, + VIDEO_OTHER +} video_codec_t; + +static video_codec_t find_video_codec(sh_video_t *sh_video) +{ + demux_stream_t *d_video=sh_video->ds; + int fmt = d_video->demuxer->file_format; + + if( + (fmt == DEMUXER_TYPE_MPEG_ES) || + (fmt == DEMUXER_TYPE_MPEG_GXF) || + (fmt == DEMUXER_TYPE_MPEG_PES) || + ( + (fmt == DEMUXER_TYPE_MPEG_PS || fmt == DEMUXER_TYPE_MPEG_TS) && + ((! sh_video->format) || (sh_video->format==0x10000001) || (sh_video->format==0x10000002)) + ) + ) + return VIDEO_MPEG12; + else if((fmt == DEMUXER_TYPE_MPEG4_ES) || + ((fmt == DEMUXER_TYPE_MPEG_TS) && (sh_video->format==0x10000004)) || + ((fmt == DEMUXER_TYPE_MPEG_PS) && (sh_video->format==0x10000004)) + ) + return VIDEO_MPEG4; + else if((fmt == DEMUXER_TYPE_H264_ES) || + ((fmt == DEMUXER_TYPE_MPEG_TS) && (sh_video->format==0x10000005)) || + ((fmt == DEMUXER_TYPE_MPEG_PS) && (sh_video->format==0x10000005)) + ) + return VIDEO_H264; + else if((fmt == DEMUXER_TYPE_MPEG_PS || fmt == DEMUXER_TYPE_MPEG_TS) && + (sh_video->format==mmioFOURCC('W', 'V', 'C', '1'))) + return VIDEO_VC1; + else if (fmt == DEMUXER_TYPE_ASF && sh_video->bih && sh_video->bih->biCompression == mmioFOURCC('D', 'V', 'R', ' ')) + return VIDEO_MPEG12; + else + return VIDEO_OTHER; +} + +int video_read_properties(sh_video_t *sh_video){ +demux_stream_t *d_video=sh_video->ds; +video_codec_t video_codec = find_video_codec(sh_video); +// Determine image properties: +switch(video_codec){ + case VIDEO_OTHER: { + if((d_video->demuxer->file_format == DEMUXER_TYPE_ASF) || (d_video->demuxer->file_format == DEMUXER_TYPE_AVI)) { + // display info: + // in case no strf chunk has been seen in avi, we have no bitmap header + if(!sh_video->bih) return 0; + sh_video->format=sh_video->bih->biCompression; + sh_video->disp_w=sh_video->bih->biWidth; + sh_video->disp_h=abs(sh_video->bih->biHeight); + } + break; + } + case VIDEO_MPEG4: { + int pos = 0, vop_cnt=0, units[3]; + videobuf_len=0; videobuf_code_len=0; + mp_msg(MSGT_DECVIDEO,MSGL_V,"Searching for Video Object Start code... "); + while(1){ + int i=sync_video_packet(d_video); + if(i<=0x11F) break; // found it! + if(!i || !skip_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + return 0; + } + } + mp_msg(MSGT_DECVIDEO,MSGL_V,"OK!\n"); + if(!videobuffer) { + videobuffer = av_malloc(VIDEOBUFFER_SIZE + MP_INPUT_BUFFER_PADDING_SIZE); + if (videobuffer) memset(videobuffer+VIDEOBUFFER_SIZE, 0, MP_INPUT_BUFFER_PADDING_SIZE); + else { + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"Cannot allocate shared memory.\n"); + return 0; + } + } + mp_msg(MSGT_DECVIDEO,MSGL_V,"Searching for Video Object Layer Start code... "); + while(1){ + int i=sync_video_packet(d_video); + mp_msg(MSGT_DECVIDEO,MSGL_V,"M4V: 0x%X\n",i); + if(i>=0x120 && i<=0x12F) break; // found it! + if(!i || !read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + return 0; + } + } + pos = videobuf_len+4; + if(!read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Can't read Video Object Layer Header\n"); + return 0; + } + mp4_header_process_vol(&picture, &(videobuffer[pos])); + mp_msg(MSGT_DECVIDEO,MSGL_V,"OK! FPS SEEMS TO BE %.3f\nSearching for Video Object Plane Start code... ", sh_video->fps); + mp4_init: + while(1){ + int i=sync_video_packet(d_video); + if(i==0x1B6) break; // found it! + if(!i || !read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + return 0; + } + } + pos = videobuf_len+4; + if(!read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Can't read Video Object Plane Header\n"); + return 0; + } + mp4_header_process_vop(&picture, &(videobuffer[pos])); + sh_video->disp_w = picture.display_picture_width; + sh_video->disp_h = picture.display_picture_height; + units[vop_cnt] = picture.timeinc_unit; + vop_cnt++; + //mp_msg(MSGT_DECVIDEO,MSGL_V, "TYPE: %d, unit: %d\n", picture.picture_type, picture.timeinc_unit); + if(!picture.fps) { + int i, mn, md, mx, diff; + if(vop_cnt < 3) + goto mp4_init; + + i=0; + mn = mx = units[0]; + for(i=0; i<3; i++) { + if(units[i] < mn) + mn = units[i]; + if(units[i] > mx) + mx = units[i]; + } + md = mn; + for(i=0; i<3; i++) { + if((units[i] > mn) && (units[i] < mx)) + md = units[i]; + } + mp_msg(MSGT_DECVIDEO,MSGL_V, "MIN: %d, mid: %d, max: %d\n", mn, md, mx); + if(mx - md > md - mn) + diff = md - mn; + else + diff = mx - md; + if(diff > 0){ + picture.fps = ((float)picture.timeinc_resolution) / diff; + mp_msg(MSGT_DECVIDEO,MSGL_V, "FPS seems to be: %f, resolution: %d, delta_units: %d\n", picture.fps, picture.timeinc_resolution, diff); + } + } + if(picture.fps) { + sh_video->fps=picture.fps; + sh_video->frametime=1.0/picture.fps; + mp_msg(MSGT_DECVIDEO,MSGL_INFO, "FPS seems to be: %f\n", picture.fps); + } + mp_msg(MSGT_DECVIDEO,MSGL_V,"OK!\n"); + sh_video->format=0x10000004; + break; + } + case VIDEO_H264: { + int pos = 0; + videobuf_len=0; videobuf_code_len=0; + mp_msg(MSGT_DECVIDEO,MSGL_V,"Searching for sequence parameter set... "); + while(1){ + int i=sync_video_packet(d_video); + if((i&~0x60) == 0x107 && i != 0x107) break; // found it! + if(!i || !skip_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + return 0; + } + } + mp_msg(MSGT_DECVIDEO,MSGL_V,"OK!\n"); + if(!videobuffer) { + videobuffer = av_malloc(VIDEOBUFFER_SIZE + MP_INPUT_BUFFER_PADDING_SIZE); + if (videobuffer) memset(videobuffer+VIDEOBUFFER_SIZE, 0, MP_INPUT_BUFFER_PADDING_SIZE); + else { + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"Cannot allocate shared memory.\n"); + return 0; + } + } + pos = videobuf_len+4; + if(!read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Can't read sequence parameter set\n"); + return 0; + } + h264_parse_sps(&picture, &(videobuffer[pos]), videobuf_len - pos); + sh_video->disp_w=picture.display_picture_width; + sh_video->disp_h=picture.display_picture_height; + mp_msg(MSGT_DECVIDEO,MSGL_V,"Searching for picture parameter set... "); + while(1){ + int i=sync_video_packet(d_video); + mp_msg(MSGT_DECVIDEO,MSGL_V,"H264: 0x%X\n",i); + if((i&~0x60) == 0x108 && i != 0x108) break; // found it! + if(!i || !read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + return 0; + } + } + mp_msg(MSGT_DECVIDEO,MSGL_V,"OK!\nSearching for Slice... "); + while(1){ + int i=sync_video_packet(d_video); + if((i&~0x60) == 0x101 || (i&~0x60) == 0x102 || (i&~0x60) == 0x105) break; // found it! + if(!i || !read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + return 0; + } + } + mp_msg(MSGT_DECVIDEO,MSGL_V,"OK!\n"); + sh_video->format=0x10000005; + if(picture.fps) { + sh_video->fps=picture.fps; + sh_video->frametime=1.0/picture.fps; + mp_msg(MSGT_DECVIDEO,MSGL_INFO, "FPS seems to be: %f\n", picture.fps); + } + break; + } + case VIDEO_MPEG12: { + if (d_video->demuxer->file_format == DEMUXER_TYPE_ASF) { // DVR-MS + if(!sh_video->bih) return 0; + sh_video->format=sh_video->bih->biCompression; + } +mpeg_header_parser: + // Find sequence_header first: + videobuf_len=0; videobuf_code_len=0; + telecine=0; telecine_cnt=-2.5; + mp_msg(MSGT_DECVIDEO,MSGL_V,"Searching for sequence header... "); + while(1){ + int i=sync_video_packet(d_video); + if(i==0x1B3) break; // found it! + if(!i || !skip_video_packet(d_video)){ + if( mp_msg_test(MSGT_DECVIDEO,MSGL_V) ) mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"MPEG: FATAL: EOF while searching for sequence header.\n"); + return 0; + } + } + mp_msg(MSGT_DECVIDEO,MSGL_V,"OK!\n"); + // ========= Read & process sequence header & extension ============ + if(!videobuffer) { + videobuffer = av_malloc(VIDEOBUFFER_SIZE + MP_INPUT_BUFFER_PADDING_SIZE); + if (videobuffer) memset(videobuffer+VIDEOBUFFER_SIZE, 0, MP_INPUT_BUFFER_PADDING_SIZE); + else { + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"Cannot allocate shared memory.\n"); + return 0; + } + } + + if(!read_video_packet(d_video)){ + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"FATAL: Cannot read sequence header.\n"); + return 0; + } + if(mp_header_process_sequence_header (&picture, &videobuffer[4])) { + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"MPEG: bad sequence header\n"); + goto mpeg_header_parser; + } + if(sync_video_packet(d_video)==0x1B5){ // next packet is seq. ext. + int pos=videobuf_len; + if(!read_video_packet(d_video)){ + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"FATAL: Cannot read sequence header extension.\n"); + return 0; + } + if(mp_header_process_extension (&picture, &videobuffer[pos+4])) { + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"MPEG: bad sequence header extension\n"); + return 0; + } + } + + // display info: + sh_video->format=picture.mpeg1?0x10000001:0x10000002; // mpeg video + sh_video->fps=picture.fps * picture.frame_rate_extension_n / picture.frame_rate_extension_d; + if(!sh_video->fps){ + sh_video->frametime=0; + } else { + sh_video->frametime=1.0/sh_video->fps; + } + sh_video->disp_w=picture.display_picture_width; + sh_video->disp_h=picture.display_picture_height; + // bitrate: + if(picture.bitrate!=0x3FFFF) // unspecified/VBR ? + sh_video->i_bps=picture.bitrate * 400 / 8; + // info: + mp_dbg(MSGT_DECVIDEO,MSGL_DBG2,"mpeg bitrate: %d (%X)\n",picture.bitrate,picture.bitrate); + mp_msg(MSGT_DECVIDEO,MSGL_INFO,"VIDEO: %s %dx%d (aspect %d) %5.3f fps %5.1f kbps (%4.1f kbyte/s)\n", + picture.mpeg1?"MPEG1":"MPEG2", + sh_video->disp_w,sh_video->disp_h, + picture.aspect_ratio_information, + sh_video->fps, + sh_video->i_bps * 8 / 1000.0, + sh_video->i_bps / 1000.0 ); + break; + } + case VIDEO_VC1: { + // Find sequence_header: + videobuf_len=0; + videobuf_code_len=0; + mp_msg(MSGT_DECVIDEO,MSGL_INFO,"Searching for VC1 sequence header... "); + while(1){ + int i=sync_video_packet(d_video); + if(i==0x10F) break; // found it! + if(!i || !skip_video_packet(d_video)){ + if( mp_msg_test(MSGT_DECVIDEO,MSGL_V) ) mp_msg(MSGT_DECVIDEO,MSGL_V,"NONE :(\n"); + mp_msg(MSGT_DECVIDEO,MSGL_ERR, "Couldn't find VC-1 sequence header\n"); + return 0; + } + } + mp_msg(MSGT_DECVIDEO,MSGL_INFO,"found\n"); + if(!videobuffer) { + videobuffer = av_malloc(VIDEOBUFFER_SIZE + MP_INPUT_BUFFER_PADDING_SIZE); + if (videobuffer) memset(videobuffer+VIDEOBUFFER_SIZE, 0, MP_INPUT_BUFFER_PADDING_SIZE); + else { + mp_tmsg(MSGT_DECVIDEO,MSGL_ERR,"Cannot allocate shared memory.\n"); + return 0; + } + } + if(!read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_ERR, "Couldn't read VC-1 sequence header!\n"); + return 0; + } + + while(1) { + int i=sync_video_packet(d_video); + if(i==0x10E) break; // found it! + if(!i || !skip_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"Couldn't find VC-1 entry point sync-code:(\n"); + return 0; + } + } + if(!read_video_packet(d_video)){ + mp_msg(MSGT_DECVIDEO,MSGL_V,"Couldn't read VC-1 entry point sync-code:(\n"); + return 0; + } + + if(mp_vc1_decode_sequence_header(&picture, &videobuffer[4], videobuf_len-4)) { + sh_video->bih = calloc(1, sizeof(*sh_video->bih) + videobuf_len); + if(sh_video->bih == NULL) { + mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Couldn't alloc %zu bytes for VC-1 extradata!\n", sizeof(*sh_video->bih) + videobuf_len); + return 0; + } + sh_video->bih->biSize= sizeof(*sh_video->bih) + videobuf_len; + memcpy(sh_video->bih + 1, videobuffer, videobuf_len); + sh_video->bih->biCompression = sh_video->format; + sh_video->bih->biWidth = sh_video->disp_w = picture.display_picture_width; + sh_video->bih->biHeight = sh_video->disp_h = picture.display_picture_height; + if(picture.fps > 0) { + sh_video->frametime=1.0/picture.fps; + sh_video->fps = picture.fps; + } + mp_msg(MSGT_DECVIDEO,MSGL_INFO,"VIDEO: VC-1 %dx%d, %5.3f fps, header len: %d\n", + sh_video->disp_w, sh_video->disp_h, sh_video->fps, videobuf_len); + } + break; + } +} // switch(file_format) + +return 1; +} + +static void process_userdata(const unsigned char* buf,int len){ + int i; + /* if the user data starts with "CC", assume it is a CC info packet */ + if(len>2 && buf[0]=='C' && buf[1]=='C'){ +// mp_msg(MSGT_DECVIDEO,MSGL_DBG2,"video.c: process_userdata() detected Closed Captions!\n"); + //subcc_process_data(buf+2,len-2); + } + if(verbose<2) return; + fprintf(stderr, "user_data: len=%3d %02X %02X %02X %02X '", + len, buf[0], buf[1], buf[2], buf[3]); + for(i=0;i<len;i++) +// if(buf[i]>=32 && buf[i]<127) fputc(buf[i], stderr); + if(buf[i]&0x60) fputc(buf[i]&0x7F, stderr); + fprintf(stderr, "'\n"); +} + +int video_read_frame(sh_video_t* sh_video,float* frame_time_ptr,unsigned char** start,int force_fps){ + demux_stream_t *d_video=sh_video->ds; + demuxer_t *demuxer=d_video->demuxer; + float frame_time=1; + float pts1=d_video->pts; + float pts=0; + float fps; + int picture_coding_type=0; + int in_size=0; + video_codec_t video_codec = find_video_codec(sh_video); + + *start=NULL; + + if(video_codec == VIDEO_MPEG12){ + int in_frame=0; + //float newfps; + //videobuf_len=0; + while(videobuf_len<VIDEOBUFFER_SIZE-MAX_VIDEO_PACKET_SIZE){ + int i=sync_video_packet(d_video); + //void* buffer=&videobuffer[videobuf_len+4]; + int start=videobuf_len+4; + if(in_frame){ + if(i<0x101 || i>=0x1B0){ // not slice code -> end of frame + if(!i) return -1; // EOF + break; + } + } else { + if(i==0x100){ + pts=d_video->pts; + d_video->pts=0; + } + if(i>=0x101 && i<0x1B0) in_frame=1; // picture startcode + else if(!i) return -1; // EOF + } + if(!read_video_packet(d_video)) return -1; // EOF + // process headers: + switch(i){ + case 0x1B3: mp_header_process_sequence_header (&picture, &videobuffer[start]);break; + case 0x1B5: mp_header_process_extension (&picture, &videobuffer[start]);break; + case 0x1B2: process_userdata (&videobuffer[start], videobuf_len-start);break; + case 0x100: picture_coding_type=(videobuffer[start+1] >> 3) & 7;break; + } + } + fps = picture.fps * picture.frame_rate_extension_n / picture.frame_rate_extension_d; + + *start=videobuffer; in_size=videobuf_len; + + // get mpeg fps: + if(sh_video->fps!=fps) if(!force_fps && !telecine){ + mp_msg(MSGT_CPLAYER,MSGL_WARN,"Warning! FPS changed %5.3f -> %5.3f (%f) [%d] \n",sh_video->fps,fps,sh_video->fps-fps,picture.frame_rate_code); + sh_video->fps=fps; + sh_video->frametime=1.0/fps; + } + + // fix mpeg2 frametime: + frame_time=(picture.display_time)*0.01f; + picture.display_time=100; + videobuf_len=0; + + telecine_cnt*=0.9; // drift out error + telecine_cnt+=frame_time-5.0/4.0; + mp_msg(MSGT_DECVIDEO,MSGL_DBG2,"\r telecine = %3.1f %5.3f \n",frame_time,telecine_cnt); + + if(telecine){ + frame_time=1; + if(telecine_cnt<-1.5 || telecine_cnt>1.5){ + mp_tmsg(MSGT_DECVIDEO,MSGL_INFO,"\ndemux_mpg: 30000/1001fps NTSC content detected, switching framerate.\n"); + telecine=0; + } + } else + if(telecine_cnt>-0.5 && telecine_cnt<0.5 && !force_fps){ + sh_video->fps=sh_video->fps*4/5; + sh_video->frametime=sh_video->frametime*5/4; + mp_tmsg(MSGT_DECVIDEO,MSGL_INFO,"\ndemux_mpg: 24000/1001fps progressive NTSC content detected, switching framerate.\n"); + telecine=1; + } + } else if(video_codec == VIDEO_MPEG4){ + while(videobuf_len<VIDEOBUFFER_SIZE-MAX_VIDEO_PACKET_SIZE){ + int i=sync_video_packet(d_video); + if(!i) return -1; + if(!read_video_packet(d_video)) return -1; // EOF + if(i==0x1B6) break; + } + *start=videobuffer; in_size=videobuf_len; + videobuf_len=0; + } else if(video_codec == VIDEO_H264){ + int in_picture = 0; + while(videobuf_len<VIDEOBUFFER_SIZE-MAX_VIDEO_PACKET_SIZE){ + int i=sync_video_packet(d_video); + int pos = videobuf_len+4; + if(!i) return -1; + if(!read_video_packet(d_video)) return -1; // EOF + if((i&~0x60) == 0x107 && i != 0x107) { + h264_parse_sps(&picture, &(videobuffer[pos]), videobuf_len - pos); + if(picture.fps > 0) { + sh_video->fps=picture.fps; + sh_video->frametime=1.0/picture.fps; + } + i=sync_video_packet(d_video); + if(!i) return -1; + if(!read_video_packet(d_video)) return -1; // EOF + } + + // here starts the access unit end detection code + // see the mail on MPlayer-dev-eng for details: + // Date: Sat, 17 Sep 2005 11:24:06 +0200 + // Subject: Re: [MPlayer-dev-eng] [RFC] h264 ES parser problems + // Message-ID: <20050917092406.GA7699@rz.uni-karlsruhe.de> + if((i&~0x60) == 0x101 || (i&~0x60) == 0x102 || (i&~0x60) == 0x105) + // found VCL NAL with slice header i.e. start of current primary coded + // picture, so start scanning for the end now + in_picture = 1; + if (in_picture) { + i = sync_video_packet(d_video) & ~0x60; // code of next packet + if(i == 0x106 || i == 0x109) break; // SEI or access unit delim. + if(i == 0x101 || i == 0x102 || i == 0x105) { + // assuming arbitrary slice ordering is not allowed, the + // first_mb_in_slice (golomb encoded) value should be 0 then + // for the first VCL NAL in a picture + if (demux_peekc(d_video) & 0x80) + break; + } + } + } + *start=videobuffer; in_size=videobuf_len; + videobuf_len=0; + } else if(video_codec == VIDEO_VC1) { + while(videobuf_len<VIDEOBUFFER_SIZE-MAX_VIDEO_PACKET_SIZE) { + int i=sync_video_packet(d_video); + if(!i) return -1; + if(!read_video_packet(d_video)) return -1; // EOF + if(i==0x10D) break; + } + *start=videobuffer; + in_size=videobuf_len; + videobuf_len=0; + } else { + // frame-based file formats: (AVI,ASF,MOV) + in_size=ds_get_packet(d_video,start); + if(in_size<0) return -1; // EOF + } + + +//------------------------ frame decoded. -------------------- + + // Increase video timers: + sh_video->num_frames+=frame_time; + ++sh_video->num_frames_decoded; + + frame_time*=sh_video->frametime; + + // override frame_time for variable/unknown FPS formats: + if(!force_fps) switch(demuxer->file_format){ + case DEMUXER_TYPE_GIF: + case DEMUXER_TYPE_MATROSKA: + case DEMUXER_TYPE_MNG: + if(d_video->pts>0 && pts1>0 && d_video->pts>pts1) + frame_time=d_video->pts-pts1; + break; + case DEMUXER_TYPE_TV: + case DEMUXER_TYPE_ASF: { + double next_pts = ds_get_next_pts(d_video); + double d= (next_pts != MP_NOPTS_VALUE) ? next_pts - d_video->pts : d_video->pts-pts1; + if(d>=0){ + if(d>0){ + if((int)sh_video->fps==1000) + mp_msg(MSGT_CPLAYER,MSGL_V,"\navg. framerate: %d fps \n",(int)(1.0f/d)); + sh_video->frametime=d; // 1ms + sh_video->fps=1.0f/d; + } + frame_time = d; + } else { + mp_msg(MSGT_CPLAYER,MSGL_WARN,"\nInvalid frame duration value (%5.3f/%5.3f => %5.3f). Defaulting to %5.3f sec.\n",d_video->pts,next_pts,d,frame_time); + // frame_time = 1/25.0; + } + } + break; + case DEMUXER_TYPE_LAVF: + case DEMUXER_TYPE_LAVF_PREFERRED: + if((int)sh_video->fps==1000 || (int)sh_video->fps<=1){ + double next_pts = ds_get_next_pts(d_video); + double d= (next_pts != MP_NOPTS_VALUE) ? next_pts - d_video->pts : d_video->pts-pts1; + if(d>=0){ + frame_time = d; + } + } + break; + } + + if(video_codec == VIDEO_MPEG12){ + sh_video->pts+=frame_time; + if(picture_coding_type==1) + d_video->keyframe = true; + if(picture_coding_type<=2 && sh_video->i_pts){ + sh_video->pts=sh_video->i_pts; + sh_video->i_pts=pts; + } else { + if(pts){ + if(picture_coding_type<=2) sh_video->i_pts=pts; + else sh_video->pts=pts; + } + } + } else + sh_video->pts=d_video->pts; + + if(frame_time_ptr) *frame_time_ptr=frame_time; + return in_size; +} |