summaryrefslogtreecommitdiffstats
path: root/demux
diff options
context:
space:
mode:
Diffstat (limited to 'demux')
-rw-r--r--demux/asf.h251
-rw-r--r--demux/asfguid.h89
-rw-r--r--demux/asfheader.c717
-rw-r--r--demux/asfheader.h28
-rw-r--r--demux/aviheader.c674
-rw-r--r--demux/aviheader.h382
-rw-r--r--demux/aviprint.c197
-rw-r--r--demux/aviprint.h35
-rw-r--r--demux/demux.c1435
-rw-r--r--demux/demux.h414
-rw-r--r--demux/demux_asf.c697
-rw-r--r--demux/demux_avi.c909
-rw-r--r--demux/demux_cue.c65
-rw-r--r--demux/demux_edl.c58
-rw-r--r--demux/demux_gif.c338
-rw-r--r--demux/demux_lavf.c1092
-rw-r--r--demux/demux_mf.c226
-rw-r--r--demux/demux_mkv.c2558
-rw-r--r--demux/demux_mng.c623
-rw-r--r--demux/demux_mpg.c1248
-rw-r--r--demux/demux_packet.h40
-rw-r--r--demux/demux_rawaudio.c127
-rw-r--r--demux/demux_rawvideo.c172
-rw-r--r--demux/demux_ts.c3537
-rw-r--r--demux/demux_ts.h24
-rw-r--r--demux/ebml.c670
-rw-r--r--demux/ebml.h107
-rw-r--r--demux/extension.c104
-rw-r--r--demux/matroska.h86
-rw-r--r--demux/mf.c171
-rw-r--r--demux/mf.h36
-rw-r--r--demux/mp3_hdr.c144
-rw-r--r--demux/mp3_hdr.h36
-rw-r--r--demux/mp_taglists.c158
-rw-r--r--demux/mp_taglists.h28
-rw-r--r--demux/mpeg_hdr.c539
-rw-r--r--demux/mpeg_hdr.h55
-rw-r--r--demux/ms_hdr.h114
-rw-r--r--demux/parse_es.c158
-rw-r--r--demux/parse_es.h45
-rw-r--r--demux/stheader.h195
-rw-r--r--demux/video.c622
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, &params);
+
+ 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;