diff options
Diffstat (limited to 'libmpdemux')
35 files changed, 7101 insertions, 0 deletions
diff --git a/libmpdemux/asf.h b/libmpdemux/asf.h new file mode 100644 index 0000000000..8d35563cda --- /dev/null +++ b/libmpdemux/asf.h @@ -0,0 +1,200 @@ +#ifndef __ASF_H +#define __ASF_H + +#include "config.h" /* for WORDS_BIGENDIAN */ +#include <inttypes.h> +#include "bswap.h" +#ifdef STREAMING +#include "network.h" +#endif + +#ifndef MIN +#define MIN(a,b) ((a<b)?a:b) +#endif + +/////////////////////// +// MS GUID definition +/////////////////////// +#ifndef GUID_DEFINED +#define GUID_DEFINED +// Size of GUID is 16 bytes! +typedef struct __attribute__((packed)) { + uint32_t Data1; // 4 bytes + uint16_t Data2; // 2 bytes + uint16_t Data3; // 2 bytes + uint8_t Data4[8]; // 8 bytes +} GUID_t; +#endif + +/////////////////////// +// 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 client[16]; // Client GUID + uint64_t file_size; + uint64_t creat_time; //File creation time FILETIME 8 + uint64_t packets; //Number of packets UINT64 8 + uint64_t end_timestamp; //Timestamp of the end position UINT64 8 + uint64_t duration; //Duration of the playback UINT64 8 + uint32_t start_timestamp; //Timestamp of the start position UINT32 4 + uint32_t unk1; //Unknown, maybe reserved ( usually contains 0 ) UINT32 4 + uint32_t flags; //Unknown, maybe flags ( usually contains 2 ) UINT32 4 + uint32_t packetsize; //Size of packet, in bytes UINT32 4 + uint32_t packetsize2; //Size of packet ( confirm ) UINT32 4 + uint32_t frame_size; //Size of uncompressed video frame UINT32 4 +} 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 differents type of ASF streaming +typedef enum { + ASF_Unknown_e, + ASF_Live_e, + ASF_Prerecorded_e, + ASF_Redirector_e +} ASF_StreamType_e; + + +/* + * Some macros to swap little endian structures read from an ASF file + * into machine endian format + */ +#ifdef WORDS_BIGENDIAN +#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)->creat_time = le2me_64((h)->creat_time); \ + (h)->packets = le2me_64((h)->packets); \ + (h)->end_timestamp = le2me_64((h)->end_timestamp); \ + (h)->duration = le2me_64((h)->duration); \ + (h)->start_timestamp = le2me_32((h)->start_timestamp); \ + (h)->unk1 = le2me_32((h)->unk1); \ + (h)->flags = le2me_32((h)->flags); \ + (h)->packetsize = le2me_32((h)->packetsize); \ + (h)->packetsize2 = le2me_32((h)->packetsize2); \ + (h)->frame_size = le2me_32((h)->frame_size); \ +} +#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); \ +} +#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) /**/ +#endif + + +#ifdef STREAMING +int asf_http_streaming_type(char *content_type, char *features); +int asf_http_streaming_start( streaming_ctrl_t *streaming_ctrl ); +int asf_http_streaming_read( streaming_ctrl_t *streaming_ctrl ); + +int asf_streaming(char *data, int length, int *drop_packet ); +#endif + +#endif diff --git a/libmpdemux/asf_streaming.c b/libmpdemux/asf_streaming.c new file mode 100644 index 0000000000..5205a143b6 --- /dev/null +++ b/libmpdemux/asf_streaming.c @@ -0,0 +1,353 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "url.h" +#include "http.h" +#include "asf.h" +#include "network.h" + +#include "stream.h" +//#include "demuxer.h" + +//extern demuxer_t *demuxer; + +static ASF_StreamType_e streaming_type = ASF_Unknown_e; + +int +asf_http_streaming_read( streaming_ctrl_t *streaming_ctrl ) { + char *buffer; + int drop_packet; + int ret; +printf("asf_streaming_read\n"); + ret = asf_streaming( streaming_ctrl->buffer->buffer, streaming_ctrl->buffer->length, &drop_packet ); +printf("ret: %d\n", ret); + if( ret<0 ) return -1; + if( ret>streaming_ctrl->buffer->length ) return 0; + buffer = (char*)malloc(ret); + if( buffer==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } +printf("buffer length: %d\n", streaming_ctrl->buffer->length ); + net_fifo_pop( streaming_ctrl->buffer, buffer, ret ); +printf(" pop: 0x%02X\n", *((unsigned int*)buffer) ); +printf("buffer length: %d\n", streaming_ctrl->buffer->length ); +printf("0x%02X\n", *((unsigned int*)(buffer+sizeof(ASF_stream_chunck_t))) ); + if( !drop_packet ) { + write( streaming_ctrl->fd_pipe_in, buffer+sizeof(ASF_stream_chunck_t), ret-sizeof(ASF_stream_chunck_t) ); + } + free( buffer ); + return ret; +} + +int +asf_streaming(char *data, int length, int *drop_packet ) { + ASF_stream_chunck_t *stream_chunck=(ASF_stream_chunck_t*)data; + printf("ASF stream chunck size=%d\n", stream_chunck->size); +printf("length: %d\n", length ); +printf("0x%02X\n", stream_chunck->type ); + + if( drop_packet!=NULL ) *drop_packet = 0; + if( data==NULL || length<=0 ) return -1; + + if( stream_chunck->size<8 ) { + printf("Ahhhh, stream_chunck size is too small: %d\n", stream_chunck->size); + return -1; + } + if( stream_chunck->size!=stream_chunck->size_confirm ) { + printf("size_confirm mismatch!: %d %d\n", stream_chunck->size, stream_chunck->size_confirm); + return -1; + } + + printf(" type: 0x%02X\n", stream_chunck->type ); + printf(" size: %d (0x%02X)\n", stream_chunck->size, stream_chunck->size ); + printf(" sequence_number: 0x%04X\n", stream_chunck->sequence_number ); + printf(" unknown: 0x%02X\n", stream_chunck->unknown ); + printf(" size_confirm: 0x%02X\n", stream_chunck->size_confirm ); + + + switch(stream_chunck->type) { + case 0x4324: // Clear ASF configuration + printf("=====> Clearing ASF stream configuration!\n"); + if( drop_packet!=NULL ) *drop_packet = 1; + return stream_chunck->size; + break; + case 0x4424: // Data follows + printf("=====> Data follows\n"); + break; + case 0x4524: // Transfer complete + printf("=====> Transfer complete\n"); + if( drop_packet!=NULL ) *drop_packet = 1; + return stream_chunck->size; + break; + case 0x4824: // ASF header chunk follows + printf("=====> ASF header chunk follows\n"); + break; + default: + printf("=====> Unknown stream type 0x%x\n", stream_chunck->type ); + } + return stream_chunck->size+4; +} + +int +asf_http_streaming_type(char *content_type, char *features) { + if( content_type==NULL ) return ASF_Unknown_e; + if( !strcasecmp(content_type, "application/octet-stream") ) { + if( features==NULL ) { + printf("=====> ASF Prerecorded\n"); + return ASF_Prerecorded_e; + } else if( strstr(features, "broadcast")) { + printf("=====> ASF Live stream\n"); + return ASF_Live_e; + } else { + printf("=====> ASF Prerecorded\n"); + return ASF_Prerecorded_e; + } + } else { + if( (!strcasecmp(content_type, "audio/x-ms-wax")) || + (!strcasecmp(content_type, "audio/x-ms-wma")) || + (!strcasecmp(content_type, "video/x-ms-asf")) || + (!strcasecmp(content_type, "video/x-ms-afs")) || + (!strcasecmp(content_type, "video/x-ms-wvx")) || + (!strcasecmp(content_type, "video/x-ms-wmv")) || + (!strcasecmp(content_type, "video/x-ms-wma")) ) { + printf("=====> ASF Redirector\n"); + return ASF_Redirector_e; + } else { + printf("=====> ASF unknown content-type: %s\n", content_type ); + return ASF_Unknown_e; + } + } + return ASF_Unknown_e; +} + +HTTP_header_t * +asf_http_request(URL_t *url) { + HTTP_header_t *http_hdr; + char str[250]; + char *ptr; + char *request; + int i; + + int offset_hi=0, offset_lo=0, req_nb=1, length=0; + int asf_nb_stream; + + // Common header for all requests. + http_hdr = http_new_header(); + http_set_uri( http_hdr, url->file ); + http_set_field( http_hdr, "Accept: */*" ); + http_set_field( http_hdr, "User-Agent: NSPlayer/4.1.0.3856" ); + sprintf( str, "Host: %s:%d", url->hostname, url->port ); + http_set_field( http_hdr, str ); + http_set_field( http_hdr, "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}" ); + sprintf(str, + "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=%u", + offset_hi, offset_lo, req_nb, length ); + http_set_field( http_hdr, str ); + + switch( streaming_type ) { + case ASF_Live_e: + case ASF_Prerecorded_e: + http_set_field( http_hdr, "Pragma: xPlayStrm=1" ); + ptr = str; + ptr += sprintf( ptr, "Pragma: stream-switch-entry="); + +// FIXME: why do you need demuxer here? if you really need it, pass it as +// parameter. -- A'rpi + +#if 0 + for( i=0, asf_nb_stream=0 ; i<256 ; i++ ) { + // FIXME START + if( demuxer==NULL ) { + ptr += sprintf( ptr, " ffff:1:0" ); + asf_nb_stream = 1; + break; + } + // FIXME END + if( demuxer->a_streams[i] ) { + ptr += sprintf( ptr, " ffff:%d:0", i ); + asf_nb_stream++; + } + if( demuxer->v_streams[i] ) { + ptr += sprintf( ptr, " ffff:%d:0", i ); + asf_nb_stream++; + } + } +#endif + http_set_field( http_hdr, str ); + sprintf( str, "Pragma: stream-switch-count=%d", asf_nb_stream ); + http_set_field( http_hdr, str ); + break; + case ASF_Redirector_e: + break; + case ASF_Unknown_e: + // First request goes here. + break; + default: + printf("Unknown asf stream type\n"); + } + + http_set_field( http_hdr, "Connection: Close" ); + http_build_request( http_hdr ); + + return http_hdr; +} + +int +asf_http_parse_response( HTTP_header_t *http_hdr ) { + char *content_type, *pragma; + char features[64] = "\0"; + int len; + if( http_response_parse(http_hdr)<0 ) { + printf("Failed to parse HTTP response\n"); + return -1; + } + if( http_hdr->status_code!=200 ) { + printf("Server return %d:%s\n", http_hdr->status_code, http_hdr->reason_phrase); + return -1; + } + + content_type = http_get_field( http_hdr, "Content-Type"); + + pragma = http_get_field( http_hdr, "Pragma"); + while( pragma!=NULL ) { + char *comma_ptr=NULL; + char *end; + // The pragma line can get severals attributes + // separeted with a comma ','. + do { + if( !strncasecmp( pragma, "features=", 9) ) { + pragma += 9; + end = strstr( pragma, "," ); + if( end==NULL ) { + len = strlen(pragma); + } + len = MIN(end-pragma,sizeof(features)); + strncpy( features, pragma, len ); + features[len]='\0'; + break; + } + comma_ptr = strstr( pragma, "," ); + if( comma_ptr!=NULL ) { + pragma = comma_ptr+1; + if( pragma[0]==' ' ) pragma++; + } + } while( comma_ptr!=NULL ); + pragma = http_get_next_field( http_hdr ); + } + + streaming_type = asf_http_streaming_type( content_type, features ); + + if( http_hdr->body_size>0 ) { + asf_streaming( http_hdr->body, http_hdr->body_size, NULL); + } + + return 0; +} + +URL_t * +asf_http_ASX_redirect( HTTP_header_t *http_hdr ) { + URL_t *url_redirect=NULL; + printf("=========>> ASX parser not yet implemented <<==========\n"); + + printf("ASX=[%s]\n", http_hdr->body ); + + return url_redirect; +} + +int +asf_http_streaming_start( streaming_ctrl_t *streaming_ctrl ) { + HTTP_header_t *http_hdr=NULL; + URL_t *url_next=NULL; + URL_t *url = *(streaming_ctrl->url); + char buffer[BUFFER_SIZE]; + int i; + int fd = streaming_ctrl->fd_net; + int done=1; + +streaming_type = ASF_Live_e; + do { + if( fd>0 ) close( fd ); + + fd = connect2Server( url->hostname, url->port ); + if( fd<0 ) return -1; + + http_hdr = asf_http_request( url ); +printf("[%s]\n", http_hdr->buffer ); + write( fd, http_hdr->buffer, http_hdr->buffer_size ); +// http_free( http_hdr ); + + http_hdr = http_new_header(); + do { + i = readFromServer( fd, buffer, BUFFER_SIZE ); +printf("read: %d\n", i ); + if( i<0 ) { + perror("read"); + http_free( http_hdr ); + return -1; + } + http_response_append( http_hdr, buffer, i ); + } while( !http_is_header_entired( http_hdr ) ); +//http_hdr->buffer[http_hdr->buffer_len]='\0'; +//printf("[%s]\n", http_hdr->buffer ); + if( asf_http_parse_response(http_hdr)<0 ) { + printf("Failed to parse header\n"); + http_free( http_hdr ); + return -1; + } + switch( streaming_type ) { + case ASF_Live_e: + case ASF_Prerecorded_e: + if( http_hdr->body_size>0 ) { +printf("--- 0x%02X\n", streaming_ctrl->buffer ); + net_fifo_push( streaming_ctrl->buffer, http_hdr->body, http_hdr->body_size ); + } else { + ASF_stream_chunck_t *ptr; + int ret; + i = readFromServer( fd, buffer, sizeof(ASF_stream_chunck_t) ); +printf("read: %d\n", i ); + ret = asf_streaming( buffer, i, NULL ); + net_fifo_push( streaming_ctrl->buffer, buffer, i ); + ptr = (ASF_stream_chunck_t*)buffer; + if( ret==ptr->size ) { + } + } +// done = 0; + break; + case ASF_Redirector_e: + url_next = asf_http_ASX_redirect( http_hdr ); + if( url_next==NULL ) { + printf("Failed to parse ASX file\n"); + close(fd); + http_free( http_hdr ); + return -1; + } + if( url_next->port==0 ) url_next->port=80; + url_free( url ); + url = url_next; + *(streaming_ctrl->url) = url_next; + url_next = NULL; + break; + case ASF_Unknown_e: + default: + printf("Unknown ASF streaming type\n"); + close(fd); + http_free( http_hdr ); + return -1; + } + + // Check if we got a redirect. + } while(!done); + + streaming_ctrl->fd_net = fd; + streaming_ctrl->streaming_read = asf_http_streaming_read; + streaming_ctrl->prebuffer_size = 10000; + streaming_ctrl->buffering = 1; + streaming_ctrl->status = streaming_playing_e; + + http_free( http_hdr ); + return fd; +} + diff --git a/libmpdemux/asfheader.c b/libmpdemux/asfheader.c new file mode 100644 index 0000000000..d8349c6499 --- /dev/null +++ b/libmpdemux/asfheader.c @@ -0,0 +1,279 @@ +// .asf fileformat docs from http://divx.euro.ru + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +extern int verbose; // defined in mplayer.c + +#include "config.h" +#include "mp_msg.h" + +#include "stream.h" +#include "demuxer.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "codec-cfg.h" +#include "stheader.h" +//#include "aviheader.h" +#include "asf.h" + +#ifdef ARCH_X86 +#define ASF_LOAD_GUID_PREFIX(guid) (*(uint32_t *)(guid)) +#else +#define ASF_LOAD_GUID_PREFIX(guid) \ + ((guid)[3] << 24 | (guid)[2] << 16 | (guid)[1] << 8 | (guid)[0]) +#endif + +#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 + + +static ASF_header_t asfh; +static ASF_obj_header_t objh; +static ASF_file_header_t fileh; +static ASF_stream_header_t streamh; +static ASF_content_description_t contenth; + +unsigned char* asf_packet=NULL; +int asf_scrambling_h=1; +int asf_scrambling_w=1; +int asf_scrambling_b=1; +int asf_packetsize=0; + +//int i; + +// the variable string is modify in this function +void pack_asf_string(char* string, int length) { + int i,j; + if( string==NULL ) return; + for( i=0, j=0; i<length && string[i]!='\0'; i+=2, j++) { + string[j]=string[i]; + } + string[j]='\0'; +} + +// the variable string is modify in this function +void print_asf_string(const char* name, char* string, int length) { + pack_asf_string(string, length); + mp_msg(MSGT_HEADER,MSGL_V,"%s%s\n", name, string); +} + +static 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_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"; + 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}; + stream_read(demuxer->stream,(char*) &asfh,sizeof(asfh)); // header obj + le2me_ASF_header_t(&asfh); // 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,asfh.objh.guid,16)){ + mp_msg(MSGT_HEADER,MSGL_V,"ASF_check: not ASF guid!\n"); + return 0; // not ASF guid + } + if(asfh.cno>256){ + mp_msg(MSGT_HEADER,MSGL_V,"ASF_check: invalid subchunks_no %d\n",(int) asfh.cno); + return 0; // invalid header??? + } + return 1; +} + +extern void print_wave_header(WAVEFORMATEX *h); +extern void print_video_header(BITMAPINFOHEADER *h); + +int read_asf_header(demuxer_t *demuxer){ + static unsigned char buffer[1024]; + +#if 1 + //printf("ASF file! (subchunks: %d)\n",asfh.cno); +while(!stream_eof(demuxer->stream)){ + int pos,endpos; + pos=stream_tell(demuxer->stream); + stream_read(demuxer->stream,(char*) &objh,sizeof(objh)); + le2me_ASF_obj_header_t(&objh); + if(stream_eof(demuxer->stream)) break; // EOF + endpos=pos+objh.size; +// for(i=0;i<16;i++) printf("%02X ",objh.guid[i]); + //printf("0x%08X [%s] %d\n",pos, asf_chunk_type(objh.guid),(int) objh.size); + switch(ASF_LOAD_GUID_PREFIX(objh.guid)){ + case ASF_GUID_PREFIX_stream_header: + stream_read(demuxer->stream,(char*) &streamh,sizeof(streamh)); + le2me_ASF_stream_header_t(&streamh); + if(verbose){ + 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",stream_tell(demuxer->stream)); + } + if(streamh.type_size>1024 || streamh.stream_size>1024){ + mp_msg(MSGT_HEADER,MSGL_FATAL,"FATAL: header size bigger than 1024 bytes!\n" + "Please contact mplayer authors, and upload/send this file.\n"); + return 0; + } + // type-specific data: + stream_read(demuxer->stream,(char*) buffer,streamh.type_size); + 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); + sh_audio->wf=calloc((streamh.type_size<sizeof(WAVEFORMATEX))?sizeof(WAVEFORMATEX):streamh.type_size,1); + memcpy(sh_audio->wf,buffer,streamh.type_size); + le2me_WAVEFORMATEX(sh_audio->wf); + if(verbose>=1) print_wave_header(sh_audio->wf); + if(ASF_LOAD_GUID_PREFIX(streamh.concealment)==ASF_GUID_PREFIX_audio_conceal_interleave){ + stream_read(demuxer->stream,(char*) buffer,streamh.stream_size); + asf_scrambling_h=buffer[0]; + asf_scrambling_w=(buffer[2]<<8)|buffer[1]; + asf_scrambling_b=(buffer[4]<<8)|buffer[3]; + 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); + //if(demuxer->audio->id==-1) demuxer->audio->id=streamh.stream_no & 0x7F; + break; + } + case ASF_GUID_PREFIX_video_stream: { + sh_video_t* sh_video=new_sh_video(demuxer,streamh.stream_no & 0x7F); + int len=streamh.type_size-(4+4+1+2); +// sh_video->bih=malloc(chunksize); memset(sh_video->bih,0,chunksize); + sh_video->bih=calloc((len<sizeof(BITMAPINFOHEADER))?sizeof(BITMAPINFOHEADER):len,1); + memcpy(sh_video->bih,&buffer[4+4+1+2],len); + le2me_BITMAPINFOHEADER(sh_video->bih); + //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; + if(verbose>=1) print_video_header(sh_video->bih); + //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); + break; +// case ASF_GUID_PREFIX_header_2_0: return "guid_header_2_0"; + case ASF_GUID_PREFIX_file_header: // guid_file_header + stream_read(demuxer->stream,(char*) &fileh,sizeof(fileh)); + le2me_ASF_file_header_t(&fileh); + mp_msg(MSGT_HEADER,MSGL_V,"ASF: packets: %d flags: %d pack_size: %d frame_size: %d\n",(int)fileh.packets,(int)fileh.flags,(int)fileh.packetsize,(int)fileh.frame_size); + asf_packetsize=fileh.packetsize; + asf_packet=malloc(asf_packetsize); // !!! + break; + case ASF_GUID_PREFIX_data_chunk: // guid_data_chunk + demuxer->movi_start=stream_tell(demuxer->stream)+26; + demuxer->movi_end=endpos; + mp_msg(MSGT_HEADER,MSGL_V,"Found movie at 0x%X - 0x%X\n",(int)demuxer->movi_start,(int)demuxer->movi_end); + break; + +// case ASF_GUID_PREFIX_index_chunk: return "guid_index_chunk"; + + case ASF_GUID_PREFIX_content_desc: // Content description + if(verbose){ + char *string=NULL; + stream_read(demuxer->stream,(char*) &contenth,sizeof(contenth)); + le2me_ASF_content_description_t(&contenth); + mp_msg(MSGT_HEADER,MSGL_V,"\n"); + // extract the title + if( contenth.title_size!=0 ) { + string=(char*)malloc(contenth.title_size); + stream_read(demuxer->stream, string, contenth.title_size); + print_asf_string(" Title: ", string, contenth.title_size); + } + // extract the author + if( contenth.author_size!=0 ) { + string=(char*)realloc((void*)string, contenth.author_size); + stream_read(demuxer->stream, string, contenth.author_size); + print_asf_string(" Author: ", string, contenth.author_size); + } + // extract the copyright + if( contenth.copyright_size!=0 ) { + string=(char*)realloc((void*)string, contenth.copyright_size); + stream_read(demuxer->stream, string, contenth.copyright_size); + print_asf_string(" Copyright: ", string, contenth.copyright_size); + } + // extract the comment + if( contenth.comment_size!=0 ) { + string=(char*)realloc((void*)string, contenth.comment_size); + stream_read(demuxer->stream, string, contenth.comment_size); + print_asf_string(" Comment: ", string, contenth.comment_size); + } + // extract the rating + if( contenth.rating_size!=0 ) { + string=(char*)realloc((void*)string, contenth.rating_size); + stream_read(demuxer->stream, string, contenth.rating_size); + print_asf_string(" Rating: ", string, contenth.rating_size); + } + mp_msg(MSGT_HEADER,MSGL_V,"\n"); + free(string); + } + break; + + } // switch GUID + + if(ASF_LOAD_GUID_PREFIX(objh.guid)==ASF_GUID_PREFIX_data_chunk) break; // movi chunk + + if(!stream_seek(demuxer->stream,endpos)) break; +} // while EOF + +#if 0 +if(verbose){ + 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 + +#endif +return 1; +} diff --git a/libmpdemux/aviheader.c b/libmpdemux/aviheader.c new file mode 100644 index 0000000000..a3c073185f --- /dev/null +++ b/libmpdemux/aviheader.c @@ -0,0 +1,241 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "mp_msg.h" + +#include "stream.h" +#include "demuxer.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "codec-cfg.h" +#include "bswap.h" +#include "stheader.h" +#include "aviheader.h" + +#define MIN(a,b) (((a)<(b))?(a):(b)) + + +static MainAVIHeader avih; + +extern void print_avih(MainAVIHeader *h); +extern void print_avih_flags(MainAVIHeader *h); +extern void print_strh(AVIStreamHeader *h); +extern void print_wave_header(WAVEFORMATEX *h); +extern void print_video_header(BITMAPINFOHEADER *h); +extern void print_index(AVIINDEXENTRY *idx,int idx_size); + +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; + +//---- AVI header: +priv->idx_size=0; +while(1){ + int id=stream_read_dword_le(demuxer->stream); + int chunksize,size2; + static int last_fccType=0; + char* hdr=NULL; + // + if(stream_eof(demuxer->stream)) break; + // + if(id==mmioFOURCC('L','I','S','T')){ + int len=stream_read_dword_le(demuxer->stream)-4; // list size + id=stream_read_dword_le(demuxer->stream); // list type + mp_msg(MSGT_HEADER,MSGL_DBG2,"LIST %.4s len=%d\n",(char *) &id,len); + if(id==listtypeAVIMOVIE){ + // found MOVI header + demuxer->movi_start=stream_tell(demuxer->stream); + demuxer->movi_end=demuxer->movi_start+len; + mp_msg(MSGT_HEADER,MSGL_V,"Found movie at 0x%X - 0x%X\n",(int)demuxer->movi_start,(int)demuxer->movi_end); + if(index_mode==-2) break; // reading from non-seekable source (stdin) + len=(len+1)&(~1); + stream_skip(demuxer->stream,len); + } + continue; + } + size2=stream_read_dword_le(demuxer->stream); + mp_msg(MSGT_HEADER,MSGL_DBG2,"CHUNK %.4s len=%d\n",(char *) &id,size2); + chunksize=(size2+1)&(~1); + switch(id){ + case mmioFOURCC('I','S','F','T'): hdr="Software";break; + case mmioFOURCC('I','N','A','M'): hdr="Name";break; + case mmioFOURCC('I','S','B','J'): hdr="Title";break; + case mmioFOURCC('I','A','R','T'): hdr="Author";break; + case mmioFOURCC('I','C','O','P'): hdr="Copyright";break; + case mmioFOURCC('I','C','M','T'): hdr="Comment";break; + case ckidAVIMAINHDR: // read 'avih' + stream_read(demuxer->stream,(char*) &avih,MIN(size2,sizeof(avih))); + le2me_MainAVIHeader(&avih); // swap to machine endian + chunksize-=MIN(size2,sizeof(avih)); + if(verbose) print_avih(&avih); else print_avih_flags(&avih); + break; + case ckidSTREAMHEADER: { // read 'strh' + AVIStreamHeader h; + stream_read(demuxer->stream,(char*) &h,MIN(size2,sizeof(h))); + le2me_AVIStreamHeader(&h); // swap to machine endian + chunksize-=MIN(size2,sizeof(h)); + ++stream_id; + if(h.fccType==streamtypeVIDEO){ + sh_video=new_sh_video(demuxer,stream_id); + memcpy(&sh_video->video,&h,sizeof(h)); + } else + if(h.fccType==streamtypeAUDIO){ + sh_audio=new_sh_audio(demuxer,stream_id); + memcpy(&sh_audio->audio,&h,sizeof(h)); + } + last_fccType=h.fccType; + if(verbose>=1) print_strh(&h); + break; } + case ckidSTREAMFORMAT: { // read 'strf' + if(last_fccType==streamtypeVIDEO){ + sh_video->bih=calloc((chunksize<sizeof(BITMAPINFOHEADER))?sizeof(BITMAPINFOHEADER):chunksize,1); +// sh_video->bih=malloc(chunksize); memset(sh_video->bih,0,chunksize); + mp_msg(MSGT_HEADER,MSGL_V,"found 'bih', %d bytes of %d\n",chunksize,sizeof(BITMAPINFOHEADER)); + stream_read(demuxer->stream,(char*) sh_video->bih,chunksize); + le2me_BITMAPINFOHEADER(sh_video->bih); // swap to machine endian + if(verbose>=1) print_video_header(sh_video->bih); + 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; +// if(demuxer->video->id==-1) demuxer->video->id=stream_id; + // IdxFix: + idxfix_videostream=stream_id; + switch(sh_video->bih->biCompression){ + 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; // we can fix keyframes only for divx coded files! + } + } else + if(last_fccType==streamtypeAUDIO){ + int wf_size = chunksize<sizeof(WAVEFORMATEX)?sizeof(WAVEFORMATEX):chunksize; + sh_audio->wf=calloc(wf_size,1); +// sh_audio->wf=malloc(chunksize); memset(sh_audio->wf,0,chunksize); + mp_msg(MSGT_HEADER,MSGL_V,"found 'wf', %d bytes of %d\n",chunksize,sizeof(WAVEFORMATEX)); + stream_read(demuxer->stream,(char*) sh_audio->wf,chunksize); + le2me_WAVEFORMATEX(sh_audio->wf); + if (sh_audio->wf->cbSize != 0 && + wf_size < sizeof(WAVEFORMATEX)+sh_audio->wf->cbSize) { + sh_audio->wf=realloc(sh_audio->wf, sizeof(WAVEFORMATEX)+sh_audio->wf->cbSize); + } + chunksize=0; + if(verbose>=1) print_wave_header(sh_audio->wf); +// if(demuxer->audio->id==-1) demuxer->audio->id=stream_id; + } + break; + } + case ckidAVINEWINDEX: if(index_mode){ + int i; + priv->idx_size=size2>>4; + mp_msg(MSGT_HEADER,MSGL_V,"Reading INDEX block, %d chunks for %ld frames\n", + priv->idx_size,avih.dwTotalFrames); + priv->idx=malloc(priv->idx_size<<4); + stream_read(demuxer->stream,(char*)priv->idx,priv->idx_size<<4); + for (i = 0; i < priv->idx_size; i++) // swap index to machine endian + le2me_AVIINDEXENTRY((AVIINDEXENTRY*)priv->idx + i); + chunksize-=priv->idx_size<<4; + if(verbose>=2) print_index(priv->idx,priv->idx_size); + break; + } + } + if(hdr){ + 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); + } + if(chunksize>0) stream_skip(demuxer->stream,chunksize); else + if(chunksize<0) mp_msg(MSGT_HEADER,MSGL_WARN,"chunksize=%d (id=%.4s)\n",chunksize,(char *) &id); + +} + +if(index_mode>=2 || (priv->idx_size==0 && index_mode==1)){ + // build index for file: + stream_reset(demuxer->stream); + stream_seek(demuxer->stream,demuxer->movi_start); + + priv->idx_pos=0; + priv->idx_size=0; + priv->idx=NULL; + + while(1){ + int id,len,skip; + AVIINDEXENTRY* idx; + unsigned char c; + demuxer->filepos=stream_tell(demuxer->stream); + if(demuxer->filepos>=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=stream_read_dword_le(demuxer->stream); // list type + continue; + } + if(stream_eof(demuxer->stream)) break; + if(!id || avi_stream_id(id)==100) goto skip_chunk; // bad ID (or padding?) + + if(priv->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){priv->idx_pos=0; break;} // error! + } + idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++]; + idx->ckid=id; + idx->dwFlags=AVIIF_KEYFRAME; // FIXME + idx->dwChunkOffset=demuxer->filepos; + idx->dwChunkLength=len; + + c=stream_read_char(demuxer->stream); + + // Fix keyframes for DivX files: + if(idxfix_divx) + if(avi_stream_id(id)==idxfix_videostream){ + if(c&0x40) idx->dwFlags=0; + } + + mp_dbg(MSGT_HEADER,MSGL_DBG2,"%08X %08X %.4s %02X %X\n",demuxer->filepos,id,(char *) &id,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)&(~1); // total bytes in this chunk + stream_seek(demuxer->stream,8+demuxer->filepos+skip); + } + priv->idx_size=priv->idx_pos; + mp_msg(MSGT_HEADER,MSGL_INFO,"AVI: Generated index table for %d chunks!\n",priv->idx_size); +} + +} + +#undef MIN + + diff --git a/libmpdemux/aviheader.h b/libmpdemux/aviheader.h new file mode 100644 index 0000000000..d3d129b691 --- /dev/null +++ b/libmpdemux/aviheader.h @@ -0,0 +1,106 @@ +#ifndef _aviheader_h +#define _aviheader_h + +//#include "config.h" /* get correct definition WORDS_BIGENDIAN */ +#include "bswap.h" + +/* + * Some macros to swap little endian structures read from an AVI file + * into machine endian format + */ +#ifdef WORDS_BIGENDIAN +#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); \ +} +#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) /**/ +#endif + + +#endif + + +typedef struct { + // index stuff: + void* idx; + int idx_size; + int idx_pos; + int idx_pos_a; + int idx_pos_v; + int idx_offset; // ennyit kell hozzaadni az index offset ertekekhez + // interleaved PTS stuff: + int skip_video_frames; + 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; +} avi_priv_t; + +#define AVI_PRIV ((avi_priv_t*)(demuxer->priv)) diff --git a/libmpdemux/aviprint.c b/libmpdemux/aviprint.c new file mode 100644 index 0000000000..c8fdceea15 --- /dev/null +++ b/libmpdemux/aviprint.c @@ -0,0 +1,102 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +//extern int verbose; // defined in mplayer.c + +#include "stream.h" +#include "demuxer.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +//#include "codec-cfg.h" +//#include "stheader.h" + +void print_avih_flags(MainAVIHeader *h){ + printf("MainAVIHeader.dwFlags: (%ld)%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){ + printf("======= AVI Header =======\n"); + printf("us/frame: %ld (fps=%5.3f)\n",h->dwMicroSecPerFrame,1000000.0f/(float)h->dwMicroSecPerFrame); + printf("max bytes/sec: %ld\n",h->dwMaxBytesPerSec); + printf("padding: %ld\n",h->dwPaddingGranularity); + print_avih_flags(h); + printf("frames total: %ld initial: %ld\n",h->dwTotalFrames,h->dwInitialFrames); + printf("streams: %ld\n",h->dwStreams); + printf("Suggested BufferSize: %ld\n",h->dwSuggestedBufferSize); + printf("Size: %ld x %ld\n",h->dwWidth,h->dwHeight); +} + +void print_strh(AVIStreamHeader *h){ + printf("======= STREAM Header =======\n"); + printf("Type: %.4s FCC: %.4s (%X)\n",(char *)&h->fccType,(char *)&h->fccHandler,(unsigned int)h->fccHandler); + printf("Flags: %ld\n",h->dwFlags); + printf("Priority: %d Language: %d\n",h->wPriority,h->wLanguage); + printf("InitialFrames: %ld\n",h->dwInitialFrames); + printf("Rate: %ld/%ld = %5.3f\n",h->dwRate,h->dwScale,(float)h->dwRate/(float)h->dwScale); + printf("Start: %ld Len: %ld\n",h->dwStart,h->dwLength); + printf("Suggested BufferSize: %ld\n",h->dwSuggestedBufferSize); + printf("Quality %ld\n",h->dwQuality); + printf("Sample size: %ld\n",h->dwSampleSize); +} + +void print_wave_header(WAVEFORMATEX *h){ + + printf("======= WAVE Format =======\n"); + printf("Format Tag: %d (0x%X)\n",h->wFormatTag,h->wFormatTag); + printf("Channels: %d\n",h->nChannels); + printf("Samplerate: %ld\n",h->nSamplesPerSec); + printf("avg byte/sec: %ld\n",h->nAvgBytesPerSec); + printf("Block align: %d\n",h->nBlockAlign); + printf("bits/sample: %d\n",h->wBitsPerSample); + printf("cbSize: %d\n",h->cbSize); +} + + +void print_video_header(BITMAPINFOHEADER *h){ + printf("======= VIDEO Format ======\n"); + printf(" biSize %ld\n", h->biSize); + printf(" biWidth %ld\n", h->biWidth); + printf(" biHeight %ld\n", h->biHeight); + printf(" biPlanes %d\n", h->biPlanes); + printf(" biBitCount %d\n", h->biBitCount); + printf(" biCompression %ld='%.4s'\n", h->biCompression, (char *)&h->biCompression); + printf(" biSizeImage %ld\n", h->biSizeImage); + printf("===========================\n"); +} + + +void print_index(AVIINDEXENTRY *idx,int idx_size){ + int i; + unsigned int pos[256]; + unsigned int num[256]; + for(i=0;i<256;i++) num[i]=pos[i]=0; + for(i=0;i<idx_size;i++){ + int id=avi_stream_id(idx[i].ckid); + if(id<0 || id>255) id=255; + printf("%5d: %.4s %4X %08X len:%6ld pos:%7d->%7.3f %7d->%7.3f\n",i, + (char *)&idx[i].ckid, + (unsigned int)idx[i].dwFlags, + (unsigned int)idx[i].dwChunkOffset, +// 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]; + } +} + + diff --git a/libmpdemux/aviwrite.c b/libmpdemux/aviwrite.c new file mode 100644 index 0000000000..beb431af8b --- /dev/null +++ b/libmpdemux/aviwrite.c @@ -0,0 +1,172 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +extern char* encode_name; +extern char* encode_index_name; + +void write_avi_chunk(FILE *f,unsigned int id,int len,void* data){ + +fwrite(&id,4,1,f); +fwrite(&len,4,1,f); +if(len>0){ + if(data){ + // DATA + fwrite(data,len,1,f); + if(len&1){ // padding + unsigned char zerobyte=0; + fwrite(&zerobyte,1,1,f); + } + } else { + // JUNK + char *avi_junk_data="[= MPlayer junk data! =]"; + if(len&1) ++len; // padding + while(len>0){ + int l=strlen(avi_junk_data); + if(l>len) l=len; + fwrite(avi_junk_data,l,1,f); + len-=l; + } + } +} + +} + + +void write_avi_list(FILE *f,unsigned int id,int len){ + unsigned int list_id=FOURCC_LIST; + len+=4; // list fix + fwrite(&list_id,4,1,f); + fwrite(&len,4,1,f); + fwrite(&id,4,1,f); +} + +struct { + MainAVIHeader avih; + AVIStreamHeader video; + BITMAPINFOHEADER bih; + unsigned int movi_start; + unsigned int movi_end; + unsigned int file_end; +} wah; + +void write_avi_header(FILE *f){ + unsigned int riff[3]; + // RIFF header: + riff[0]=mmioFOURCC('R','I','F','F'); + riff[1]=wah.file_end; // filesize + riff[2]=formtypeAVI; // 'AVI ' + fwrite(&riff,12,1,f); + // AVI header: + write_avi_list(f,listtypeAVIHEADER,sizeof(wah.avih)+8+12+sizeof(wah.video)+8+sizeof(wah.bih)+8); + write_avi_chunk(f,ckidAVIMAINHDR,sizeof(wah.avih),&wah.avih); + // stream header: + write_avi_list(f,listtypeSTREAMHEADER,sizeof(wah.video)+8+sizeof(wah.bih)+8); + write_avi_chunk(f,ckidSTREAMHEADER,sizeof(wah.video),&wah.video); + write_avi_chunk(f,ckidSTREAMFORMAT,sizeof(wah.bih),&wah.bih); + // JUNK: + write_avi_chunk(f,ckidAVIPADDING,2048-(ftell(f)&2047)-8,NULL); + // 'movi' header: + write_avi_list(f,listtypeAVIMOVIE,wah.movi_end-ftell(f)-12); + wah.movi_start=ftell(f); +} + +// called _before_ encoding: (write placeholders and video info) +void write_avi_header_1(FILE *f,int fcc,float fps,int width,int height){ + int frames=8*3600*fps; // 8 hours + + wah.file_end= + wah.movi_end=0x7f000000; + + wah.avih.dwMicroSecPerFrame=1000000.0f/fps; + wah.avih.dwMaxBytesPerSec=fps*500000; // ????? + wah.avih.dwPaddingGranularity=1; // padding + wah.avih.dwFlags=AVIF_ISINTERLEAVED; + wah.avih.dwTotalFrames=frames; + wah.avih.dwInitialFrames=0; + wah.avih.dwStreams=1; + wah.avih.dwSuggestedBufferSize=0x10000; // 1MB + wah.avih.dwWidth=width; + wah.avih.dwHeight=height; + wah.avih.dwReserved[0]= + wah.avih.dwReserved[1]= + wah.avih.dwReserved[2]= + wah.avih.dwReserved[3]=0; + + wah.video.fccType=streamtypeVIDEO; + wah.video.fccHandler=fcc; + wah.video.dwFlags=0; + wah.video.wPriority=0; + wah.video.wLanguage=0; + wah.video.dwInitialFrames=0; + wah.video.dwScale=10000; + wah.video.dwRate=fps*10000; + wah.video.dwStart=0; + wah.video.dwLength=frames; + wah.video.dwSuggestedBufferSize=0x100000; // 1MB ???? + wah.video.dwQuality=10000; + wah.video.dwSampleSize=width*height*3; + + wah.bih.biSize=sizeof(wah.bih); // 40 ? + wah.bih.biWidth=width; + wah.bih.biHeight=height; + wah.bih.biPlanes=1; + wah.bih.biBitCount=24; + wah.bih.biCompression=fcc; + wah.bih.biSizeImage=3*width*height; + wah.bih.biXPelsPerMeter= + wah.bih.biYPelsPerMeter= + wah.bih.biClrUsed= + wah.bih.biClrImportant=0; + + write_avi_header(f); +} + +void avi_fixate(){ + // append index and fix avi headers: + FILE *f1=fopen(encode_name,"r+"); + FILE *f2; + + if(!f1) return; // error + + fseek(f1,0,SEEK_END); + wah.file_end=wah.movi_end=ftell(f1); + + // index: + if(encode_index_name && (f2=fopen(encode_index_name,"rb"))){ + AVIINDEXENTRY idx; + unsigned int pos=0; + int frames=0; + write_avi_chunk(f1,ckidAVINEWINDEX,0,NULL); + while(fread(&idx,sizeof(idx),1,f2)>0){ + idx.dwChunkOffset-=wah.movi_start-4; + fwrite(&idx,sizeof(idx),1,f1); + ++frames; + } + fclose(f2); + unlink(encode_index_name); + wah.file_end=ftell(f1); + // re-write idx1 length: + pos=wah.file_end-wah.movi_end-8; + fseek(f1,wah.movi_end+4,SEEK_SET); + fwrite(&pos,4,1,f1); + // fixup frames: + wah.avih.dwTotalFrames=frames; + wah.video.dwLength=frames; + } + + // re-write avi header: + fseek(f1,0,SEEK_SET); + write_avi_header(f1); + + fclose(f1); + +} + + + diff --git a/libmpdemux/bswap.h b/libmpdemux/bswap.h new file mode 100644 index 0000000000..21072f676a --- /dev/null +++ b/libmpdemux/bswap.h @@ -0,0 +1,2 @@ +/* Let it be for now*/ +#include "../bswap.h" diff --git a/libmpdemux/codec-cfg.h b/libmpdemux/codec-cfg.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/libmpdemux/codec-cfg.h diff --git a/libmpdemux/config.h b/libmpdemux/config.h new file mode 100644 index 0000000000..319abb0131 --- /dev/null +++ b/libmpdemux/config.h @@ -0,0 +1,2 @@ +/* Let it be for now*/ +#include "../config.h" diff --git a/libmpdemux/demux_asf.c b/libmpdemux/demux_asf.c new file mode 100644 index 0000000000..94787171cb --- /dev/null +++ b/libmpdemux/demux_asf.c @@ -0,0 +1,367 @@ +// ASF file parser for DEMUXER v0.3 by A'rpi/ESP-team + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#include "stream.h" +#include "asf.h" +#include "demuxer.h" + + +/* + * Load 16/32-bit values in little endian byte order + * from an unaligned address + */ +#ifdef ARCH_X86 +#define LOAD_LE32(p) (*(unsigned int*)(p)) +#define LOAD_LE16(p) (*(unsigned short*)(p)) +#else +#define LOAD_LE32(p) (((unsigned char*)(p))[0] | \ + ((unsigned char*)(p))[1]<< 8 | \ + ((unsigned char*)(p))[2]<<16 | \ + ((unsigned char*)(p))[3]<<24 ) +#define LOAD_LE16(p) (((unsigned char*)(p))[0] | \ + ((unsigned char*)(p))[1]<<8) +#endif + +// defined at asfheader.c: +extern unsigned char* asf_packet; +extern int asf_scrambling_h; +extern int asf_scrambling_w; +extern int asf_scrambling_b; +extern int asf_packetsize; + + +// based on asf file-format doc by Eugene [http://divx.euro.ru] + +static void asf_descrambling(unsigned char *src,int len){ + unsigned char *dst=malloc(len); + unsigned char *s2=src; + int i=0,x,y; + while(len-i>=asf_scrambling_h*asf_scrambling_w*asf_scrambling_b){ +// 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); + memcpy(src,dst,i); + free(dst); +} + + +static int demux_asf_read_packet(demuxer_t *demux,unsigned char *data,int len,int id,int seq,unsigned long time,unsigned short dur,int offs,int keyframe){ + demux_stream_t *ds=NULL; + + 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){ + if(ds->asf_seq!=seq){ + // closed segment, finalize packet: + if(ds==demux->audio) + if(asf_scrambling_h>1 && asf_scrambling_w>1) + asf_descrambling(ds->asf_packet->buffer,ds->asf_packet->len); + ds_add_packet(ds,ds->asf_packet); + ds->asf_packet=NULL; + } else { + // append data to it! + demux_packet_t* dp=ds->asf_packet; + 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); + memcpy(dp->buffer+dp->len,data,len); + mp_dbg(MSGT_DEMUX,MSGL_DBG4,"data appended! %d+%d\n",dp->len,len); + dp->len+=len; + // 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); + dp->pts=time*0.001f; + dp->flags=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; +} + +//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 +int demux_asf_fill_buffer(demuxer_t *demux){ + + demux->filepos=stream_tell(demux->stream); + if(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_packet[0]==0x82){ + unsigned char flags=asf_packet[3]; + unsigned char segtype=asf_packet[4]; + unsigned char* p=&asf_packet[5]; + unsigned char* p_end=p+asf_packetsize; + unsigned long time; + unsigned short duration; + int segs=1; + unsigned char segsizetype=0x80; + int seg; + int padding=0; + int plen; + + if(verbose>1){ + int i; + for(i=0;i<16;i++) printf(" %02X",asf_packet[i]); + printf("\n"); + } + + //if(segtype!=0x5d) printf("Warning! packet[4] != 0x5d \n"); + + // Calculate packet size (plen): + if(flags&0x40){ + // Explicit (absoulte) packet size + plen=LOAD_LE16(p); p+=2; + 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); + if(flags&(8|16)){ + padding=p[0];p++; + if(flags&16){ padding|=p[0]<<8; p++;} + mp_msg(MSGT_DEMUX,MSGL_V,"Warning! explicit=%d padding=%d \n",plen,asf_packetsize-padding); + } + } else { + // Padding (relative) size + if(flags&8){ + padding=p[0];++p; + } else + if(flags&16){ + padding=LOAD_LE16(p);p+=2; + } + plen=asf_packetsize-padding; + } + + time = LOAD_LE32(p); p+=4; + duration = LOAD_LE16(p); p+=2; + if(flags&1){ + segsizetype=p[0] & 0xC0; + segs=p[0] & 0x3F; + ++p; + } + mp_dbg(MSGT_DEMUX,MSGL_DBG4,"%08X: flag=%02X segs=%d pad=%d time=%ld dur=%d\n", + demux->filepos,flags,segs,padding,time,duration); + for(seg=0;seg<segs;seg++){ + //ASF_segmhdr_t* sh; + unsigned char streamno; + unsigned char seq; + int len; + unsigned long x; + unsigned char type; + unsigned long time2; + int keyframe=0; + + if(p>=p_end) mp_msg(MSGT_DEMUX,MSGL_V,"Warning! invalid packet 1, sig11 coming soon...\n"); + + if(verbose>1){ + int i; + printf("seg %d:",seg); + for(i=0;i<16;i++) printf(" %02X",p[i]); + printf("\n"); + } + + streamno=p[0]&0x7F; + if(p[0]&0x80) keyframe=1; + seq=p[1]; + p+=2; + + switch(segtype){ + case 0x55: + x=*((unsigned char*)p); + p++; + break; + case 0x59: + x=LOAD_LE16(p); + p+=2; + break; + case 0x5D: + x=LOAD_LE32(p); + p+=4; + break; + default: + mp_msg(MSGT_DEMUX,MSGL_V,"Warning! unknown segtype == 0x%2X \n",segtype); + x=0; + } + + type=p[0]; p++; // 0x01: grouping 0x08: single + + switch(type){ + case 0x01: + //printf("grouping: %02X \n",p[0]); + ++p; // skip unknown byte + break; + case 0x08: + //printf("!!! obj_length = %d\n",*((unsigned long*)p)); + p+=4; + time2=LOAD_LE32(p);p+=4; + break; + default: + mp_msg(MSGT_DEMUX,MSGL_V,"unknown segment type: 0x%02X \n",type); + } + + if(flags&1){ + // multiple segments + if(segsizetype==0x40){ + len=*((unsigned char*)p);p++; // 1 byte + } else { + len=LOAD_LE16(p);p+=2; // 2 byte + } + } 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); + } + mp_dbg(MSGT_DEMUX,MSGL_DBG4," seg #%d: streamno=%d seq=%d type=%02X len=%d\n",seg,streamno,seq,type,len); + + switch(type){ + 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); + demux_asf_read_packet(demux,p,len2,streamno,seq,x,duration,-1,keyframe); + p+=len2; + len-=len2+1; + } + if(len!=0){ + mp_msg(MSGT_DEMUX,MSGL_V,"ASF_parser: warning! groups total != len\n"); + } + break; + case 0x08: + // NO GROUPING: + //printf("fragment offset: %d \n",sh->x); + 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,"%08X: UNKNOWN TYPE %02X %02X %02X %02X %02X...\n",demux->filepos,asf_packet[0],asf_packet[1],asf_packet[2],asf_packet[3],asf_packet[4]); + return 0; +} + + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "codec-cfg.h" +#include "stheader.h" + + +void demux_seek_asf(demuxer_t *demuxer,float rel_seek_secs,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; + + //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=10; // packets / sec + off_t rel_seek_packs=(flags&2)? // 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&1)?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); + + ds_fill_buffer(d_video); + if(sh_audio){ + ds_fill_buffer(d_audio); + resync_audio_stream(sh_audio); + } + + 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->flags&1) break; // found a keyframe! + if(!ds_fill_buffer(d_video)) break; // skip frame. EOF? + } + + +} + diff --git a/libmpdemux/demux_avi.c b/libmpdemux/demux_avi.c new file mode 100644 index 0000000000..1583371b48 --- /dev/null +++ b/libmpdemux/demux_avi.c @@ -0,0 +1,673 @@ +// AVI file parser for DEMUXER v2.9 by A'rpi/ESP-team + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#include "stream.h" +#include "demuxer.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "codec-cfg.h" +#include "stheader.h" + +#include "aviheader.h" + +// Select ds from ID +demux_stream_t* demux_avi_select_stream(demuxer_t *demux,unsigned int id){ + int stream_id=avi_stream_id(id); + +// printf("demux_avi_select_stream(%d) {a:%d/v:%d}\n",stream_id, +// demux->audio->id,demux->video->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); + } + 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); + } + return NULL; +} + +static int demux_avi_read_packet(demuxer_t *demux,unsigned int id,unsigned int len,int idxpos,int flags){ + avi_priv_t *priv=demux->priv; + int skip; + float pts=0; + demux_stream_t *ds=demux_avi_select_stream(demux,id); + + mp_dbg(MSGT_DEMUX,MSGL_DBG3,"demux_avi.read_packet: %X\n",id); + + if(ds==demux->audio){ + + if(priv->pts_corrected==0){ +// printf("\rYYY-A A: %5.3f V: %5.3f \n",priv->avi_audio_pts,priv->avi_video_pts); + if(priv->pts_has_video){ + // we have video pts now + float 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; + } + pts=priv->avi_audio_pts; //+priv->pts_correction; + priv->avi_audio_pts=0; + } else + if(ds==demux->video){ + // video + if(priv->skip_video_frames>0){ + // drop frame (seeking) + --priv->skip_video_frames; + ds=NULL; +// } else { +// pts=priv->avi_video_pts; + } + // ezt a 2 sort lehet hogy fell kell majd cserelni: + //priv->avi_video_pts+=avi_pts_frametime; + //priv->avi_video_pts+=(float)avi_header.video.dwScale/(float)avi_header.video.dwRate; + //priv->avi_video_pts+=((sh_video_t*)ds->sh)->frametime; +// FIXME!!! +#if 1 +// printf("ds=0x%X\n",ds); +// printf("packno=%d\n",ds->pack_no); +// printf("### pack_no=%d\n",demux->video->pack_no+demux->video->packs); + priv->avi_video_pts = (demux->video->pack_no+demux->video->packs) * + (float)((sh_video_t*)demux->video->sh)->video.dwScale / + (float)((sh_video_t*)demux->video->sh)->video.dwRate; +#else + priv->avi_video_pts+=(float)((sh_video_t*)(demux->video->sh))->video.dwScale/(float)((sh_video_t*)(demux->video->sh))->video.dwRate; +// priv->avi_video_pts+=avi_video_ftime; +#endif +// printf("\rYYY-V A: %5.3f V: %5.3f \n",priv->avi_audio_pts,priv->avi_video_pts); + priv->avi_audio_pts=priv->avi_video_pts+priv->pts_correction; + priv->pts_has_video=1; + + pts=priv->avi_video_pts; + + //printf("read pack_no: %d pts %5.3f \n",demux->video->pack_no+demux->video->packs,pts); + + } + +// len=stream_read_dword_le(demux->stream); + 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; + } + 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; +} + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +int demux_avi_fill_buffer(demuxer_t *demux){ +avi_priv_t *priv=demux->priv; +unsigned int id=0; +unsigned int len; +int max_packs=128; +int ret=0; + +do{ + int flags=0; + AVIINDEXENTRY *idx=NULL; +#if 0 + demux->filepos=stream_tell(demux->stream); + if(demux->filepos>=demux->movi_end){ + demux->stream->eof=1; + return 0; + } + if(stream_eof(demux->stream)) return 0; +#endif + if(priv->idx_size>0 && priv->idx_pos<priv->idx_size){ + unsigned int pos; + + //if(priv->idx_pos<0) printf("Fatal! idx_pos=%d\n",priv->idx_pos); + + idx=&((AVIINDEXENTRY *)priv->idx)[priv->idx_pos++]; + + //printf("[%d]",priv->idx_pos);fflush(stdout); + + //stream_seek(demux->stream,idx.dwChunkOffset); + //printf("IDX pos=%X idx.pos=%X idx.size=%X idx.flags=%X\n",demux->filepos, + // pos-4,idx->dwChunkLength,idx->dwFlags); + if(idx->dwFlags&AVIIF_LIST){ + // LIST + continue; + } + 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=idx->dwChunkOffset+priv->idx_offset; + if(pos<demux->movi_start || pos>=demux->movi_end){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! idx=0x%X \n",pos); + continue; + } +#if 0 + if(pos!=demux->filepos){ + mp_msg(MSGT_DEMUX,MSGL_V,"Warning! pos=0x%X idx.pos=0x%X diff=%d \n",demux->filepos,pos,pos-demux->filepos); + } +#endif + 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); + id=idx->ckid; +// continue; + } + len=stream_read_dword_le(demux->stream); +// if((len&(~1))!=(idx->dwChunkLength&(~1))){ +// if((len)!=(idx->dwChunkLength)){ + if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%ld \n",len,idx->dwChunkLength); + len=idx->dwChunkLength; +// continue; + } + if(idx->dwFlags&AVIIF_KEYFRAME) flags=1; + } else { + demux->filepos=stream_tell(demux->stream); + if(demux->filepos>=demux->movi_end){ + demux->stream->eof=1; + return 0; + } + id=stream_read_dword_le(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=stream_read_dword_le(demux->stream); // list type + continue; + } + } + ret=demux_avi_read_packet(demux,id,len,priv->idx_pos-1,flags); +// if(!ret && priv->skip_video_frames<=0) +// if(--max_packs==0){ +// demux->stream->eof=1; +// mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_DoesntContainSelectedStream); +// return 0; +// } +} while(ret!=1); + return 1; +} + + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +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 max_packs=128; +int ret=0; + +do{ + int flags=0; + 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){ + unsigned int pos; + idx=&((AVIINDEXENTRY *)priv->idx)[idx_pos]; +// idx=&priv->idx[idx_pos]; + + if(idx->dwFlags&AVIIF_LIST){ + // LIST + continue; + } + 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=idx->dwChunkOffset+priv->idx_offset; + if(pos<demux->movi_start || pos>=demux->movi_end){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkOffset out of range! current=0x%X idx=0x%X \n",demux->filepos,pos); + continue; + } +#if 0 + if(pos!=demux->filepos){ + mp_msg(MSGT_DEMUX,MSGL_V,"Warning! pos=0x%X idx.pos=0x%X diff=%d \n",demux->filepos,pos,pos-demux->filepos); + } +#endif + 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); + id=idx->ckid; +// continue; + } + len=stream_read_dword_le(demux->stream); +// if((len&(~1))!=(idx->dwChunkLength&(~1))){ +// if((len)!=(idx->dwChunkLength)){ + if((len!=idx->dwChunkLength)&&((len+1)!=idx->dwChunkLength)){ + mp_msg(MSGT_DEMUX,MSGL_V,"ChunkSize mismatch! raw=%d idx=%ld \n",len,idx->dwChunkLength); + len=idx->dwChunkLength; +// continue; + } + if(idx->dwFlags&AVIIF_KEYFRAME) flags=1; + } else return 0; + ret=demux_avi_read_packet(demux,id,len,idx_pos,flags); +// if(!ret && priv->skip_video_frames<=0) +// if(--max_packs==0){ +// demux->stream->eof=1; +// mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_DoesntContainSelectedStream); +// return 0; +// } +} while(ret!=1); + return 1; +} + + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +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; +int *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->stream->eof=1; + return 0; + } + if(stream_eof(demux->stream)) return 0; + + id=stream_read_dword_le(demux->stream); + len=stream_read_dword_le(demux->stream); + if(id==mmioFOURCC('L','I','S','T')){ + id=stream_read_dword_le(demux->stream); // list type + continue; + } + + if(ds==demux_avi_select_stream(demux,id)){ + // read it! + ret=demux_avi_read_packet(demux,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; +} + +//extern int audio_id; +//extern int video_id; +extern int index_mode; // -1=untouched 0=don't use index 1=use (geneate) index +extern int force_ni; +extern int pts_from_bps; + +void read_avi_header(demuxer_t *demuxer,int index_mode); + +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=malloc(sizeof(avi_priv_t)); + + // priv struct: + priv->avi_audio_pts=priv->avi_video_pts=0.0f; + priv->pts_correction=0.0f; + priv->skip_video_frames=0; + priv->pts_corr_bytes=0; + priv->pts_has_video=priv->pts_corrected=0; + demuxer->priv=(void*)priv; + + //---- AVI header: + read_avi_header(demuxer,(demuxer->stream->type!=STREAMTYPE_STREAM)?index_mode:-2); + stream_reset(demuxer->stream); + stream_seek(demuxer->stream,demuxer->movi_start); + priv->idx_pos=0; + priv->idx_pos_a=0; + priv->idx_pos_v=0; + if(priv->idx_size>0){ + // decide index format: + if(((AVIINDEXENTRY *)priv->idx)[0].dwChunkOffset<demuxer->movi_start) + priv->idx_offset=demuxer->movi_start-4; + else + priv->idx_offset=0; + mp_msg(MSGT_DEMUX,MSGL_V,"AVI index offset: %d\n",priv->idx_offset); + } +// demuxer->endpos=avi_header.movi_end; + + if(priv->idx_size>0){ + // check that file is non-interleaved: + int i; + int a_pos=-1; + int 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); + int pos=idx->dwChunkOffset+priv->idx_offset; + 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: " MSGTR_MissingVideoStream); + return NULL; +// GUI_MSG( mplErrorAVINI ) + } + if(a_pos==-1){ + mp_msg(MSGT_DEMUX,MSGL_INFO,"AVI_NI: " MSGTR_MissingAudioStream); + sh_audio=NULL; + } else { + if(force_ni || abs(a_pos-v_pos)>0x100000){ // distance > 1MB + mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_NI_Message,force_ni?MSGTR_NI_Forced:MSGTR_NI_Detected); + demuxer->type=DEMUXER_TYPE_AVI_NI; // HACK!!!! + pts_from_bps=1; // force BPS sync! + } + } + } else { + // no index + if(force_ni){ + mp_msg(MSGT_DEMUX,MSGL_INFO,MSGTR_UsingNINI); + demuxer->type=DEMUXER_TYPE_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: " MSGTR_MissingVideoStreamBug); + return NULL; +// GUI_MSG( mplAVIErrorMissingVideoStream ) + } + 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(!ds_fill_buffer(d_audio)){ + mp_msg(MSGT_DEMUX,MSGL_INFO,"AVI: " MSGTR_MissingAudioStream); + sh_audio=NULL; + } else { + sh_audio=d_audio->sh;sh_audio->ds=d_audio; + sh_audio->format=sh_audio->wf->wFormatTag; + } + } + // calc. FPS: + 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; + // calculating video bitrate: + sh_video->i_bps=demuxer->movi_end-demuxer->movi_start-priv->idx_size*8; + if(sh_audio) sh_video->i_bps-=sh_audio->audio.dwLength; + mp_msg(MSGT_DEMUX,MSGL_V,"AVI video length=%d\n",sh_video->i_bps); + sh_video->i_bps=((float)sh_video->i_bps/(float)sh_video->video.dwLength)*sh_video->fps; + mp_msg(MSGT_DEMUX,MSGL_INFO,"VIDEO: [%.4s] %ldx%ld %dbpp %4.2f fps %5.1f kbps (%4.1f kbyte/s)\n", + (char *)&sh_video->bih->biCompression, + sh_video->bih->biWidth, + sh_video->bih->biHeight, + sh_video->bih->biBitCount, + sh_video->fps, + sh_video->i_bps*0.008f, + sh_video->i_bps/1024.0f ); + + return demuxer; + +} + +//extern float initial_pts_delay; + +void demux_seek_avi(demuxer_t *demuxer,float rel_seek_secs,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&1){ + // seek absolute + video_chunk_pos=0; + } + + if(flags&2){ + // float 0..1 + int total=sh_video->video.dwLength; + if(total<=1){ + // bad video header, try to get it from audio + if(sh_audio) total=sh_video->fps*sh_audio->audio.dwLength/sh_audio->wf->nAvgBytesPerSec; + if(total<=1){ + mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CouldntDetFNo); + total=0; + } + } + rel_seek_frames=rel_seek_secs*total; + } + + 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){ + 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; + } + 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; + d_audio->dpos=0; + + if(sh_audio){ + int i; +// int apos=0; + int last=0; + 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 +#if 0 + int align; + curr_audio_pos=(priv->avi_video_pts) * sh_audio->wf->nAvgBytesPerSec; + if(curr_audio_pos<0)curr_audio_pos=0; + align=sh_audio->audio.dwSampleSize; + if(sh_audio->wf->nBlockAlign>align) align=sh_audio->wf->nBlockAlign; + curr_audio_pos/=align; + curr_audio_pos*=align; +#else + curr_audio_pos=(priv->avi_video_pts)*(float)sh_audio->audio.dwRate/(float)sh_audio->audio.dwScale; + curr_audio_pos*=sh_audio->audio.dwSampleSize; +#endif + + // 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; + audio_chunk_pos=i; ++d_audio->pack_no; + if(d_audio->dpos<=curr_audio_pos && curr_audio_pos<(d_audio->dpos+len)){ + //if(verbose)printf("break;\n"); + break; + } + d_audio->dpos+=len; + } + } + skip_audio_bytes=curr_audio_pos-d_audio->dpos; + + } else { + // VBR audio + int chunks=(priv->avi_video_pts)*(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; + d_audio->dpos+=len; + audio_chunk_pos=i; + } + --chunks; + } + } + //if(audio_chunk_pos>chunk_max) audio_chunk_pos=chunk_max; + +// printf("VBR seek: %5.3f -> chunk_no %d -> chunk_idx %d + skip %d \n", +// priv->avi_video_pts, audio_chunk_pos, ); + + } + + // 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; +// d_audio->dpos=apos; +// d_audio->pts=initial_pts_delay+(float)apos/(float)sh_audio->wf->nAvgBytesPerSec; + + 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->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", + priv->idx_pos,audio_chunk_pos,video_chunk_pos, + priv->skip_video_frames,skip_audio_bytes,skip_audio_secs); + + if(skip_audio_bytes){ + demux_read_data(d_audio,NULL,skip_audio_bytes); + //d_audio->pts=0; // PTS is outdated because of the raw data skipping + } + resync_audio_stream(sh_audio); + +// sh_audio->timer=-skip_audio_secs; + + } + d_video->pts=priv->avi_video_pts; // OSD + +} + + + diff --git a/libmpdemux/demux_mov.c b/libmpdemux/demux_mov.c new file mode 100644 index 0000000000..76b85c9473 --- /dev/null +++ b/libmpdemux/demux_mov.c @@ -0,0 +1,613 @@ +// QuickTime MOV file parser by A'rpi +// based on TOOLS/movinfo.c by me & Al3x +// compressed header support from moov.c of the openquicktime lib. + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" + +#ifdef HAVE_PNG +// should be detected by ./configure... +#define HAVE_ZLIB +#endif + +#include "mp_msg.h" +#include "help_mp.h" + +#include "stream.h" +#include "demuxer.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "codec-cfg.h" +#include "stheader.h" + +#ifdef HAVE_ZLIB +#include <zlib.h> +#endif + +typedef struct { + unsigned int pts; // duration + unsigned int size; + off_t pos; +} mov_sample_t; + +typedef struct { + unsigned int sample; // number of the first sample in teh chunk + unsigned int size; // number of samples in the chunk + int desc; // for multiple codecs mode - not used + off_t pos; +} mov_chunk_t; + +typedef struct { + unsigned int first; + unsigned int spc; + unsigned int sdid; +} mov_chunkmap_t; + +typedef struct { + unsigned int num; + unsigned int dur; +} mov_durmap_t; + +typedef struct { + int id; + int type; + int pos; + // + int timescale; + unsigned int length; + int samplesize; // 0 = variable + int duration; // 0 = variable + int width,height; // for video + unsigned int fourcc; + // + int tkdata_len; // track data + unsigned char* tkdata; + int stdata_len; // stream data + unsigned char* stdata; + int samples_size; + mov_sample_t* samples; + int chunks_size; + mov_chunk_t* chunks; + int chunkmap_size; + mov_chunkmap_t* chunkmap; + int durmap_size; + mov_durmap_t* durmap; +} mov_track_t; + +void mov_build_index(mov_track_t* trak){ + int i,j,s; + int last=trak->chunks_size; + unsigned int pts=0; + printf("MOV track: %d chunks, %d samples\n",trak->chunks_size,trak->samples_size); + printf("pts=%d scale=%d time=%5.3f\n",trak->length,trak->timescale,(float)trak->length/(float)trak->timescale); + // process chunkmap: + i=trak->chunkmap_size; + while(i>0){ + --i; + for(j=trak->chunkmap[i].first;j<last;j++){ + trak->chunks[j].desc=trak->chunkmap[i].sdid; + trak->chunks[j].size=trak->chunkmap[i].spc; + } + last=trak->chunkmap[i].first; + } + + // calc pts of chunks: + s=0; + for(j=0;j<trak->chunks_size;j++){ + trak->chunks[j].sample=s; + s+=trak->chunks[j].size; + } + + if(!trak->samples_size){ + // constant sampesize + if(trak->durmap_size==1 || (trak->durmap_size==2 && trak->durmap[1].num==1)){ + trak->duration=trak->durmap[0].dur; + } else printf("*** constant samplesize & variable duration not yet supported! ***\nContact the author if you have such sample file!\n"); + return; + } + + // calc pts: + s=0; + for(j=0;j<trak->durmap_size;j++){ + for(i=0;i<trak->durmap[j].num;i++){ + trak->samples[s].pts=pts; + ++s; + pts+=trak->durmap[j].dur; + } + } + + // calc sample offsets + s=0; + for(j=0;j<trak->chunks_size;j++){ + off_t pos=trak->chunks[j].pos; + for(i=0;i<trak->chunks[j].size;i++){ + trak->samples[s].pos=pos; +#if 0 + printf("Sample %5d: pts=%8d off=0x%08X size=%d\n",s, + trak->samples[s].pts, + (int)trak->samples[s].pos, + trak->samples[s].size); +#endif + pos+=trak->samples[s].size; + ++s; + } + } + +} + +#define MOV_MAX_TRACKS 256 + +#define MOV_TRAK_UNKNOWN 0 +#define MOV_TRAK_VIDEO 1 +#define MOV_TRAK_AUDIO 2 + +typedef struct { + off_t moov_start; + off_t moov_end; + off_t mdat_start; + off_t mdat_end; + int track_db; + mov_track_t* tracks[MOV_MAX_TRACKS]; +} mov_priv_t; + +#define MOV_FOURCC(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|(d)) + +int mov_check_file(demuxer_t* demuxer){ + int flags=0; + mov_priv_t* priv=malloc(sizeof(mov_priv_t)); + + mp_msg(MSGT_DEMUX,MSGL_V,"Checking for MOV\n"); + + memset(priv,0,sizeof(mov_priv_t)); + demuxer->priv=priv; + + while(1){ + off_t len=stream_read_dword(demuxer->stream); + unsigned int id=stream_read_dword(demuxer->stream); + if(stream_eof(demuxer->stream)) break; // EOF + if(len<8) break; // invalid chunk + switch(id){ + case MOV_FOURCC('m','o','o','v'): + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: Movie header found!\n"); + priv->moov_start=stream_tell(demuxer->stream); + priv->moov_end=priv->moov_start+len-8; + flags|=1; + break; + case MOV_FOURCC('m','d','a','t'): + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: Movie DATA found!\n"); + priv->mdat_start=stream_tell(demuxer->stream); + priv->mdat_end=priv->mdat_start+len-8; + flags|=2; + break; + default: + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: unknown chunk: %.4s %d\n",&id,(int)len); + } + if(!stream_skip(demuxer->stream,len-8)) break; + } + + if(flags==1) + mp_msg(MSGT_DEMUX,MSGL_WARN,"MOV: missing data (mdat) chunk! Maybe broken file...\n"); + else if(flags==2) + mp_msg(MSGT_DEMUX,MSGL_WARN,"MOV: missing header (moov/cmov) chunk! Maybe broken file...\n"); + +return (flags==3); +} + +static void lschunks(demuxer_t* demuxer,int level,off_t endpos,mov_track_t* trak){ + mov_priv_t* priv=demuxer->priv; + while(1){ + off_t pos; + off_t len; + unsigned int id; + // + pos=stream_tell(demuxer->stream); +// printf("stream_tell==%d\n",pos); + if(pos>=endpos) return; // END + len=stream_read_dword(demuxer->stream); +// printf("len==%d\n",len); + if(len<8) return; // error + len-=8; + id=stream_read_dword(demuxer->stream); + // + mp_msg(MSGT_DEMUX,MSGL_DBG2,"lschunks %.4s %d\n",&id,(int)len); + // + if(trak){ + switch(id){ + case MOV_FOURCC('t','k','h','d'): { + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sTrack header!\n",level,""); + // read codec data + trak->tkdata_len=len; + trak->tkdata=malloc(trak->tkdata_len); + stream_read(demuxer->stream,trak->tkdata,trak->tkdata_len); + break; + } + case MOV_FOURCC('m','d','h','d'): { + unsigned int tmp; + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sMedia header!\n",level,""); +#if 0 + tmp=stream_read_dword(demuxer->stream); + printf("dword1: 0x%08X (%d)\n",tmp,tmp); + tmp=stream_read_dword(demuxer->stream); + printf("dword2: 0x%08X (%d)\n",tmp,tmp); + tmp=stream_read_dword(demuxer->stream); + printf("dword3: 0x%08X (%d)\n",tmp,tmp); + tmp=stream_read_dword(demuxer->stream); + printf("dword4: 0x%08X (%d)\n",tmp,tmp); + tmp=stream_read_dword(demuxer->stream); + printf("dword5: 0x%08X (%d)\n",tmp,tmp); + tmp=stream_read_dword(demuxer->stream); + printf("dword6: 0x%08X (%d)\n",tmp,tmp); +#endif + stream_skip(demuxer->stream,12); + // read timescale + trak->timescale=stream_read_dword(demuxer->stream); + // read length + trak->length=stream_read_dword(demuxer->stream); + break; + } + case MOV_FOURCC('v','m','h','d'): { + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sVideo header!\n",level,""); + trak->type=MOV_TRAK_VIDEO; + // read video data + break; + } + case MOV_FOURCC('s','m','h','d'): { + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sSound header!\n",level,""); + trak->type=MOV_TRAK_AUDIO; + // read audio data + break; + } + case MOV_FOURCC('s','t','s','d'): { + int i=stream_read_dword(demuxer->stream); // temp! + int count=stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sDescription list! (cnt:%d)\n",level,"",count); + for(i=0;i<count;i++){ + off_t pos=stream_tell(demuxer->stream); + off_t len=stream_read_dword(demuxer->stream); + unsigned int fourcc=stream_read_dword_le(demuxer->stream); + if(len<8) break; // error + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*s desc #%d: %.4s",level,"",i,&fourcc); + if(!i){ + trak->fourcc=fourcc; + // read codec data + trak->stdata_len=len-8; + trak->stdata=malloc(trak->stdata_len); + stream_read(demuxer->stream,trak->stdata,trak->stdata_len); + if(trak->type==MOV_TRAK_VIDEO && trak->stdata_len>43){ + mp_msg(MSGT_DEMUX,MSGL_V," '%.*s'",trak->stdata_len-43,trak->stdata+43); + } + } + mp_msg(MSGT_DEMUX,MSGL_V,"\n"); + if(fourcc!=trak->fourcc && i) + mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_MOVvariableFourCC); + if(!stream_seek(demuxer->stream,pos+len)) break; + } + break; + } + case MOV_FOURCC('s','t','t','s'): { + int temp=stream_read_dword(demuxer->stream); + int len=stream_read_dword(demuxer->stream); + int i; + int x=0; + unsigned int pts=0; + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sSample duration table! (%d blocks)\n",level,"",len); + trak->durmap=malloc(sizeof(mov_durmap_t)*len); + memset(trak->durmap,0,sizeof(mov_durmap_t)*len); + trak->durmap_size=len; + for(i=0;i<len;i++){ + trak->durmap[i].num=stream_read_dword(demuxer->stream); + trak->durmap[i].dur=stream_read_dword(demuxer->stream); + pts+=trak->durmap[i].num*trak->durmap[i].dur; + } + if(trak->length!=pts) printf("Warning! pts=%d length=%d\n",pts,trak->length); + break; + } + case MOV_FOURCC('s','t','s','c'): { + int temp=stream_read_dword(demuxer->stream); + int len=stream_read_dword(demuxer->stream); + int i; + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sSample->Chunk mapping table! (%d blocks)\n",level,"",len); + // read data: + trak->chunkmap_size=len; + trak->chunkmap=malloc(sizeof(mov_chunkmap_t)*len); + for(i=0;i<len;i++){ + trak->chunkmap[i].first=stream_read_dword(demuxer->stream)-1; + trak->chunkmap[i].spc=stream_read_dword(demuxer->stream); + trak->chunkmap[i].sdid=stream_read_dword(demuxer->stream); + } + break; + } + case MOV_FOURCC('s','t','s','z'): { + int temp=stream_read_dword(demuxer->stream); + int ss=stream_read_dword(demuxer->stream); + int len=stream_read_dword(demuxer->stream); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sSample size table! len=%d ss=%d\n",level,"",len,ss); + trak->samplesize=ss; + if(!ss){ + // variable samplesize + int i; + trak->samples=realloc(trak->samples,sizeof(mov_sample_t)*len); + trak->samples_size=len; + for(i=0;i<len;i++) + trak->samples[i].size=stream_read_dword(demuxer->stream); + } + break; + } + case MOV_FOURCC('s','t','c','o'): { + int temp=stream_read_dword(demuxer->stream); + int len=stream_read_dword(demuxer->stream); + int i; + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sChunk offset table! (%d chunks)\n",level,"",len); + // extend array if needed: + if(len>trak->chunks_size){ + trak->chunks=realloc(trak->chunks,sizeof(mov_chunk_t)*len); + trak->chunks_size=len; + } + // read elements: + for(i=0;i<len;i++) trak->chunks[i].pos=stream_read_dword(demuxer->stream); + break; + } + case MOV_FOURCC('m','d','i','a'): { + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sMedia stream!\n",level,""); + lschunks(demuxer,level+1,pos+len,trak); + break; + } + case MOV_FOURCC('m','i','n','f'): { + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sMedia info!\n",level,""); + lschunks(demuxer,level+1,pos+len,trak); + break; + } + case MOV_FOURCC('s','t','b','l'): { + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: %*sSample info!\n",level,""); + lschunks(demuxer,level+1,pos+len,trak); + break; + } + }//switch(id) + } else + if(id==MOV_FOURCC('t','r','a','k')){ +// if(trak) printf("MOV: Warning! trak in trak?\n"); + if(priv->track_db>=MOV_MAX_TRACKS){ + mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_MOVtooManyTrk); + return; + } + trak=malloc(sizeof(mov_track_t)); + memset(trak,0,sizeof(mov_track_t)); + mp_msg(MSGT_DEMUX,MSGL_V,"MOV: Track #%d:\n",priv->track_db); + trak->id=priv->track_db; + priv->tracks[priv->track_db]=trak; + lschunks(demuxer,level+1,pos+len,trak); + mov_build_index(trak); + switch(trak->type){ + case MOV_TRAK_AUDIO: { + sh_audio_t* sh=new_sh_audio(demuxer,priv->track_db); + sh->format=trak->fourcc; + printf("!!! audio bits: %d chans: %d\n",trak->stdata[19],trak->stdata[17]); + printf("Fourcc: %.4s\n",&trak->fourcc); + // Emulate WAVEFORMATEX struct: + sh->wf=malloc(sizeof(WAVEFORMATEX)); + memset(sh->wf,0,sizeof(WAVEFORMATEX)); + sh->wf->nChannels=trak->stdata[17]; + sh->wf->wBitsPerSample=trak->stdata[19]; + sh->wf->nSamplesPerSec=trak->timescale; + sh->wf->nAvgBytesPerSec=sh->wf->nChannels*((sh->wf->wBitsPerSample+7)/8)*sh->wf->nSamplesPerSec; + // Selection: + if(demuxer->audio->id==-1 || demuxer->audio->id==priv->track_db){ + // (auto)selected audio track: + demuxer->audio->id=priv->track_db; + demuxer->audio->sh=sh; sh->ds=demuxer->audio; + } + break; + } + case MOV_TRAK_VIDEO: { + sh_video_t* sh=new_sh_video(demuxer,priv->track_db); + sh->format=trak->fourcc; + sh->fps=trak->timescale; + sh->frametime=1.0f/sh->fps; + sh->disp_w=trak->tkdata[77]|(trak->tkdata[76]<<8); + sh->disp_h=trak->tkdata[81]|(trak->tkdata[80]<<8); + + // emulate BITMAPINFOHEADER: + sh->bih=malloc(sizeof(BITMAPINFOHEADER)); + memset(sh->bih,0,sizeof(BITMAPINFOHEADER)); + sh->bih->biSize=40; + sh->bih->biWidth=sh->disp_w; + sh->bih->biHeight=sh->disp_h; + sh->bih->biPlanes=0; + sh->bih->biBitCount=16; + sh->bih->biCompression=trak->fourcc; + sh->bih->biSizeImage=sh->bih->biWidth*sh->bih->biHeight; + + printf("Image size: %d x %d\n",sh->disp_w,sh->disp_h); + printf("Fourcc: %.4s Codec: '%.*s'\n",&trak->fourcc,trak->stdata_len-43,trak->stdata+43); + + if(demuxer->video->id==-1 || demuxer->video->id==priv->track_db){ + // (auto)selected video track: + demuxer->video->id=priv->track_db; + demuxer->video->sh=sh; sh->ds=demuxer->video; + } + break; + } + } + printf("--------------\n"); + priv->track_db++; + trak=NULL; + } else +#ifndef HAVE_ZLIB + if(id==MOV_FOURCC('c','m','o','v')){ + mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_MOVcomprhdr); + return; + } +#else + if(id==MOV_FOURCC('c','m','o','v')){ +// mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_MOVcomprhdr); + lschunks(demuxer,level+1,pos+len,NULL); + } else + if(id==MOV_FOURCC('d','c','o','m')){ +// int temp=stream_read_dword(demuxer->stream); + unsigned int len=stream_read_dword(demuxer->stream); + printf("Compressed header uses %.4s algo!\n",&len); + } else + if(id==MOV_FOURCC('c','m','v','d')){ +// int temp=stream_read_dword(demuxer->stream); + unsigned int moov_sz=stream_read_dword(demuxer->stream); + unsigned int cmov_sz=len-4; + unsigned char* cmov_buf=malloc(cmov_sz); + unsigned char* moov_buf=malloc(moov_sz+16); + int zret; + z_stream zstrm; + stream_t* backup; + + printf("Compressed header size: %d / %d\n",cmov_sz,moov_sz); + + stream_read(demuxer->stream,cmov_buf,cmov_sz); + + zstrm.zalloc = (alloc_func)0; + zstrm.zfree = (free_func)0; + zstrm.opaque = (voidpf)0; + zstrm.next_in = cmov_buf; + zstrm.avail_in = cmov_sz; + zstrm.next_out = moov_buf; + zstrm.avail_out = moov_sz; + + zret = inflateInit(&zstrm); + if (zret != Z_OK) + { fprintf(stderr,"QT cmov: inflateInit err %d\n",zret); + return; + } + zret = inflate(&zstrm, Z_NO_FLUSH); + if ((zret != Z_OK) && (zret != Z_STREAM_END)) + { fprintf(stderr,"QT cmov inflate: ERR %d\n",zret); + return; + } +#if 0 + else { + FILE *DecOut; + DecOut = fopen("Out.bin", "w"); + fwrite(moov_buf, 1, moov_sz, DecOut); + fclose(DecOut); + } +#endif + if(moov_sz != zstrm.total_out) printf("Warning! moov size differs cmov: %d zlib: %d\n",moov_sz,zstrm.total_out); + zret = inflateEnd(&zstrm); + + backup=demuxer->stream; + demuxer->stream=new_memory_stream(moov_buf,moov_sz); + stream_skip(demuxer->stream,8); + lschunks(demuxer,level+1,moov_sz,NULL); // parse uncompr. 'moov' + //free_stream(demuxer->stream); + demuxer->stream=backup; + + } +#endif + + pos+=len+8; + if(pos>=endpos) break; + if(!stream_seek(demuxer->stream,pos)) break; + } +} + +int mov_read_header(demuxer_t* demuxer){ + mov_priv_t* priv=demuxer->priv; + + printf("mov_read_header!\n"); + + // Parse header: + stream_reset(demuxer->stream); + if(!stream_seek(demuxer->stream,priv->moov_start)) return 0; // ??? + lschunks(demuxer, 0, priv->moov_end, NULL); + +#if 1 + return 1; +#else + mp_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_MOVnotyetsupp); + return 0; +#endif +} + +// return value: +// 0 = EOF or no stream found +// 1 = successfully read a packet +int demux_mov_fill_buffer(demuxer_t *demuxer,demux_stream_t* ds){ + mov_priv_t* priv=demuxer->priv; + mov_track_t* trak=NULL; + float pts; + + if(ds->id<0 || ds->id>=priv->track_db) return 0; + trak=priv->tracks[ds->id]; + +if(trak->samplesize){ + // read chunk: + if(trak->pos>=trak->chunks_size) return 0; // EOF + stream_seek(demuxer->stream,trak->chunks[trak->pos].pos); + pts=(float)(trak->chunks[trak->pos].sample*trak->duration)/(float)trak->timescale; + ds_read_packet(ds,demuxer->stream,trak->chunks[trak->pos].size*trak->samplesize,pts,trak->chunks[trak->pos].pos,0); +} else { + // read sample: + if(trak->pos>=trak->samples_size) return 0; // EOF + stream_seek(demuxer->stream,trak->samples[trak->pos].pos); + pts=(float)trak->samples[trak->pos].pts/(float)trak->timescale; + ds_read_packet(ds,demuxer->stream,trak->samples[trak->pos].size,pts,trak->samples[trak->pos].pos,0); +} + ++trak->pos; + + return 1; + +} + +static float mov_seek_track(mov_track_t* trak,float pts,int flags){ + +// printf("MOV track seek called %5.3f \n",pts); + if(flags&2) pts*=trak->length; else pts*=(float)trak->timescale; + +if(trak->samplesize){ + int sample=pts/trak->duration; +// printf("MOV track seek - chunk: %d (pts: %5.3f dur=%d) \n",sample,pts,trak->duration); + if(!(flags&1)) sample+=trak->chunks[trak->pos].sample; // relative + trak->pos=0; + while(trak->pos<trak->chunks_size && trak->chunks[trak->pos].sample<sample) ++trak->pos; + pts=(float)(trak->chunks[trak->pos].sample*trak->duration)/(float)trak->timescale; +} else { + unsigned int ipts=pts; +// printf("MOV track seek - sample: %d \n",ipts); + if(!(flags&1)) ipts+=trak->samples[trak->pos].pts; + trak->pos=0; + while(trak->pos<trak->samples_size && trak->samples[trak->pos].pts<ipts) ++trak->pos; + pts=(float)trak->samples[trak->pos].pts/(float)trak->timescale; +} + +// printf("MOV track seek done: %5.3f \n",pts); + +return pts; +} + +void demux_seek_mov(demuxer_t *demuxer,float pts,int flags){ + mov_priv_t* priv=demuxer->priv; + demux_stream_t* ds; + +// printf("MOV seek called %5.3f flag=%d \n",pts,flags); + + ds=demuxer->video; + if(ds && ds->id>=0 && ds->id<priv->track_db){ + mov_track_t* trak=priv->tracks[ds->id]; + //if(flags&2) pts*=(float)trak->length/(float)trak->timescale; + //if(!(flags&1)) pts+=ds->pts; + pts=ds->pts=mov_seek_track(trak,pts,flags); + flags=1; // absolute seconds + } + + ds=demuxer->audio; + if(ds && ds->id>=0 && ds->id<priv->track_db){ + mov_track_t* trak=priv->tracks[ds->id]; + //if(flags&2) pts*=(float)trak->length/(float)trak->timescale; + //if(!(flags&1)) pts+=ds->pts; + ds->pts=mov_seek_track(trak,pts,flags); + } + +} + diff --git a/libmpdemux/demux_mpg.c b/libmpdemux/demux_mpg.c new file mode 100644 index 0000000000..dc4cc7f438 --- /dev/null +++ b/libmpdemux/demux_mpg.c @@ -0,0 +1,403 @@ +// MPG/VOB file parser for DEMUXER v2.5 by A'rpi/ESP-team + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#include "config.h" +#include "dvdauth.h" +#include "stream.h" +#include "demuxer.h" +#include "parse_es.h" + +//#define MAX_PS_PACKETSIZE 2048 +#define MAX_PS_PACKETSIZE (224*1024) + +static int mpeg_pts_error=0; + +static unsigned int read_mpeg_timestamp(stream_t *s,int c){ + int d,e; + unsigned int 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=(((c>>1)&7)<<30)|((d>>1)<<15)|(e>>1); + mp_dbg(MSGT_DEMUX,MSGL_DBG3,"{%d}",pts); + return pts; +} + +//static unsigned int packet_start_pos=0; + +//extern void *new_sh_audio(demuxer_t *demux,int id); +//extern void *new_sh_video(demuxer_t *demux,int id); +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "codec-cfg.h" +#include "stheader.h" + +static int demux_mpg_read_packet(demuxer_t *demux,int id){ + int d; + int len; +#ifdef HAVE_LIBCSS + int css=0; +#endif + unsigned char c=0; + unsigned int pts=0; + unsigned int dts=0; + demux_stream_t *ds=NULL; + + 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) 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; + + 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); + 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}"); + dts=read_mpeg_timestamp(demux->stream,c); + len-=4+1+4; + } else + if((c>>6)==2){ + int pts_flags; + int hdrlen; + // System-2 (.VOB) stream: + if((c>>4)&3) { +#ifdef HAVE_LIBCSS + css=1; +#else + mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_EncryptedVOB); +#endif + } + c=stream_read_char(demux->stream); pts_flags=c>>6; + 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){ + c=stream_read_char(demux->stream); + pts=read_mpeg_timestamp(demux->stream,c); + len-=5;hdrlen-=5; + } else + if(pts_flags==3){ + c=stream_read_char(demux->stream); + pts=read_mpeg_timestamp(demux->stream,c); + c=stream_read_char(demux->stream); + dts=read_mpeg_timestamp(demux->stream,c); + len-=10;hdrlen-=10; + } + len-=hdrlen; + if(hdrlen>0) stream_skip(demux->stream,hdrlen); // skip header bytes + + //============== DVD Audio sub-stream ====================== + if(id==0x1BD){ + int aid=stream_read_char(demux->stream);--len; + if(len<3) return -1; // invalid audio packet + + // AID: + // 0x20..0x3F subtitle + // 0x80..0x9F AC3 audio + // 0xA0..0xBF PCM audio + + if((aid & 0xE0) == 0x20){ + // subtitle: + aid&=0x1F; + + if(!demux->s_streams[aid]){ + mp_msg(MSGT_DEMUX,MSGL_V,"==> Found subtitle: %d\n",aid); + demux->s_streams[aid]=1; + } + + if(demux->sub->id==aid){ + ds=demux->sub; + } + + } else if((aid & 0xC0) == 0x80) { + +// aid=128+(aid&0x7F); + // aid=0x80..0xBF + + if(!demux->a_streams[aid]) new_sh_audio(demux,aid); + if(demux->audio->id==-1) demux->audio->id=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: + c=stream_read_char(demux->stream);//type=c; + c=stream_read_char(demux->stream);//type|=c<<8; + c=stream_read_char(demux->stream);//type|=c<<16; +// printf("[%06X]",type); + len-=3; + if((aid&0xE0)==0xA0 && len>=2){ + // read PCM header + int head; + head=stream_read_char(demux->stream); head=c<<8; + c=stream_read_char(demux->stream); head|=c; len-=2; + while(len>0 && head!=0x180){ + head=c<<8; + c=stream_read_char(demux->stream); + head|=c;--len; + } + if(!len) mp_msg(MSGT_DEMUX,MSGL_V,"End of packet while searching for PCM header\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; + if(!demux->a_streams[aid]) new_sh_audio(demux,aid); + if(demux->audio->id==-1) demux->audio->id=aid; + if(demux->audio->id==aid){ + ds=demux->audio; + if(!ds->sh) ds->sh=demux->a_streams[aid]; + } + } 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(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); +#ifdef HAVE_LIBCSS + if (css) { + if (descrambling) CSSDescramble(demux->stream->buffer,key_title); else + mp_msg(MSGT_DEMUX,MSGL_WARN,MSGTR_EncryptedVOBauth); + } +#endif + ds_read_packet(ds,demux->stream,len,pts/90000.0f,demux->filepos,0); +// 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; +} + +int num_elementary_packets100=0; +int num_elementary_packets101=0; +int num_elementary_packets1B6=0; +int num_elementary_packetsPES=0; + +int demux_mpg_es_fill_buffer(demuxer_t *demux){ + // 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; +} + +int demux_mpg_fill_buffer(demuxer_t *demux){ +unsigned int head=0; +int skipped=0; +int max_packs=128; +int ret=0; + +// System stream +do{ + demux->filepos=stream_tell(demux->stream); + 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; + ++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; +#if 0 + else if(head>=0x1C0 && head<=0x1EF){ + demux->synced=2; + mp_msg(MSGT_DEMUX,MSGL_V,"Mpeg PES stream synced at 0x%X (%d)!\n",demux->filepos,demux->filepos); + num_elementary_packets100=0; // requires for re-sync! + num_elementary_packets101=0; // requires for re-sync! + } +#endif + } 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%X (%d)!\n",demux->filepos,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_msg(MSGT_DEMUX,MSGL_ERR,MSGTR_DoesntContainSelectedStream); + return 0; + } + } else { + if(head>=0x100 && head<0x1B0){ + if(head==0x100) ++num_elementary_packets100; else + if(head==0x101) ++num_elementary_packets101; + mp_msg(MSGT_DEMUX,MSGL_DBG3,"Opps... elementary video packet found: %03X\n",head); + } else + if(head>=0x1C0 && head<0x1F0){ + ++num_elementary_packetsPES; + mp_msg(MSGT_DEMUX,MSGL_DBG3,"Opps... PES packet found: %03X\n",head); + } else + if(head==0x1B6) ++num_elementary_packets1B6; +#if 1 + 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; + } +#endif + } +} 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; +} + +//extern off_t seek_to_byte; + +void demux_seek_mpg(demuxer_t *demuxer,float rel_seek_secs,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; + + //================= seek in MPEG ========================== + off_t newpos=(flags&1)?demuxer->movi_start:demuxer->filepos; + + if(flags&2){ + // float seek 0..1 + newpos+=(demuxer->movi_end-demuxer->movi_start)*rel_seek_secs; + } else { + // time seek (secs) + if(!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; + } + + if(newpos<demuxer->movi_start) newpos=demuxer->movi_start; + +#ifdef _LARGEFILE_SOURCE + newpos&=~((long long)STREAM_BUFFER_SIZE-1); /* sector boundary */ +#else + newpos&=~(STREAM_BUFFER_SIZE-1); /* sector boundary */ +#endif + 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); + resync_audio_stream(sh_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; + } + } + i=sync_video_packet(d_video); + if(i==0x1B3 || i==0x1B8) break; // found it! + if(!i || !skip_video_packet(d_video)) break; // EOF? + } + + +} + diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c new file mode 100644 index 0000000000..f573ef7860 --- /dev/null +++ b/libmpdemux/demuxer.c @@ -0,0 +1,580 @@ +//=================== DEMUXER v2.5 ========================= + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#include "stream.h" +#include "demuxer.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" + +#include "codec-cfg.h" +#include "stheader.h" + +void free_demuxer_stream(demux_stream_t *ds){ + ds_free_packs(ds); + free(ds); +} + +demux_stream_t* new_demuxer_stream(struct demuxer_st *demuxer,int id){ + demux_stream_t* ds=malloc(sizeof(demux_stream_t)); + ds->buffer_pos=ds->buffer_size=0; + ds->buffer=NULL; + ds->pts=0; + ds->pts_bytes=0; + ds->eof=0; + ds->pos=0; + ds->dpos=0; + ds->pack_no=0; +//--------------- + ds->packs=0; + ds->bytes=0; + ds->first=ds->last=NULL; + ds->id=id; + ds->demuxer=demuxer; +//---------------- + ds->asf_seq=-1; + ds->asf_packet=NULL; +//---------------- + ds->sh=NULL; + return ds; +} + +demuxer_t* new_demuxer(stream_t *stream,int type,int a_id,int v_id,int s_id){ + demuxer_t *d=malloc(sizeof(demuxer_t)); + memset(d,0,sizeof(demuxer_t)); + d->stream=stream; + d->movi_start=stream->start_pos; + d->movi_end=stream->end_pos; + d->seekable=1; + d->synced=0; + d->filepos=0; + d->audio=new_demuxer_stream(d,a_id); + d->video=new_demuxer_stream(d,v_id); + d->sub=new_demuxer_stream(d,s_id); + d->type=type; + stream_reset(stream); + stream_seek(stream,stream->start_pos); + return d; +} + +sh_audio_t* new_sh_audio(demuxer_t *demuxer,int id){ + if(demuxer->a_streams[id]){ + mp_msg(MSGT_DEMUXER,MSGL_WARN,MSGTR_AudioStreamRedefined,id); + } else { + mp_msg(MSGT_DEMUXER,MSGL_V,"==> Found audio stream: %d\n",id); + demuxer->a_streams[id]=malloc(sizeof(sh_audio_t)); + memset(demuxer->a_streams[id],0,sizeof(sh_audio_t)); + } + return demuxer->a_streams[id]; +} + +void free_sh_audio(sh_audio_t* sh){ + if(sh->a_in_buffer) free(sh->a_in_buffer); + if(sh->a_buffer) free(sh->a_buffer); + if(sh->wf) free(sh->wf); + free(sh); +} + +sh_video_t* new_sh_video(demuxer_t *demuxer,int id){ + if(demuxer->v_streams[id]){ + mp_msg(MSGT_DEMUXER,MSGL_WARN,MSGTR_VideoStreamRedefined,id); + } else { + mp_msg(MSGT_DEMUXER,MSGL_V,"==> Found video stream: %d\n",id); + demuxer->v_streams[id]=malloc(sizeof(sh_video_t)); + memset(demuxer->v_streams[id],0,sizeof(sh_video_t)); + } + return demuxer->v_streams[id]; +} + +void free_sh_video(sh_video_t* sh){ + if(sh->our_out_buffer) free(sh->our_out_buffer); + if(sh->bih) free(sh->bih); + free(sh); +} + +void free_demuxer(demuxer_t *demuxer){ + int i; + // free streams: + for(i=0;i<256;i++){ + if(demuxer->a_streams[i]) free_sh_audio(demuxer->a_streams[i]); + if(demuxer->v_streams[i]) free_sh_video(demuxer->v_streams[i]); + } + //if(sh_audio) free_sh_audio(sh_audio); + //if(sh_video) free_sh_video(sh_video); + // free demuxers: + free_demuxer_stream(demuxer->audio); + free_demuxer_stream(demuxer->video); + free(demuxer); +} + + +void ds_add_packet(demux_stream_t *ds,demux_packet_t* dp){ +// demux_packet_t* dp=new_demux_packet(len); +// stream_read(stream,dp->buffer,len); +// dp->pts=pts; //(float)pts/90000.0f; +// dp->pos=pos; + // 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); +} + +void ds_read_packet(demux_stream_t *ds,stream_t *stream,int len,float pts,off_t pos,int flags){ + demux_packet_t* dp=new_demux_packet(len); + stream_read(stream,dp->buffer,len); + dp->pts=pts; //(float)pts/90000.0f; + dp->pos=pos; + dp->flags=flags; + // 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_mpg_es_fill_buffer(demuxer_t *demux); +int demux_mpg_fill_buffer(demuxer_t *demux); +int demux_avi_fill_buffer(demuxer_t *demux); +int demux_avi_fill_buffer_ni(demuxer_t *demux,demux_stream_t *ds); +int demux_avi_fill_buffer_nini(demuxer_t *demux,demux_stream_t *ds); +int demux_asf_fill_buffer(demuxer_t *demux); +int demux_mov_fill_buffer(demuxer_t *demux,demux_stream_t* ds); + +int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){ + // Note: parameter 'ds' can be NULL! +// printf("demux->type=%d\n",demux->type); + switch(demux->type){ + case DEMUXER_TYPE_MPEG_ES: return demux_mpg_es_fill_buffer(demux); + case DEMUXER_TYPE_MPEG_PS: return demux_mpg_fill_buffer(demux); + case DEMUXER_TYPE_AVI: return demux_avi_fill_buffer(demux); + case DEMUXER_TYPE_AVI_NI: return demux_avi_fill_buffer_ni(demux,ds); + case DEMUXER_TYPE_AVI_NINI: return demux_avi_fill_buffer_nini(demux,ds); + case DEMUXER_TYPE_ASF: return demux_asf_fill_buffer(demux); + case DEMUXER_TYPE_MOV: return demux_mov_fill_buffer(demux,ds); + } + return 0; +} + +// return value: +// 0 = EOF +// 1 = succesfull +int ds_fill_buffer(demux_stream_t *ds){ + demuxer_t *demux=ds->demuxer; + if(ds->buffer) free(ds->buffer); + if(verbose>2){ + if(ds==demux->audio) mp_dbg(MSGT_DEMUXER,MSGL_DBG3,"ds_fill_buffer(d_audio) called\n");else + if(ds==demux->video) mp_dbg(MSGT_DEMUXER,MSGL_DBG3,"ds_fill_buffer(d_video) called\n");else + mp_dbg(MSGT_DEMUXER,MSGL_DBG3,"ds_fill_buffer(unknown 0x%X) called\n",(unsigned int)ds); + } + 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){ + ds->pts=p->pts; + ds->pts_bytes=0; + } + ds->pts_bytes+=p->len; // !!! + ds->flags=p->flags; + // free packet: + ds->bytes-=p->len; + ds->first=p->next; + if(!ds->first) ds->last=NULL; + free(p); + --ds->packs; + return 1; //ds->buffer_size; + } + if(demux->audio->packs>=MAX_PACKS || demux->audio->bytes>=MAX_PACK_BYTES){ + mp_msg(MSGT_DEMUXER,MSGL_ERR,MSGTR_TooManyAudioInBuffer,demux->audio->packs,demux->audio->bytes); + mp_msg(MSGT_DEMUXER,MSGL_HINT,MSGTR_MaybeNI); + break; + } + if(demux->video->packs>=MAX_PACKS || demux->video->bytes>=MAX_PACK_BYTES){ + mp_msg(MSGT_DEMUXER,MSGL_ERR,MSGTR_TooManyVideoInBuffer,demux->video->packs,demux->video->bytes); + mp_msg(MSGT_DEMUXER,MSGL_HINT,MSGTR_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; +} + +int demux_read_data_pack(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; // stop at end of package! (for correct timestamping) + } +} +return bytes; +} + + +void ds_free_packs(demux_stream_t *ds){ + demux_packet_t *dp=ds->first; + while(dp){ + demux_packet_t *dn=dp->next; + free(dp->buffer); + free(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->buffer) free(ds->buffer); + ds->buffer=NULL; + ds->buffer_pos=ds->buffer_size; + ds->pts=0; ds->pts_bytes=0; +} + +int ds_get_packet(demux_stream_t *ds,unsigned char **start){ + while(1){ + 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_sub(demux_stream_t *ds,unsigned char **start){ + while(1){ + 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; + } +} + +// ==================================================================== + +// feed-back from demuxers: +extern int num_elementary_packets100; // for MPEG-ES fileformat detection +extern int num_elementary_packets101; +extern int num_elementary_packetsPES; +extern int num_elementary_packets1B6; + +// commandline options, flags: +//extern int seek_to_byte; +extern int force_ni; +extern int pts_from_bps; + +extern int audio_id; +extern int video_id; +extern int dvdsub_id; + +void read_avi_header(demuxer_t *demuxer,int index_mode); +int asf_check_header(demuxer_t *demuxer); +int read_asf_header(demuxer_t *demuxer); +demux_stream_t* demux_avi_select_stream(demuxer_t *demux,unsigned int id); +demuxer_t* demux_open_avi(demuxer_t* demuxer); +int mov_check_file(demuxer_t* demuxer); +int mov_read_header(demuxer_t* demuxer); + + +demuxer_t* demux_open(stream_t *stream,int file_format,int audio_id,int video_id,int dvdsub_id){ + +//int file_format=(*file_format_ptr); + +demuxer_t *demuxer=NULL; + +demux_stream_t *d_audio=NULL; +demux_stream_t *d_video=NULL; + +sh_audio_t *sh_audio=NULL; +sh_video_t *sh_video=NULL; + +//printf("demux_open(%p,%d,%d,%d,%d) \n",stream,file_format,audio_id,video_id,dvdsub_id); + +//=============== Try to open as AVI file: ================= +if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_AVI){ + demuxer=new_demuxer(stream,DEMUXER_TYPE_AVI,audio_id,video_id,dvdsub_id); + { //---- RIFF header: + int id=stream_read_dword_le(demuxer->stream); // "RIFF" + if(id==mmioFOURCC('R','I','F','F')){ + stream_read_dword_le(demuxer->stream); //filesize + id=stream_read_dword_le(demuxer->stream); // "AVI " + if(id==formtypeAVI){ + mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedAVIfile); + file_format=DEMUXER_TYPE_AVI; + } + } + } +} +//=============== Try to open as ASF file: ================= +if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_ASF){ + demuxer=new_demuxer(stream,DEMUXER_TYPE_ASF,audio_id,video_id,dvdsub_id); + if(asf_check_header(demuxer)){ + mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedASFfile); + file_format=DEMUXER_TYPE_ASF; + } +} +//=============== Try to open as MPEG-PS file: ================= +if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MPEG_PS){ + int pes=1; + while(pes>=0){ + demuxer=new_demuxer(stream,DEMUXER_TYPE_MPEG_PS,audio_id,video_id,dvdsub_id); + if(!pes) demuxer->synced=1; // hack! + num_elementary_packets100=0; + num_elementary_packets101=0; + num_elementary_packets1B6=0; + num_elementary_packetsPES=0; + + if(ds_fill_buffer(demuxer->video)){ + if(!pes) + mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedMPEGPESfile); + else + mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedMPEGPSfile); + file_format=DEMUXER_TYPE_MPEG_PS; + } else { + // some hack to get meaningfull error messages to our unhappy users: + if(num_elementary_packets100>=2 && num_elementary_packets101>=2 && + abs(num_elementary_packets101-num_elementary_packets100)<8){ + if(num_elementary_packetsPES>=4 && num_elementary_packetsPES>=num_elementary_packets100-4){ + --pes;continue; // tricky... + } + file_format=DEMUXER_TYPE_MPEG_ES; // <-- hack is here :) + } else { + if(demuxer->synced==2) + mp_msg(MSGT_DEMUXER,MSGL_ERR,"MPEG: " MSGTR_MissingVideoStreamBug); + else + mp_msg(MSGT_DEMUXER,MSGL_V,"Not MPEG System Stream format... (maybe Transport Stream?)\n"); + } + } + break; + } +} +//=============== Try to open as MPEG-ES file: ================= +if(file_format==DEMUXER_TYPE_MPEG_ES){ // little hack, see above! + demuxer=new_demuxer(stream,DEMUXER_TYPE_MPEG_ES,audio_id,video_id,dvdsub_id); + if(!ds_fill_buffer(demuxer->video)){ + mp_msg(MSGT_DEMUXER,MSGL_ERR,MSGTR_InvalidMPEGES); + file_format=DEMUXER_TYPE_UNKNOWN; + } else { + mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedMPEGESfile); + } +} +//=============== Try to open as MOV file: ================= +#if 1 +if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MOV){ + demuxer=new_demuxer(stream,DEMUXER_TYPE_MOV,audio_id,video_id,dvdsub_id); + if(mov_check_file(demuxer)){ + mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedQTMOVfile); + file_format=DEMUXER_TYPE_MOV; + } +} +#endif +//=============== Unknown, exiting... =========================== +if(file_format==DEMUXER_TYPE_UNKNOWN){ + mp_msg(MSGT_DEMUXER,MSGL_ERR,MSGTR_FormatNotRecognized); + return NULL; +// GUI_MSG( mplUnknowFileType ) +} +//====== File format recognized, set up these for compatibility: ========= +d_audio=demuxer->audio; +d_video=demuxer->video; +//d_dvdsub=demuxer->sub; + +demuxer->file_format=file_format; + +switch(file_format){ + case DEMUXER_TYPE_MOV: { + if(!mov_read_header(demuxer)) return NULL; +// sh_video=d_video->sh;if(sh_video) sh_video->ds=d_video; +// sh_audio=d_audio->sh;if(sh_audio) sh_audio->ds=d_audio; + break; + } + case DEMUXER_TYPE_AVI: { + return (demuxer_t*) demux_open_avi(demuxer); +// break; + } + case DEMUXER_TYPE_ASF: { + //---- ASF header: + read_asf_header(demuxer); + stream_reset(demuxer->stream); + stream_seek(demuxer->stream,demuxer->movi_start); +// demuxer->idx_pos=0; +// demuxer->endpos=avi_header.movi_end; + if(!ds_fill_buffer(d_video)){ + mp_msg(MSGT_DEMUXER,MSGL_WARN,"ASF: " MSGTR_MissingVideoStream); + sh_video=NULL; + //printf("ASF: missing video stream!? contact the author, it may be a bug :(\n"); + //GUI_MSG( mplASFErrorMissingVideoStream ) + } else { + sh_video=d_video->sh;sh_video->ds=d_video; + sh_video->fps=1000.0f; sh_video->frametime=0.001f; // 1ms + mp_msg(MSGT_DEMUXER,MSGL_INFO,"VIDEO: [%.4s] %ldx%ld %dbpp\n", + (char *)&sh_video->bih->biCompression, + sh_video->bih->biWidth, + sh_video->bih->biHeight, + sh_video->bih->biBitCount); +// sh_video->i_bps=10*asf_packetsize; // FIXME! + } + if(audio_id!=-2){ + mp_msg(MSGT_DEMUXER,MSGL_V,"ASF: Searching for audio stream (id:%d)\n",d_audio->id); + if(!ds_fill_buffer(d_audio)){ + mp_msg(MSGT_DEMUXER,MSGL_INFO,"ASF: " MSGTR_MissingAudioStream); + sh_audio=NULL; + } else { + sh_audio=d_audio->sh;sh_audio->ds=d_audio; + sh_audio->format=sh_audio->wf->wFormatTag; + } + } + break; + } + case DEMUXER_TYPE_MPEG_ES: { + sh_audio=NULL; // ES streams has no audio channel + d_video->sh=new_sh_video(demuxer,0); // create dummy video stream header, id=0 + sh_video=d_video->sh;sh_video->ds=d_video; + break; + } + case DEMUXER_TYPE_MPEG_PS: { + sh_video=d_video->sh;sh_video->ds=d_video; + if(demuxer->stream->type!=STREAMTYPE_VCD) demuxer->movi_start=0; // for VCD + + if(audio_id!=-2) { + if(!ds_fill_buffer(d_audio)){ + mp_msg(MSGT_DEMUXER,MSGL_INFO,"MPEG: " MSGTR_MissingAudioStream); + sh_audio=NULL; + } else { + sh_audio=d_audio->sh;sh_audio->ds=d_audio; + switch(d_audio->id & 0xE0){ // 1110 0000 b (high 3 bit: type low 5: id) + case 0x00: sh_audio->format=0x50;break; // mpeg + case 0xA0: sh_audio->format=0x10001;break; // dvd pcm + case 0x80: sh_audio->format=0x2000;break; // ac3 + default: sh_audio=NULL; // unknown type + } + } + } + break; + } +} // switch(file_format) + +return demuxer; +} + +int demux_seek_avi(demuxer_t *demuxer,float rel_seek_secs,int flags); +int demux_seek_asf(demuxer_t *demuxer,float rel_seek_secs,int flags); +int demux_seek_mpg(demuxer_t *demuxer,float rel_seek_secs,int flags); +void demux_seek_mov(demuxer_t *demuxer,float pts,int flags); + +int demux_seek(demuxer_t *demuxer,float rel_seek_secs,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; + +if(!demuxer->seekable){ + if(demuxer->file_format==DEMUXER_TYPE_AVI) + mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CantSeekRawAVI); + else + mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CantSeekFile); + return 0; +} + + // clear demux buffers: + if(sh_audio){ ds_free_packs(d_audio);sh_audio->a_buffer_len=0;} + ds_free_packs(d_video); + + demuxer->stream->eof=0; // clear eof flag + + if(sh_audio) sh_audio->timer=0; + sh_video->timer=0; // !!!!!! + +switch(demuxer->file_format){ + + case DEMUXER_TYPE_AVI: + demux_seek_avi(demuxer,rel_seek_secs,flags); break; + + case DEMUXER_TYPE_ASF: + demux_seek_asf(demuxer,rel_seek_secs,flags); break; + + case DEMUXER_TYPE_MPEG_ES: + case DEMUXER_TYPE_MPEG_PS: + demux_seek_mpg(demuxer,rel_seek_secs,flags); break; + + case DEMUXER_TYPE_MOV: + demux_seek_mov(demuxer,rel_seek_secs,flags); break; + +} // switch(demuxer->file_format) + +return 1; +} + + diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h new file mode 100644 index 0000000000..0cb3eea22e --- /dev/null +++ b/libmpdemux/demuxer.h @@ -0,0 +1,155 @@ + +#include "config.h" + +#define MAX_PACKS 4096 +#define MAX_PACK_BYTES 0x800000 + +#define DEMUXER_TYPE_UNKNOWN 0 +#define DEMUXER_TYPE_MPEG_ES 1 +#define DEMUXER_TYPE_MPEG_PS 2 +#define DEMUXER_TYPE_AVI 3 +#define DEMUXER_TYPE_AVI_NI 4 +#define DEMUXER_TYPE_AVI_NINI 5 +#define DEMUXER_TYPE_ASF 6 +#define DEMUXER_TYPE_MOV 7 + +#define DEMUXER_TIME_NONE 0 +#define DEMUXER_TIME_PTS 1 +#define DEMUXER_TIME_FILE 2 +#define DEMUXER_TIME_BPS 3 + + +// Holds one packet/frame/whatever +typedef struct demux_packet_st { + int len; + float pts; + off_t pos; // position in index (AVI) or file (MPG) + unsigned char* buffer; + int flags; // keyframe, etc + struct demux_packet_st* next; +} demux_packet_t; + +typedef struct { + int buffer_pos; // current buffer position + int buffer_size; // current buffer size + unsigned char* buffer; // current buffer + float 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 + int flags; // flags of current packet (keyframe etc) +//--------------- + 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 + int id; // stream ID (for multiple audio/video streams) + struct demuxer_st *demuxer; // parent demuxer structure (stream handler) +// ---- asf ----- + demux_packet_t *asf_packet; // read asf fragments here + int asf_seq; +// ---- stream header ---- + void* sh; +} demux_stream_t; + +typedef struct demuxer_st { + stream_t *stream; + int synced; // stream synced (used by mpeg) + off_t filepos; // input stream current pos. + int type; // demuxer type: mpeg PS, mpeg ES, avi, avi-ni, avi-nini, asf + int file_format; // file format: mpeg/avi/asf + off_t movi_start; + off_t movi_end; + int seekable; // flag + // + demux_stream_t *audio; // audio buffer/demuxer + demux_stream_t *video; // video buffer/demuxer + demux_stream_t *sub; // dvd subtitle buffer/demuxer + + // stream headers: + void* a_streams[256]; // audio streams (sh_audio_t) + void* v_streams[256]; // video sterams (sh_video_t) + char s_streams[32]; // dvd subtitles (flag) + + void* priv; // fileformat-dependent data +} demuxer_t; + +inline static demux_packet_t* new_demux_packet(int len){ + demux_packet_t* dp=malloc(sizeof(demux_packet_t)); + dp->len=len; + dp->buffer=malloc(len); + dp->next=NULL; + dp->pts=0; + dp->pos=0; + dp->flags=0; + return dp; +} + +inline static void free_demux_packet(demux_packet_t* dp){ + free(dp->buffer); + free(dp); +} + +demux_stream_t* new_demuxer_stream(struct demuxer_st *demuxer,int id); +demuxer_t* new_demuxer(stream_t *stream,int type,int a_id,int v_id,int s_id); +void free_demuxer_stream(demux_stream_t *ds); +void free_demuxer(demuxer_t *demuxer); + +void ds_add_packet(demux_stream_t *ds,demux_packet_t* dp); +void ds_read_packet(demux_stream_t *ds,stream_t *stream,int len,float pts,off_t pos,int flags); + +int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds); +int ds_fill_buffer(demux_stream_t *ds); + +inline static off_t ds_tell(demux_stream_t *ds){ + return (ds->dpos-ds->buffer_size)+ds->buffer_pos; +} + +inline static int ds_tell_pts(demux_stream_t *ds){ + return (ds->pts_bytes-ds->buffer_size)+ds->buffer_pos; +} + +int demux_read_data(demux_stream_t *ds,unsigned char* mem,int len); +int demux_read_data_pack(demux_stream_t *ds,unsigned char* mem,int len); + +#if 1 +#define demux_getc(ds) (\ + (ds->buffer_pos<ds->buffer_size) ? ds->buffer[ds->buffer_pos++] \ + :((!ds_fill_buffer(ds))? (-1) : ds->buffer[ds->buffer_pos++] ) ) +#else +inline static int demux_getc(demux_stream_t *ds){ + if(ds->buffer_pos>=ds->buffer_size){ + if(!ds_fill_buffer(ds)){ +// printf("DEMUX_GETC: EOF reached!\n"); + return -1; // EOF + } + } +// printf("[%02X]",ds->buffer[ds->buffer_pos]); + return ds->buffer[ds->buffer_pos++]; +} +#endif + +void ds_free_packs(demux_stream_t *ds); +int ds_get_packet(demux_stream_t *ds,unsigned char **start); +int ds_get_packet_sub(demux_stream_t *ds,unsigned char **start); + + +static inline int avi_stream_id(unsigned int id){ + unsigned char *p=(unsigned char *)&id; + unsigned char a,b; +#if WORDS_BIGENDIAN + a=p[3]-'0'; b=p[2]-'0'; +#else + a=p[0]-'0'; b=p[1]-'0'; +#endif + if(a>9 || b>9) return 100; // invalid ID + return a*10+b; +} + +demuxer_t* demux_open(stream_t *stream,int file_format,int aid,int vid,int sid); +int demux_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); + + + diff --git a/libmpdemux/dvdauth.c b/libmpdemux/dvdauth.c new file mode 100644 index 0000000000..65801d6dda --- /dev/null +++ b/libmpdemux/dvdauth.c @@ -0,0 +1,229 @@ +/* (C)2001 by LGB (Gabor Lenart), based on example programs in libcss + lgb@lgb.hu */ + +/* don't do anything with this source if css support was not requested */ +#include "config.h" +#ifdef HAVE_LIBCSS + +#include <stdio.h> +#include <stdlib.h> +//#include <string.h> // FIXME: conflicts with fs.h +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <css.h> +#if CSS_MAJOR_VERSION > 0 || (CSS_MAJOR_VERSION == 0 && CSS_MINOR_VERSION > 1) +# include <dvd.h> +# undef OLD_CSS_API +#else +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include <sys/dvdio.h> +# elif defined(__linux__) +# include <linux/cdrom.h> +# elif defined(__sun) +# include <sun/dvdio.h> +# else +# error "Need the DVD ioctls" +# endif +# define OLD_CSS_API 1 +#endif + +#include "dvdauth.h" + + +#if OLD_CSS_API +/* + * provide some backward compatibiliy macros to compile this + * code using the old libcss-0.1 + */ +#define DVDHandle int +#define DVDOpenFailed (-1) + +#define DVDAuth(hdl, s) ioctl(hdl, DVD_AUTH, s) +#define DVDOpenDevice(path) open(path, O_RDONLY) +#define DVDCloseDevice(hdl) close(hdl) +#define CSSDVDisEncrypted(hdl) CSSisEncrypted(hdl) +#define CSSDVDAuthDisc CSSAuthDisc +/* Arghhh! Please think before you commit! You forget to check the return + value of path_to_lba (-1 for error) in this way ... - LGB */ +//#define CSSDVDAuthTitlePath(hdl,key_title,path) \ +// CSSAuthTitle(hdl,key_title,path_to_lba(path)) + +#else /*OLD_CSS_API*/ + +#define DVDHandle struct dvd_device * +#define DVDOpenFailed NULL + +#endif /*OLD_CSS_API*/ + + +char *dvd_auth_device=NULL; +char *dvd_device=NULL; +char *dvd_raw_device=NULL; +unsigned char key_disc[2048]; +unsigned char key_title[5]; +unsigned char *dvdimportkey=NULL; +int descrambling=0; + + +#if OLD_CSS_API +/* + * With the old libcss-0.1 api, we have to find out the LBA for + * a title for title authentication. + */ +#ifdef __linux__ +#include <linux/fs.h> + +#ifndef FIBMAP +#define FIBMAP 1 +#endif + +static int path_to_lba (char *path) +{ + int lba = 0; + char cmd[100]; + FILE *fp; + + snprintf(cmd,sizeof(cmd),"fibmap_mplayer %s",path); + fp=popen(cmd,"r"); + if (fp) { + int ret; + bzero(cmd,sizeof(cmd)); + fgets(cmd,99,fp); + if ((ret=pclose(fp))) + fprintf(stderr,"fibmap_mplayer: %s\n",*cmd?cmd:"no error info"); + if(WIFEXITED(ret) && !WEXITSTATUS(ret)) + lba=atoi(cmd); + else + fp=NULL; + } + if (!fp) { + int fd; + printf("fibmap_mplayer could not run, trying with ioctl() ...\n"); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "Cannot open file %s: %s", + path ? path : "(NULL)", strerror(errno)); + return -1; + } + if (ioctl(fd, FIBMAP, &lba) != 0) { + perror ("ioctl FIBMAP"); + fprintf(stderr,"Hint: run mplayer as root (or better to install fibmap_mplayer as suid root)!\n"); + close(fd); + return -1; + } + close(fd); + } + printf("LBA: %d\n",lba); + return lba; +} + + +int CSSDVDAuthTitlePath(DVDHandle hdl,unsigned char *key_title,char *path) +{ + int lba=path_to_lba(path); + if (lba==-1) return -1; + return CSSAuthTitle(hdl,key_title,lba); +} + + +#else /*linux*/ +static int path_to_lba (char *path) +{ +#warning translating pathname to iso9660 LBA is not supported on this platform + fprintf(stderr, "Translating pathname to iso9660 LBA is not supported on this platform\n"); + return -1; +} +#endif /*linux*/ +#endif /*OLD_CSS_API*/ + + +static void reset_agids ( DVDHandle dvd ) +{ + dvd_authinfo ai; + int i; + for (i = 0; i < 4; i++) { + memset(&ai, 0, sizeof(ai)); + ai.type = DVD_INVALIDATE_AGID; + ai.lsa.agid = i; + DVDAuth(dvd, &ai); + } +} + + +int dvd_import_key ( unsigned char *hexkey ) +{ + unsigned char *t=key_title; + int digit=4,len; + bzero(key_title,sizeof(key_title)); +// printf("DVD key: %s\n",hexkey); + for (len=0;len<10;len++) { +// printf("-> %c\n",*hexkey); + if (!*hexkey) return 1; + if (*hexkey>='A'&&*hexkey<='F') *t|=(*hexkey-'A'+10)<<digit; + else if (*hexkey>='0'&&*hexkey<='9') *t|=(*hexkey-'0')<<digit; + else return 1; + if (digit) digit=0; else { + digit=4; + t++; + } + hexkey++; + } + if (*hexkey) return 1; + printf("DVD key (requested): %02X%02X%02X%02X%02X\n",key_title[0],key_title[1],key_title[2],key_title[3],key_title[4]); + descrambling=1; + return 0; +} + + + +int dvd_auth ( char *dev , char *filename ) +{ + DVDHandle dvd; /* DVD device handle */ + + if ((dvd=DVDOpenDevice(dev)) == DVDOpenFailed) { + fprintf(stderr,"DVD: cannot open DVD device \"%s\": %s.\n", + dev, strerror(errno)); + return 1; + } + + if (!CSSDVDisEncrypted(dvd)) { + printf("DVD is unencrypted! Skipping authentication!\n(note: you should not use -dvd switch for unencrypted discs!)\n"); + DVDCloseDevice(dvd); + return 0; + } else printf("DVD is encrypted, issuing authentication ...\n"); + + /* reset AGIDs */ + reset_agids(dvd); + + /* authenticate disc */ + if (CSSDVDAuthDisc(dvd,key_disc)) { + fprintf(stderr,"DVD: CSSDVDAuthDisc() failed.\n"); + DVDCloseDevice(dvd); + return 1; + } + + if (CSSDVDAuthTitlePath(dvd,key_title,filename)) { + fprintf(stderr,"DVD: CSSDVDAuthTitle() failed.\n"); + DVDCloseDevice(dvd); + return 1; + } + + /* decrypting title */ + if (CSSDecryptTitleKey (key_title, key_disc) < 0) { + fprintf(stderr,"DVD: CSSDecryptTitleKey() failed.\n"); + DVDCloseDevice(dvd); + return 1; + } + + DVDCloseDevice(dvd); + printf("DVD title key is: %02X%02X%02X%02X%02X\n",key_title[0],key_title[1],key_title[2],key_title[3],key_title[4]); + descrambling=1; + return 0; +} + + +#endif diff --git a/libmpdemux/dvdauth.h b/libmpdemux/dvdauth.h new file mode 100644 index 0000000000..68ad752c0a --- /dev/null +++ b/libmpdemux/dvdauth.h @@ -0,0 +1,16 @@ +//#include "config.h" +#ifdef HAVE_LIBCSS +#ifndef _MPLAYER_CSS_H +#define _MPLAYER_CSS_H + +extern char *dvd_auth_device; +extern unsigned char key_disc[]; +extern unsigned char key_title[]; +extern unsigned char *dvdimportkey; +extern int descrambling; + +int dvd_auth ( char *, char * ); +int dvd_import_key ( unsigned char * ); + +#endif +#endif diff --git a/libmpdemux/help_mp.h b/libmpdemux/help_mp.h new file mode 100644 index 0000000000..c714314853 --- /dev/null +++ b/libmpdemux/help_mp.h @@ -0,0 +1 @@ +#include "../help_mp.h" diff --git a/libmpdemux/http.c b/libmpdemux/http.c new file mode 100644 index 0000000000..75157fccf0 --- /dev/null +++ b/libmpdemux/http.c @@ -0,0 +1,296 @@ +/* + * HTTP Helper + * by Bertrand Baudet <bertrand_baudet@yahoo.com> + * (C) 2001, MPlayer team. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "http.h" + +HTTP_header_t * +http_new_header() { + HTTP_header_t *http_hdr; + + http_hdr = (HTTP_header_t*)malloc(sizeof(HTTP_header_t)); + if( http_hdr==NULL ) return NULL; + memset( http_hdr, 0, sizeof(HTTP_header_t) ); + + return http_hdr; +} + +void +http_free( HTTP_header_t *http_hdr ) { + int i; + if( http_hdr==NULL ) return; + if( http_hdr->protocol!=NULL ) free( http_hdr->protocol ); + if( http_hdr->uri!=NULL ) free( http_hdr->uri ); + if( http_hdr->reason_phrase!=NULL ) free( http_hdr->reason_phrase ); + if( http_hdr->body!=NULL ) free( http_hdr->body ); + if( http_hdr->field_search!=NULL ) free( http_hdr->field_search ); + if( http_hdr->method!=NULL ) free( http_hdr->method ); + if( http_hdr->buffer!=NULL ) free( http_hdr->buffer ); + for( i=0 ; i<http_hdr->field_nb ; i++ ) + if( http_hdr->fields[i]!=NULL ) free( http_hdr->fields[i] ); + free( http_hdr ); +} + +int +http_response_append( HTTP_header_t *http_hdr, char *response, int length ) { + char *ptr = NULL; + if( http_hdr==NULL || response==NULL || length<0 ) return -1; + ptr = (char*)malloc( http_hdr->buffer_size+length ); + if( ptr==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + if( http_hdr->buffer_size==0 ) { + // Buffer empty, copy response into it. + memcpy( ptr, response, length ); + http_hdr->buffer_size = length; + } else { + // Buffer not empty, grow buffer, copy and append the response. + memcpy( ptr, http_hdr->buffer, http_hdr->buffer_size ); + free( http_hdr->buffer ); + memcpy( ptr+http_hdr->buffer_size, response, length ); + http_hdr->buffer_size += length; + } + http_hdr->buffer = ptr; + return http_hdr->buffer_size; +} + +int +http_is_header_entired( HTTP_header_t *http_hdr ) { + if( http_hdr==NULL ) return -1; + + if( strstr(http_hdr->buffer, "\r\n\r\n")==NULL ) return 0; + else return 1; +} + +int +http_response_parse( HTTP_header_t *http_hdr ) { + char *hdr_ptr, *ptr; + char *field=NULL; + int pos_hdr_sep, len; + if( http_hdr==NULL ) return -1; + if( http_hdr->is_parsed ) return 0; + + // Get the protocol + hdr_ptr = strstr( http_hdr->buffer, " " ); + if( hdr_ptr==NULL ) { + printf("Malformed answer. No space separator found.\n"); + return -1; + } + len = hdr_ptr-http_hdr->buffer; + http_hdr->protocol = (char*)malloc(len+1); + if( http_hdr->protocol==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + strncpy( http_hdr->protocol, http_hdr->buffer, len ); + http_hdr->protocol[len]='\0'; + if( !strncasecmp( http_hdr->protocol, "HTTP", 4) ) { + if( sscanf( http_hdr->protocol+5,"1.%d", &(http_hdr->http_minor_version) )!=1 ) { + printf("Malformed answer. Unable to get HTTP minor version.\n"); + return -1; + } + } + + // Get the status code + if( sscanf( ++hdr_ptr, "%d", &(http_hdr->status_code) )!=1 ) { + printf("Malformed answer. Unable to get status code.\n"); + return -1; + } + hdr_ptr += 4; + + // Get the reason phrase + ptr = strstr( hdr_ptr, "\r\n" ); + if( hdr_ptr==NULL ) { + printf("Malformed answer. Unable to get the reason phrase.\n"); + return -1; + } + len = ptr-hdr_ptr; + http_hdr->reason_phrase = (char*)malloc(len+1); + if( http_hdr->reason_phrase==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + strncpy( http_hdr->reason_phrase, hdr_ptr, len ); + http_hdr->reason_phrase[len]='\0'; + + // Set the position of the header separator: \r\n\r\n + ptr = strstr( http_hdr->buffer, "\r\n\r\n" ); + if( ptr==NULL ) { + printf("Header may be incomplete. No CRLF CRLF found.\n"); + return -1; + } + pos_hdr_sep = ptr-http_hdr->buffer; + + hdr_ptr = strstr( http_hdr->buffer, "\r\n" )+2; + do { + ptr = strstr( hdr_ptr, "\r\n"); + if( ptr==NULL ) { + printf("No CRLF found\n"); + return -1; + } + len = ptr-hdr_ptr; + field = (char*)realloc(field, len+1); + if( field==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + strncpy( field, hdr_ptr, len ); + field[len]='\0'; + http_set_field( http_hdr, field ); + hdr_ptr = ptr+2; + } while( hdr_ptr<(http_hdr->buffer+pos_hdr_sep) ); + + if( field!=NULL ) free( field ); + + if( pos_hdr_sep+4<http_hdr->buffer_size ) { + // Response has data! + int data_length = http_hdr->buffer_size-(pos_hdr_sep+4); + http_hdr->body = (char*)malloc( data_length ); + if( http_hdr->body==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + memcpy( http_hdr->body, http_hdr->buffer+pos_hdr_sep+4, data_length ); + http_hdr->body_size = data_length; + } + + http_hdr->is_parsed = 1; + return 0; +} + +char * +http_build_request( HTTP_header_t *http_hdr ) { + char *ptr; + int i; + int len; + if( http_hdr==NULL ) return NULL; + + if( http_hdr->method==NULL ) http_set_method( http_hdr, "GET"); + if( http_hdr->uri==NULL ) http_set_uri( http_hdr, "/"); + + // Compute the request length + len = strlen(http_hdr->method)+strlen(http_hdr->uri)+12; // Method line + for( i=0 ; i<http_hdr->field_nb ; i++ ) // Fields + len += strlen(http_hdr->fields[i])+2; + len += 2; // CRLF + if( http_hdr->body!=NULL ) { + len += http_hdr->body_size; + } + if( http_hdr->buffer!=NULL ) { + free( http_hdr->buffer ); + http_hdr->buffer = NULL; + } + http_hdr->buffer = (char*)malloc(len); + if( http_hdr->buffer==NULL ) { + printf("Memory allocation failed\n"); + return NULL; + } + http_hdr->buffer_size = len; + + ptr = http_hdr->buffer; + ptr += sprintf( ptr, "%s %s HTTP/1.%d\r\n", http_hdr->method, http_hdr->uri, http_hdr->http_minor_version ); + for( i=0 ; i<http_hdr->field_nb ; i++ ) + ptr += sprintf( ptr, "%s\r\n", http_hdr->fields[i] ); + ptr += sprintf( ptr, "\r\n" ); + if( http_hdr->body!=NULL ) { + memcpy( ptr, http_hdr->body, http_hdr->body_size ); + } + return http_hdr->buffer; +} + +char * +http_get_field( HTTP_header_t *http_hdr, const char *field_name ) { + if( http_hdr==NULL || field_name==NULL ) return NULL; + http_hdr->search_pos = 0; + if( http_hdr->field_search!=NULL ) free( http_hdr->field_search ); + http_hdr->field_search = (char*)malloc(strlen(field_name)+1); + if( http_hdr->field_search==NULL ) { + printf("Memory allocation failed\n"); + return NULL; + } + strcpy( http_hdr->field_search, field_name ); + return http_get_next_field( http_hdr ); +} + +char * +http_get_next_field( HTTP_header_t *http_hdr ) { + char *ptr; + int i; + if( http_hdr==NULL ) return NULL; + + for( i=http_hdr->search_pos ; i<http_hdr->field_nb ; i++ ) { + ptr = strstr( http_hdr->fields[i], ":" ); + if( ptr==NULL ) return NULL; + if( !strncasecmp( http_hdr->fields[i], http_hdr->field_search, ptr-http_hdr->fields[i] ) ) { + ptr++; // Skip the column + while( ptr[0]==' ' ) ptr++; // Skip the spaces if there is some + http_hdr->search_pos = i+1; + return ptr; // return the value without the field name + } + } + return NULL; +} + +void +http_set_field( HTTP_header_t *http_hdr, const char *field ) { + int pos; + if( http_hdr==NULL || field==NULL ) return; + + pos = http_hdr->field_nb; + + http_hdr->fields[pos] = (char*)malloc(strlen(field)+1); + if( http_hdr->fields[pos]==NULL ) { + printf("Memory allocation failed\n"); + return; + } + http_hdr->field_nb++; + strcpy( http_hdr->fields[pos], field ); +} + +void +http_set_method( HTTP_header_t *http_hdr, const char *method ) { + if( http_hdr==NULL || method==NULL ) return; + + http_hdr->method = (char*)malloc(strlen(method)+1); + if( http_hdr->method==NULL ) { + printf("Memory allocation failed\n"); + return; + } + strcpy( http_hdr->method, method ); +} + +void +http_set_uri( HTTP_header_t *http_hdr, const char *uri ) { + if( http_hdr==NULL || uri==NULL ) return; + + http_hdr->uri = (char*)malloc(strlen(uri)+1); + if( http_hdr->uri==NULL ) { + printf("Memory allocation failed\n"); + return; + } + strcpy( http_hdr->uri, uri ); +} + +void +http_debug_hdr( HTTP_header_t *http_hdr ) { + int i; + if( http_hdr==NULL ) return; + + printf("protocol: %s\n", http_hdr->protocol ); + printf("http minor version: %d\n", http_hdr->http_minor_version ); + printf("uri: %s\n", http_hdr->uri ); + printf("method: %s\n", http_hdr->method ); + printf("status code: %d\n", http_hdr->status_code ); + printf("reason phrase: %s\n", http_hdr->reason_phrase ); + + printf("Fields:\n"); + for( i=0 ; i<http_hdr->field_nb ; i++ ) + printf(" %d - %s\n", i, http_hdr->fields[i] ); +} diff --git a/libmpdemux/http.h b/libmpdemux/http.h new file mode 100644 index 0000000000..f8919288ae --- /dev/null +++ b/libmpdemux/http.h @@ -0,0 +1,44 @@ +/* + * HTTP Helper + * by Bertrand Baudet <bertrand_baudet@yahoo.com> + * (C) 2001, MPlayer team. + */ + +#ifndef __HTTP_H +#define __HTTP_H + +#define HTTP_FIELD_MAX 20 + +typedef struct { + char *protocol; + char *method; + char *uri; + int status_code; + char *reason_phrase; + int http_minor_version; + char *fields[HTTP_FIELD_MAX]; + int field_nb; + char *field_search; + int search_pos; + char *body; + int body_size; + char *buffer; + int buffer_size; + int is_parsed; +} HTTP_header_t; + +HTTP_header_t* http_new_header(); +void http_free( HTTP_header_t *http_hdr ); +int http_response_append( HTTP_header_t *http_hdr, char *data, int length ); +int http_response_parse( HTTP_header_t *http_hdr ); +int http_is_header_entired( HTTP_header_t *http_hdr ); +char* http_build_request( HTTP_header_t *http_hdr ); +char* http_get_field( HTTP_header_t *http_hdr, const char *field_name ); +char* http_get_next_field( HTTP_header_t *http_hdr ); +void http_set_field( HTTP_header_t *http_hdr, const char *field ); +void http_set_method( HTTP_header_t *http_hdr, const char *method ); +void http_set_uri( HTTP_header_t *http_hdr, const char *uri ); + +void http_debug_hdr( HTTP_header_t *http_hdr ); + +#endif // __HTTP_H diff --git a/libmpdemux/mp_msg.h b/libmpdemux/mp_msg.h new file mode 100644 index 0000000000..0f8ceae361 --- /dev/null +++ b/libmpdemux/mp_msg.h @@ -0,0 +1,2 @@ +/* Let it be for now*/ +#include "../mp_msg.h" diff --git a/libmpdemux/network.c b/libmpdemux/network.c new file mode 100644 index 0000000000..45299b8e4d --- /dev/null +++ b/libmpdemux/network.c @@ -0,0 +1,592 @@ +/* + * Network layer for MPlayer + * by Bertrand BAUDET <bertrand_baudet@yahoo.com> + * (C) 2001, MPlayer team. + */ + +//#define DUMP2FILE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <pthread.h> + +#include <errno.h> +#include <ctype.h> + +#include "stream.h" +#include "demuxer.h" + +#include "network.h" +#include "http.h" +#include "url.h" +#include "asf.h" + +streaming_ctrl_t *streaming_ctrl; + +static ASF_StreamType_e streaming_type = ASF_Unknown_e; + +Net_Fifo * +net_fifo_new() { + Net_Fifo *net_fifo; + net_fifo = (Net_Fifo*)malloc(sizeof(Net_Fifo)); + if( net_fifo==NULL ) { + printf("Memory allocation failed\n"); + return NULL; + } + memset( net_fifo, 0, sizeof(Net_Fifo) ); + return net_fifo; +} + +void +net_fifo_free( Net_Fifo *net_fifo ) { + if( net_fifo->buffer!=NULL ) free( net_fifo->buffer ); + free( net_fifo ); +} + +int +net_fifo_push(Net_Fifo *net_fifo, char *buffer, int length ) { + char *ptr; + if( net_fifo==NULL || buffer==NULL || length<0 ) return -1; + + ptr = (char*)malloc(length+net_fifo->length); + if( ptr==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + if( net_fifo->buffer!=NULL ) { + memcpy( ptr, net_fifo->buffer, net_fifo->length ); + free( net_fifo->buffer ); + } + memcpy( ptr+net_fifo->length, buffer, length ); + net_fifo->buffer = ptr; + net_fifo->length += length; + return net_fifo->length; +} + +int +net_fifo_pop(Net_Fifo *net_fifo, char *buffer, int length ) { + char *ptr; + int len; + if( net_fifo==NULL || buffer==NULL || length<0 ) return -1; + if( net_fifo->buffer==NULL || net_fifo->length==0 ) return -1; + + len = MIN(net_fifo->length, length); + + ptr = (char*)malloc(net_fifo->length-len); + if( ptr==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + memcpy( buffer, net_fifo->buffer, len ); + if( net_fifo->length-len!=0 ) { + memcpy( ptr, net_fifo->buffer+len, net_fifo->length-len ); + free( net_fifo->buffer ); + net_fifo->buffer = ptr; + net_fifo->length -= len; + } else { + free( net_fifo->buffer ); + net_fifo->buffer = NULL; + net_fifo->length = 0; + } + return len; +} + +streaming_ctrl_t * +streaming_ctrl_new( ) { + streaming_ctrl_t *streaming_ctrl; + streaming_ctrl = (streaming_ctrl_t*)malloc(sizeof(streaming_ctrl_t)); + if( streaming_ctrl==NULL ) { + printf("Failed to allocate memory\n"); + return NULL; + } + memset( streaming_ctrl, 0, sizeof(streaming_ctrl_t) ); + streaming_ctrl->buffer = net_fifo_new(); + return streaming_ctrl; +} + +void +streaming_ctrl_free( streaming_ctrl_t *streaming_ctrl ) { + if( streaming_ctrl==NULL ) return; + if( streaming_ctrl->buffer!=NULL ) net_fifo_free( streaming_ctrl->buffer ); + free( streaming_ctrl ); +} + +int +readFromServer(int fd, char *buffer, int length) { + int ret; + int done=0; + fd_set set; + struct timeval tv; + if( buffer==NULL || length<0 ) return -1; + + +// fcntl( fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK ); + return read( fd, buffer, length ); + + do { + tv.tv_sec = 0; + tv.tv_usec = 10000; // 10 milli-seconds timeout + FD_ZERO( &set ); + FD_SET( fd, &set ); + ret = select( fd+1, &set, NULL, NULL, &tv ); + if( ret<0 ) { + perror("select"); + } else if( ret==0 ) { + printf("timeout\n"); + } + if( FD_ISSET(fd, &set) ) { + ret = read( fd, buffer, length ); + if( ret<0 ) { + if( errno!=EINPROGRESS ) { + } + } else { + done = 1; + } + } else { + return -1; + } + } while( !done ); + + return ret; +} + +// Connect to a server using a TCP connection +int +connect2Server(char *host, int port) { + int socket_server_fd; + int err, err_len; + fd_set set; + struct timeval tv; + struct sockaddr_in server_address; + + printf("Connecting to server %s:%d ...\n", host, port ); + + socket_server_fd = socket(AF_INET, SOCK_STREAM, 0); +// fcntl( socket_server_fd, F_SETFL, fcntl(socket_server_fd, F_GETFL) | O_NONBLOCK ); + if( socket_server_fd==-1 ) { + perror("Failed to create socket"); + return -1; + } + + if( isalpha(host[0]) ) { + struct hostent *hp =(struct hostent*)gethostbyname( host ); + if( hp==NULL ) { + printf("Counldn't resolve name: %s\n", host); + return -1; + } + memcpy( (void*)&server_address.sin_addr.s_addr, (void*)hp->h_addr, hp->h_length ); + } else { + inet_pton(AF_INET, host, &server_address.sin_addr); + } + server_address.sin_family=AF_INET; + server_address.sin_port=htons(port); + + if( connect( socket_server_fd, (struct sockaddr*)&server_address, sizeof(server_address) )==-1 ) { + if( errno!=EINPROGRESS ) { + perror("Failed to connect to server"); + close(socket_server_fd); + return -1; + } + } + + tv.tv_sec = 0; + tv.tv_usec = 10000; // 10 milli-seconds timeout + FD_ZERO( &set ); + FD_SET( socket_server_fd, &set ); + if( select(socket_server_fd+1, NULL, &set, NULL, &tv)>0 ) { + err_len = sizeof( err ); + getsockopt( socket_server_fd, SOL_SOCKET, SO_ERROR, &err, &err_len ); + if( err ) { + printf("Couldn't connect to host %s\n", host ); + printf("Socket error: %d\n", err ); + close(socket_server_fd); + return -1; + } + } + return socket_server_fd; +} + +int +http_send_request( URL_t *url ) { + HTTP_header_t *http_hdr; + int fd; + http_hdr = http_new_header(); + http_set_uri( http_hdr, url->file ); + http_set_field( http_hdr, "User-Agent: MPlayer"); + http_set_field( http_hdr, "Connection: closed"); + if( http_build_request( http_hdr )==NULL ) { + return -1; + } + + fd = connect2Server( url->hostname, url->port ); + if( fd<0 ) { + return -1; + } + write( fd, http_hdr->buffer, http_hdr->buffer_size ); + http_free( http_hdr ); + + return fd; +} + +HTTP_header_t * +http_read_response( int fd ) { + HTTP_header_t *http_hdr; + char response[BUFFER_SIZE]; + int i; + + http_hdr = http_new_header(); + if( http_hdr==NULL ) { + return NULL; + } + + do { + i = readFromServer( fd, response, BUFFER_SIZE ); + if( i<0 ) { + printf("Read failed\n"); + } + http_response_append( http_hdr, response, i ); + } while( !http_is_header_entired( http_hdr ) ); + http_response_parse( http_hdr ); + return http_hdr; +} + +// By using the protocol, the extension of the file or the content-type +// we might be able to guess the streaming type. +int +autodetectProtocol(URL_t *url, int *fd_out) { + HTTP_header_t *http_hdr; + int fd=-1; + int i; + int redirect; + char *extension; + char *content_type; + char *next_url; + char response[1024]; + + do { + *fd_out=-1; + next_url = NULL; + extension = NULL; + content_type = NULL; + redirect = 0; + + if( url==NULL ) return DEMUXER_TYPE_UNKNOWN; + + // Get the extension of the file if present + if( url->file!=NULL ) { + for( i=strlen(url->file) ; i>0 ; i-- ) { + if( url->file[i]=='.' ) { + extension=(url->file)+i+1; + break; + } + } + } +// extension=NULL; + if( extension!=NULL ) { + printf("Extension: %s\n", extension ); + if( !strcasecmp(extension, "asf") || + !strcasecmp(extension, "wmv") || + !strcasecmp(extension, "asx") ) { + if( url->port==0 ) url->port = 80; + return DEMUXER_TYPE_ASF; + } + if( !strcasecmp(extension, "mpg") || + !strcasecmp(extension, "mpeg") ) { + if( url->port==0 ) url->port = 80; + return DEMUXER_TYPE_MPEG_PS; + } + if( !strcasecmp(extension, "avi") ) { + if( url->port==0 ) url->port = 80; + return DEMUXER_TYPE_AVI; + } + } + + // Checking for RTSP + if( !strcasecmp(url->protocol, "rtsp") ) { + printf("RTSP protocol not yet implemented!\n"); + return DEMUXER_TYPE_UNKNOWN; + } + + // Checking for ASF + if( !strcasecmp(url->protocol, "mms") ) { + if( url->port==0 ) url->port = 80; + return DEMUXER_TYPE_ASF; + } + + // HTTP based protocol + if( !strcasecmp(url->protocol, "http") ) { + if( url->port==0 ) url->port = 80; + + fd = http_send_request( url ); + if( fd<0 ) { + *fd_out=-1; + return DEMUXER_TYPE_UNKNOWN; + } + + http_hdr = http_read_response( fd ); + if( http_hdr==NULL ) { + close( fd ); + *fd_out=-1; + return DEMUXER_TYPE_UNKNOWN; + } + + *fd_out=fd; + //http_debug_hdr( http_hdr ); + + // Check if the response is an ICY status_code reason_phrase + if( !strcasecmp(http_hdr->protocol, "ICY") ) { + // Ok, we have detected an mp3 streaming + return DEMUXER_TYPE_MPEG_PS; + } + + switch( http_hdr->status_code ) { + case 200: // OK + // Look if we can use the Content-Type + content_type = http_get_field( http_hdr, "Content-Type" ); + if( content_type!=NULL ) { + printf("Content-Type: [%s]\n", content_type ); + printf("Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") ); + // Check for ASF + if( asf_http_streaming_type(content_type, NULL)!=ASF_Unknown_e ) { + return DEMUXER_TYPE_ASF; + } + // Check for MP3 streaming + // Some MP3 streaming server answer with audio/mpeg + if( !strcasecmp(content_type, "audio/mpeg") ) { + return DEMUXER_TYPE_MPEG_PS; + } + // Check for MPEG streaming + if( !strcasecmp(content_type, "video/mpeg") ) { + return DEMUXER_TYPE_MPEG_PS; + } + // AVI ??? => video/x-msvideo + if( !strcasecmp(content_type, "video/x-msvideo") ) { + return DEMUXER_TYPE_AVI; + } + } + break; + // Redirect + case 301: // Permanently + case 302: // Temporarily + // TODO: RFC 2616, recommand to detect infinite redirection loops + next_url = http_get_field( http_hdr, "Location" ); + if( next_url!=NULL ) { + close( fd ); + url_free( url ); + url = url_new( next_url ); + redirect = 1; + } + break; + default: + printf("Server returned %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); + close( fd ); + *fd_out=-1; + return DEMUXER_TYPE_UNKNOWN; + } + } + } while( redirect ); + + return DEMUXER_TYPE_UNKNOWN; +} + +int +nop_streaming_read( streaming_ctrl_t *streaming_ctrl ) { + char *buffer; + int len; + if( streaming_ctrl==NULL ) return -1; + len = streaming_ctrl->buffer->length; + if( len==0 ) return 0; + + buffer = (char*)malloc( len ); + if( buffer==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + net_fifo_pop( streaming_ctrl->buffer, buffer, len ); + write( streaming_ctrl->fd_pipe_in, buffer, len ); + free( buffer ); + return len; +} + +int +nop_streaming_start( streaming_ctrl_t *streaming_ctrl ) { + HTTP_header_t *http_hdr; + int fd; + if( streaming_ctrl==NULL ) return -1; + + fd = streaming_ctrl->fd_net; + if( fd<0 ) { + fd = http_send_request( *(streaming_ctrl->url) ); + if( fd<0 ) return -1; + http_hdr = http_read_response( fd ); + if( http_hdr==NULL ) return -1; + + switch( http_hdr->status_code ) { + case 200: // OK + printf("Content-Type: [%s]\n", http_get_field(http_hdr, "Content-Type") ); + printf("Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") ); + if( http_hdr->body_size>0 ) { + write( streaming_ctrl->fd_pipe_in, http_hdr->body, http_hdr->body_size ); + } + break; + default: + printf("Server return %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); + close( fd ); + fd = -1; + } + streaming_ctrl->fd_net = fd; + } + + http_free( http_hdr ); + + streaming_ctrl->streaming_read = nop_streaming_read; + streaming_ctrl->prebuffer_size = 180000; +// streaming_ctrl->prebuffer_size = 0; + streaming_ctrl->buffering = 1; +// streaming_ctrl->buffering = 0; + streaming_ctrl->status = streaming_playing_e; + return fd; +} + +void +network_streaming(void *arg) { + char buffer[BUFFER_SIZE]; + fd_set fd_net_in; + int ret; + + arg = arg; + + do { + FD_ZERO( &fd_net_in ); + FD_SET( streaming_ctrl->fd_net, &fd_net_in ); + + ret = select( streaming_ctrl->fd_net+1, &fd_net_in, NULL, NULL, NULL ); + if( ret<0 ) { + perror("select"); + return; //exit(1); // FIXME! + } + if( FD_ISSET( streaming_ctrl->fd_net, &fd_net_in ) ) { + ret = readFromServer( streaming_ctrl->fd_net, buffer, BUFFER_SIZE ); + if( ret<=0 ) { + streaming_ctrl->status=streaming_stopped_e; + } else { +//printf(" push: 0x%02X\n", *((unsigned int*)buffer) ); + net_fifo_push( streaming_ctrl->buffer, buffer, ret ); + if( !streaming_ctrl->buffering ) { + do { + ret = streaming_ctrl->streaming_read( streaming_ctrl ); + if( ret<0 && streaming_ctrl->buffer->length<streaming_ctrl->prebuffer_size ) { + // Need buffering + streaming_ctrl->buffering = 1; + } + } while( streaming_ctrl->buffer->length>streaming_ctrl->prebuffer_size ); + } else { + if( streaming_ctrl->buffer->length>streaming_ctrl->prebuffer_size ) { + streaming_ctrl->buffering = 0; + printf("\n"); + } else { + printf(" Buffering: %d \%\r", (int)((float)(((float)streaming_ctrl->buffer->length)/((float)streaming_ctrl->prebuffer_size))*100) ); + fflush(stdout); + } + } + } + } else { + printf("Network fd not set\n"); + } + } while( streaming_ctrl->status==streaming_playing_e ); + + // Flush the buffer + while( streaming_ctrl->buffer->length>0 ) { + ret = streaming_ctrl->streaming_read( streaming_ctrl ); + if( ret<0 ) break; + } + +printf("Network thread done\n"); + + // Close to the pipe to stop mplayer. + close( streaming_ctrl->fd_pipe_in ); + +} + +int +streaming_start(URL_t **url, int fd, int streaming_type) { + int fd_pipe[2]; + // Open the pipe + if( pipe(fd_pipe)<0 ) { + printf("Pipe creation failed\n"); + return -1; + } + + streaming_ctrl = streaming_ctrl_new( ); + if( streaming_ctrl==NULL ) { + return -1; + } + streaming_ctrl->url = url; + streaming_ctrl->fd_pipe_in = fd_pipe[1]; + streaming_ctrl->fd_net = fd; + +#ifdef DUMP2FILE +{ + int fd_file; + fd_file = open("dump.stream", O_WRONLY | O_CREAT ); + if( fd_file<0 ) { + perror("open"); + } + streaming_ctrl->fd_pipe_in = fd_file; +} +#endif + + switch( streaming_type ) { + case DEMUXER_TYPE_ASF: + // Send the appropriate HTTP request + fd = asf_http_streaming_start( streaming_ctrl ); + break; + case DEMUXER_TYPE_AVI: + case DEMUXER_TYPE_MPEG_ES: + case DEMUXER_TYPE_MPEG_PS: + fd = nop_streaming_start( streaming_ctrl ); + break; + case DEMUXER_TYPE_UNKNOWN: + default: + printf("Unable to detect the streaming type\n"); + close( fd ); + free( streaming_ctrl ); + return -1; + } + + if( fd<0 ) { + free( streaming_ctrl ); + return -1; + } + + // Start the network thread + if( pthread_create( &(streaming_ctrl->thread_id), NULL , (void*)network_streaming, (void*)NULL)<0 ) { + printf("Unable to start the network thread.\n"); + close( fd ); + free( streaming_ctrl ); + return -1; + } +printf("Network thread created with id: %d\n", streaming_ctrl->thread_id ); + +// streaming_ctrl->status = streaming_stopped_e; + +// return fd; + return fd_pipe[0]; +} + +int +streaming_stop( ) { + streaming_ctrl->status = streaming_stopped_e; + return 0; +} diff --git a/libmpdemux/network.h b/libmpdemux/network.h new file mode 100644 index 0000000000..4ef14500e7 --- /dev/null +++ b/libmpdemux/network.h @@ -0,0 +1,49 @@ +/* + * Network layer for MPlayer + * by Bertrand BAUDET <bertrand_baudet@yahoo.com> + * (C) 2001, MPlayer team. + */ + +#ifndef __NETWORK_H +#define __NETWORK_H + +#include <pthread.h> + +#include "stream.h" + +#include "url.h" + +#define BUFFER_SIZE 2048 + +typedef enum { + streaming_stopped_e, + streaming_playing_e +} streaming_status; + +typedef struct { + char *buffer; + int length; +} Net_Fifo; + +typedef struct streaming_control { + URL_t **url; + int fd_net; + int fd_pipe_in; + streaming_status status; + pthread_t thread_id; + Net_Fifo *buffer; + int buffering; // boolean + int prebuffer_size; + int (*streaming_read)( struct streaming_control *stream_ctrl ); +} streaming_ctrl_t; + +Net_Fifo* net_fifo_new( ); +void net_fifo_free(Net_Fifo *net_fifo ); +int net_fifo_pop(Net_Fifo *net_fifo, char *buffer, int length ); +int net_fifo_push(Net_Fifo *net_fifo, char *buffer, int length ); + +int connect2Server(char *host, int port); +int readFromServer(int fd, char *buffer, int length ); +int autodetectProtocol( URL_t *url, int *fd_out ); + +#endif diff --git a/libmpdemux/open.c b/libmpdemux/open.c new file mode 100644 index 0000000000..774e65c32c --- /dev/null +++ b/libmpdemux/open.c @@ -0,0 +1,437 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#ifdef __FreeBSD__ +#include <sys/cdrio.h> +#endif + +#include "stream.h" +#include "demuxer.h" + +#ifdef STREAMING +#include "url.h" +#include "network.h" +static URL_t* url; +#endif + +int dvd_title=0; +int dvd_chapter=1; +int dvd_angle=1; + +#ifdef USE_DVDREAD + +#include <dvdread/dvd_reader.h> +#include <dvdread/ifo_types.h> +#include <dvdread/ifo_read.h> +#include <dvdread/nav_read.h> + +#define DVDREAD_VERSION(maj,min,micro) ((maj)*10000 + (min)*100 + (micro)) + +/* + * Try to autodetect the libdvd-0.9.0 library + * (0.9.0 removed the <dvdread/dvd_udf.h> header, and moved the two defines + * DVD_VIDEO_LB_LEN and MAX_UDF_FILE_NAME_LEN from it to + * <dvdread/dvd_reader.h>) + */ +#if defined(DVD_VIDEO_LB_LEN) && defined(MAX_UDF_FILE_NAME_LEN) +#define LIBDVDREAD_VERSION DVDREAD_VERSION(0,9,0) +#else +#define LIBDVDREAD_VERSION DVDREAD_VERSION(0,8,0) +#endif + + +typedef struct { + dvd_reader_t *dvd; + dvd_file_t *title; + ifo_handle_t *vmg_file; + tt_srpt_t *tt_srpt; + ifo_handle_t *vts_file; + vts_ptt_srpt_t *vts_ptt_srpt; + pgc_t *cur_pgc; + // + int cur_cell; + int cur_pack; + int cell_last_pack; + // Navi: + int packs_left; + dsi_t dsi_pack; + int angle_seek; +} dvd_priv_t; + +#endif + +extern int vcd_get_track_end(int fd,int track); + +// Open a new stream (stdin/file/vcd/url) + +stream_t* open_stream(char* filename,int vcd_track,int* file_format){ +stream_t* stream=NULL; +int f=-1; +off_t len; +#ifdef VCD_CACHE +int vcd_cache_size=128; +#endif +#ifdef __FreeBSD__ +int bsize = VCD_SECTOR_SIZE; +#endif + +//============ Open VideoCD track ============== +if(vcd_track){ + int ret,ret2; + if(!filename) filename=DEFAULT_CDROM_DEVICE; + f=open(filename,O_RDONLY); + if(f<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CdDevNotfound,filename);return NULL; } + vcd_read_toc(f); + ret2=vcd_get_track_end(f,vcd_track); + if(ret2<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_ErrTrackSelect " (get)\n");return NULL;} + ret=vcd_seek_to_track(f,vcd_track); + if(ret<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_ErrTrackSelect " (seek)\n");return NULL;} +// seek_to_byte+=ret; + mp_msg(MSGT_OPEN,MSGL_V,"VCD start byte position: 0x%X end: 0x%X\n",ret,ret2); +#ifdef VCD_CACHE + vcd_cache_init(vcd_cache_size); +#endif +#ifdef __FreeBSD__ + if (ioctl (f, CDRIOCSETBLOCKSIZE, &bsize) == -1) { + perror ( "Error in CDRIOCSETBLOCKSIZE"); + } +#endif + stream=new_stream(f,STREAMTYPE_VCD); + stream->start_pos=ret; + stream->end_pos=ret2; + return stream; +} + +//============ Open DVD title ============== +#ifdef USE_DVDREAD +if(dvd_title){ +// int ret,ret2; + dvd_priv_t *d; + int ttn,pgc_id,pgn; + dvd_reader_t *dvd; + dvd_file_t *title; + ifo_handle_t *vmg_file; + tt_srpt_t *tt_srpt; + ifo_handle_t *vts_file; + /** + * Open the disc. + */ + if(!filename) filename=DEFAULT_DVD_DEVICE; + dvd = DVDOpen(filename); + if( !dvd ) { + mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CantOpenDVD,filename); + return NULL; + } + + mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_DVDwait); + + /** + * Load the video manager to find out the information about the titles on + * this disc. + */ + vmg_file = ifoOpen( dvd, 0 ); + if( !vmg_file ) { + mp_msg(MSGT_OPEN,MSGL_ERR, "Can't open VMG info!\n"); + DVDClose( dvd ); + return NULL; + } + tt_srpt = vmg_file->tt_srpt; + /** + * Make sure our title number is valid. + */ + mp_msg(MSGT_OPEN,MSGL_INFO, MSGTR_DVDnumTitles, + tt_srpt->nr_of_srpts ); + if( dvd_title < 1 || dvd_title > tt_srpt->nr_of_srpts ) { + mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDinvalidTitle, dvd_title); + ifoClose( vmg_file ); + DVDClose( dvd ); + return NULL; + } + --dvd_title; // remap 1.. -> 0.. + /** + * Make sure the chapter number is valid for this title. + */ + mp_msg(MSGT_OPEN,MSGL_INFO, MSGTR_DVDnumChapters, + tt_srpt->title[dvd_title].nr_of_ptts ); + if( dvd_chapter<1 || dvd_chapter>tt_srpt->title[dvd_title].nr_of_ptts ) { + mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDinvalidChapter, dvd_chapter); + ifoClose( vmg_file ); + DVDClose( dvd ); + return NULL; + } + --dvd_chapter; // remap 1.. -> 0.. + /** + * Make sure the angle number is valid for this title. + */ + mp_msg(MSGT_OPEN,MSGL_INFO, MSGTR_DVDnumAngles, + tt_srpt->title[dvd_title].nr_of_angles ); + if( dvd_angle<1 || dvd_angle>tt_srpt->title[dvd_title].nr_of_angles ) { + mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDinvalidAngle, dvd_angle); + ifoClose( vmg_file ); + DVDClose( dvd ); + return NULL; + } + --dvd_angle; // remap 1.. -> 0.. + /** + * Load the VTS information for the title set our title is in. + */ + vts_file = ifoOpen( dvd, tt_srpt->title[dvd_title].title_set_nr ); + if( !vts_file ) { + mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDnoIFO, + tt_srpt->title[dvd_title].title_set_nr ); + ifoClose( vmg_file ); + DVDClose( dvd ); + return NULL; + } + /** + * We've got enough info, time to open the title set data. + */ + title = DVDOpenFile( dvd, tt_srpt->title[dvd_title].title_set_nr, + DVD_READ_TITLE_VOBS ); + if( !title ) { + mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDnoVOBs, + tt_srpt->title[dvd_title].title_set_nr ); + ifoClose( vts_file ); + ifoClose( vmg_file ); + DVDClose( dvd ); + return NULL; + } + + mp_msg(MSGT_OPEN,MSGL_INFO, MSGTR_DVDopenOk); + // store data + d=malloc(sizeof(dvd_priv_t)); memset(d,0,sizeof(dvd_priv_t)); + d->dvd=dvd; + d->title=title; + d->vmg_file=vmg_file; + d->tt_srpt=tt_srpt; + d->vts_file=vts_file; + + /** + * Determine which program chain we want to watch. This is based on the + * chapter number. + */ + ttn = tt_srpt->title[ dvd_title ].vts_ttn; // local + pgc_id = vts_file->vts_ptt_srpt->title[ttn-1].ptt[dvd_chapter].pgcn; // local + pgn = vts_file->vts_ptt_srpt->title[ttn-1].ptt[dvd_chapter].pgn; // local + d->cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc; + d->cur_cell = d->cur_pgc->program_map[pgn-1] - 1; // start playback here + d->packs_left=-1; // for Navi stuff + d->angle_seek=0; + + if( d->cur_pgc->cell_playback[d->cur_cell].block_type + == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle; + d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector; + d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector; + mp_msg(MSGT_DVD,MSGL_V, "DVD start cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack); + + // ... (unimplemented) +// return NULL; + stream=new_stream(-1,STREAMTYPE_DVD); + stream->start_pos=(off_t)d->cur_pack*2048; + //stream->end_pos=0; + stream->priv=(void*)d; + return stream; +} +#endif + +//============ Open STDIN ============ + if(!strcmp(filename,"-")){ + // read from stdin + mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_ReadSTDIN); + f=0; // 0=stdin + stream=new_stream(f,STREAMTYPE_STREAM); + return stream; + } + +#ifdef STREAMING + url = url_new(filename); + if(url) { + (*file_format)=autodetectProtocol( url, &f ); + if( (*file_format)==DEMUXER_TYPE_UNKNOWN ) { + mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_UnableOpenURL, filename); + url_free(url); + return NULL; + } + f=streaming_start( &url, f, *file_format ); + if(f<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_UnableOpenURL, url->url); return NULL; } + mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_ConnToServer, url->hostname ); + stream=new_stream(f,STREAMTYPE_STREAM); + return NULL; + } +#endif + +//============ Open plain FILE ============ + f=open(filename,O_RDONLY); + if(f<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_FileNotFound,filename);return NULL; } + len=lseek(f,0,SEEK_END); lseek(f,0,SEEK_SET); + if (len == -1) + perror("Error: lseek failed to obtain video file size"); + else + if(verbose) +#ifdef _LARGEFILE_SOURCE + mp_msg(MSGT_OPEN,MSGL_V,"File size is %lld bytes\n", (long long)len); +#else + mp_msg(MSGT_OPEN,MSGL_V,"File size is %u bytes\n", (unsigned int)len); +#endif + stream=new_stream(f,STREAMTYPE_FILE); + stream->end_pos=len; + return stream; + +} + + +#ifdef USE_DVDREAD + +static int dvd_next_cell(dvd_priv_t *d){ + int next_cell=d->cur_cell; + + mp_msg(MSGT_DVD,MSGL_V, "dvd_next_cell: next1=0x%X \n",next_cell); + + if( d->cur_pgc->cell_playback[ next_cell ].block_type + == BLOCK_TYPE_ANGLE_BLOCK ) { + while(next_cell<d->cur_pgc->nr_of_cells){ + if( d->cur_pgc->cell_playback[next_cell].block_mode + == BLOCK_MODE_LAST_CELL ) break; + ++next_cell; + } + } + mp_msg(MSGT_DVD,MSGL_V, "dvd_next_cell: next2=0x%X \n",next_cell); + + ++next_cell; + if(next_cell>=d->cur_pgc->nr_of_cells) return -1; // EOF + if( d->cur_pgc->cell_playback[next_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK ){ + next_cell+=dvd_angle; + if(next_cell>=d->cur_pgc->nr_of_cells) return -1; // EOF + } + mp_msg(MSGT_DVD,MSGL_V, "dvd_next_cell: next3=0x%X \n",next_cell); + return next_cell; +} + +int dvd_read_sector(dvd_priv_t *d,unsigned char* data){ + int len; + + if(d->packs_left==0){ + /** + * If we're not at the end of this cell, we can determine the next + * VOBU to display using the VOBU_SRI information section of the + * DSI. Using this value correctly follows the current angle, + * avoiding the doubled scenes in The Matrix, and makes our life + * really happy. + * + * Otherwise, we set our next address past the end of this cell to + * force the code above to go to the next cell in the program. + */ + if( d->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL ) { + d->cur_pack= d->dsi_pack.dsi_gi.nv_pck_lbn + + ( d->dsi_pack.vobu_sri.next_vobu & 0x7fffffff ); + mp_msg(MSGT_DVD,MSGL_DBG2, "Navi new pos=0x%X \n",d->cur_pack); + } else { + // end of cell! find next cell! + mp_msg(MSGT_DVD,MSGL_V, "--- END OF CELL !!! ---\n"); + d->cur_pack=d->cell_last_pack+1; + } + } + +read_next: + + if(d->cur_pack>d->cell_last_pack){ + // end of cell! + int next=dvd_next_cell(d); + if(next>=0){ + d->cur_cell=next; + +// if( d->cur_pgc->cell_playback[d->cur_cell].block_type +// == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle; + d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector; + d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector; + mp_msg(MSGT_DVD,MSGL_V, "DVD next cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack); + + } else return -1; // EOF + } + + len = DVDReadBlocks( d->title, d->cur_pack, 1, data ); + if(!len) return -1; //error + + if(data[38]==0 && data[39]==0 && data[40]==1 && data[41]==0xBF && + data[1024]==0 && data[1025]==0 && data[1026]==1 && data[1027]==0xBF){ + // found a Navi packet!!! +#if LIBDVDREAD_VERSION >= DVDREAD_VERSION(0,9,0) + navRead_DSI( &d->dsi_pack, &(data[ DSI_START_BYTE ]) ); +#else + navRead_DSI( &d->dsi_pack, &(data[ DSI_START_BYTE ]), sizeof(dsi_t) ); +#endif + if(d->cur_pack != d->dsi_pack.dsi_gi.nv_pck_lbn ){ + mp_msg(MSGT_DVD,MSGL_V, "Invalid NAVI packet! lba=0x%X navi=0x%X \n", + d->cur_pack,d->dsi_pack.dsi_gi.nv_pck_lbn); + } else { + // process! + d->packs_left = d->dsi_pack.dsi_gi.vobu_ea; + mp_msg(MSGT_DVD,MSGL_DBG2, "Found NAVI packet! lba=0x%X len=%d \n",d->cur_pack,d->packs_left); + if(d->angle_seek){ + int skip=d->dsi_pack.sml_agli.data[dvd_angle].address; + if(skip) d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+skip; + d->angle_seek=0; + mp_msg(MSGT_DVD,MSGL_V, "Angle-seek synced! skip=%d new_lba=0x%X \n",skip,d->cur_pack); + } + } + ++d->cur_pack; + goto read_next; + } + + ++d->cur_pack; + if(d->packs_left>=0) --d->packs_left; + + if(d->angle_seek) goto read_next; // searching for Navi packet + + return d->cur_pack-1; +} + +void dvd_seek(dvd_priv_t *d,int pos){ + d->packs_left=-1; + d->cur_pack=pos; + +// check if we stay in current cell (speedup things, and avoid angle skip) +if(d->cur_pack>d->cell_last_pack || + d->cur_pack<d->cur_pgc->cell_playback[ d->cur_cell ].first_sector){ + + // ok, cell change, find the right cell! + d->cur_cell=0; + if( d->cur_pgc->cell_playback[d->cur_cell].block_type + == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle; + + while(1){ + int next; + d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector; + if(d->cur_pack<d->cur_pgc->cell_playback[ d->cur_cell ].first_sector){ + d->cur_pack=d->cur_pgc->cell_playback[ d->cur_cell ].first_sector; + break; + } + if(d->cur_pack<=d->cell_last_pack) break; // ok, we find it! :) + next=dvd_next_cell(d); + if(next<0){ +// d->cur_pack=d->cell_last_pack+1; + break; // we're after the last cell + } + d->cur_cell=next; + } + +} + +mp_msg(MSGT_DVD,MSGL_V, "DVD Seek! lba=0x%X cell=%d packs: 0x%X-0x%X \n", + d->cur_pack,d->cur_cell,d->cur_pgc->cell_playback[ d->cur_cell ].first_sector,d->cell_last_pack); + +// if we're in interleaved multi-angle cell, find the right angle chain! +// (read Navi block, and use the seamless angle jump table) +d->angle_seek=1; + +} + +#endif diff --git a/libmpdemux/parse_es.c b/libmpdemux/parse_es.c new file mode 100644 index 0000000000..310b1c976d --- /dev/null +++ b/libmpdemux/parse_es.c @@ -0,0 +1,117 @@ +//=================== MPEG-ES VIDEO PARSER ========================= + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +extern int verbose; // defined in mplayer.c + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#include "stream.h" +#include "demuxer.h" + +#include "parse_es.h" + +//static unsigned char videobuffer[MAX_VIDEO_PACKET_SIZE]; +unsigned char* videobuffer=NULL; +int videobuf_len=0; +unsigned char videobuf_code[4]; +int videobuf_code_len=0; + +// sync video stream, and returns next packet code +int sync_video_packet(demux_stream_t *ds){ + int skipped=0; + // we need enough bytes in the buffer: + while(videobuf_code_len<4){ +#if 0 + int c; + c=demux_getc(ds);if(c<0){ return 0;} // EOF + videobuf_code[videobuf_code_len++]=c; +#else + videobuf_code[videobuf_code_len++]=demux_getc(ds); +#endif + } + // sync packet: + while(1){ + int c; + if(videobuf_code[0]==0 && + videobuf_code[1]==0 && + videobuf_code[2]==1) break; // synced + // shift buffer, drop first byte + ++skipped; + videobuf_code[0]=videobuf_code[1]; + videobuf_code[1]=videobuf_code[2]; + videobuf_code[2]=videobuf_code[3]; + c=demux_getc(ds);if(c<0){ return 0;} // EOF + videobuf_code[3]=c; + } + if(skipped) mp_dbg(MSGT_PARSEES,MSGL_DBG2,"videobuf: %d bytes skipped (next: 0x1%02X)\n",skipped,videobuf_code[3]); + return 0x100|videobuf_code[3]; +} + +// return: packet length +int read_video_packet(demux_stream_t *ds){ +int packet_start; + + // SYNC STREAM +// if(!sync_video_packet(ds)) return 0; // cannot sync (EOF) + + // COPY STARTCODE: + packet_start=videobuf_len; + videobuffer[videobuf_len+0]=videobuf_code[0]; + videobuffer[videobuf_len+1]=videobuf_code[1]; + videobuffer[videobuf_len+2]=videobuf_code[2]; + videobuffer[videobuf_len+3]=videobuf_code[3]; + videobuf_len+=4; + + // READ PACKET: + { unsigned int head=-1; + while(videobuf_len<VIDEOBUFFER_SIZE){ + int c=demux_getc(ds); + if(c<0) break; // EOF + videobuffer[videobuf_len++]=c; +#if 1 + head<<=8; + if(head==0x100) break; // synced + head|=c; +#else + if(videobuffer[videobuf_len-4]==0 && + videobuffer[videobuf_len-3]==0 && + videobuffer[videobuf_len-2]==1) break; // synced +#endif + } + } + + if(ds->eof){ + videobuf_code_len=0; // EOF, no next code + return videobuf_len-packet_start; + } + + videobuf_len-=4; + + mp_dbg(MSGT_PARSEES,MSGL_DBG2,"videobuf: packet 0x1%02X len=%d (total=%d)\n",videobuffer[packet_start+3],videobuf_len-packet_start,videobuf_len); + + // Save next packet code: + videobuf_code[0]=videobuffer[videobuf_len]; + videobuf_code[1]=videobuffer[videobuf_len+1]; + videobuf_code[2]=videobuffer[videobuf_len+2]; + videobuf_code[3]=videobuffer[videobuf_len+3]; + videobuf_code_len=4; + + return videobuf_len-packet_start; +} + +// return: next packet code +int skip_video_packet(demux_stream_t *ds){ + + // SYNC STREAM +// if(!sync_video_packet(ds)) return 0; // cannot sync (EOF) + + videobuf_code_len=0; // force resync + + // SYNC AGAIN: + return sync_video_packet(ds); +} diff --git a/libmpdemux/parse_es.h b/libmpdemux/parse_es.h new file mode 100644 index 0000000000..45936b396a --- /dev/null +++ b/libmpdemux/parse_es.h @@ -0,0 +1,18 @@ + +#define MAX_VIDEO_PACKET_SIZE (224*1024+4) +#define VIDEOBUFFER_SIZE 0x100000 + +extern unsigned char* videobuffer; +extern int videobuf_len; +extern unsigned char videobuf_code[4]; +extern int videobuf_code_len; + +// sync video stream, and returns next packet code +int sync_video_packet(demux_stream_t *ds); + +// return: packet length +int read_video_packet(demux_stream_t *ds); + +// return: next packet code +int skip_video_packet(demux_stream_t *ds); + diff --git a/libmpdemux/stheader.h b/libmpdemux/stheader.h new file mode 100644 index 0000000000..0e83c53d84 --- /dev/null +++ b/libmpdemux/stheader.h @@ -0,0 +1,94 @@ +// Stream headers: + +/* +typedef struct { + int driver; + // codec descriptor from codec.conf +} codecinfo_t; +*/ + +#ifdef HAVE_OGGVORBIS +#include <math.h> +#include <vorbis/codec.h> +typedef struct { + ogg_sync_state oy; /* sync and verify incoming physical bitstream */ + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + + vorbis_info vi; /* struct that stores all the static vorbis bitstream + settings */ + vorbis_comment vc; /* struct that stores all the bitstream user comments */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ +} ov_struct_t; +#endif + +typedef struct { + demux_stream_t *ds; + unsigned int format; + struct codecs_st *codec; + int inited; + // output format: + float timer; // value of old a_frame + int samplerate; + int samplesize; + int channels; + int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec) + int i_bps; // == bitrate (compressed bytes/sec) + // in buffers: + char* a_in_buffer; + int a_in_buffer_len; + int a_in_buffer_size; + // out buffers: + char* a_buffer; + int a_buffer_len; + int a_buffer_size; + int sample_format; + // win32 codec stuff: + AVIStreamHeader audio; + WAVEFORMATEX *wf; +// char wf_ext[64]; // in format + WAVEFORMATEX o_wf; // out format + HACMSTREAM srcstream; // handle + int audio_in_minsize; + int audio_out_minsize; + // other codecs: +// ac3_frame_t *ac3_frame; + void* ac3_frame; + int pcm_bswap; +#ifdef HAVE_OGGVORBIS + ov_struct_t *ov; // should be assigned on init +#endif +} sh_audio_t; + +typedef struct { + demux_stream_t *ds; + unsigned int format; + struct codecs_st *codec; + int inited; + // output format: + float timer; // value of old v_frame + float fps; + float frametime; // 1/fps + int i_bps; // == bitrate (compressed bytes/sec) + int disp_w,disp_h; // display size (filled by fileformat parser) +// int coded_w,coded_h; // coded size (filled by video codec) + float aspect; + unsigned int outfmtidx; +// unsigned int bitrate; + // buffers: + float num_frames; // number of frames played + int num_frames_decoded; // number of frames decoded + char *our_out_buffer; + // win32 codec stuff: + AVIStreamHeader video; + BITMAPINFOHEADER *bih; // in format + BITMAPINFOHEADER o_bih; // out format + HIC hic; // handle +} sh_video_t; + +sh_audio_t* new_sh_audio(demuxer_t *demuxer,int id); +sh_video_t* new_sh_video(demuxer_t *demuxer,int id); + diff --git a/libmpdemux/stream.c b/libmpdemux/stream.c new file mode 100644 index 0000000000..de138cef02 --- /dev/null +++ b/libmpdemux/stream.c @@ -0,0 +1,188 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include "config.h" +#include "mp_msg.h" +#include "help_mp.h" + +#include "stream.h" + +extern int verbose; // defined in mplayer.c + +#ifdef __FreeBSD__ +#include "vcd_read_fbsd.h" +#else +#include "vcd_read.h" +#endif + +#ifdef USE_DVDREAD +int dvd_read_sector(void* d,void* p2); +void dvd_seek(void* d,off_t pos); +#endif + +//=================== STREAMER ========================= + +int stream_fill_buffer(stream_t *s){ + int len; + if(s->eof){ s->buf_pos=s->buf_len=0; return 0; } + switch(s->type){ + case STREAMTYPE_FILE: + case STREAMTYPE_STREAM: + len=read(s->fd,s->buffer,STREAM_BUFFER_SIZE);break; + case STREAMTYPE_VCD: +#ifdef VCD_CACHE + len=vcd_cache_read(s->fd,s->buffer);break; +#else + len=vcd_read(s->fd,s->buffer);break; +#endif +#ifdef USE_DVDREAD + case STREAMTYPE_DVD: { + off_t pos=dvd_read_sector(s->priv,s->buffer); + if(pos>=0){ + len=2048; // full sector + s->pos=2048*pos-len; + } else len=-1; // error + break; + } +#endif + default: len=0; + } + if(len<=0){ s->eof=1; s->buf_pos=s->buf_len=0; return 0; } + s->buf_pos=0; + s->buf_len=len; + s->pos+=len; +// printf("[%d]",len);fflush(stdout); + return len; +} + +int stream_seek_long(stream_t *s,off_t pos){ +off_t newpos; + +// if(verbose>=3) printf("seek_long to 0x%X\n",(unsigned int)pos); + + s->buf_pos=s->buf_len=0; + + switch(s->type){ + case STREAMTYPE_FILE: + case STREAMTYPE_STREAM: +#ifdef _LARGEFILE_SOURCE + newpos=pos&(~((long long)STREAM_BUFFER_SIZE-1));break; +#else + newpos=pos&(~(STREAM_BUFFER_SIZE-1));break; +#endif + case STREAMTYPE_VCD: + newpos=(pos/VCD_SECTOR_DATA)*VCD_SECTOR_DATA;break; + case STREAMTYPE_DVD: + newpos=pos/2048; newpos*=2048; break; + } + +if(verbose>=3){ +#ifdef _LARGEFILE_SOURCE + printf("s->pos=%llX newpos=%llX new_bufpos=%llX buflen=%X \n", + (long long)s->pos,(long long)newpos,(long long)pos,s->buf_len); +#else + printf("s->pos=%X newpos=%X new_bufpos=%X buflen=%X \n", + (unsigned int)s->pos,newpos,pos,s->buf_len); +#endif +} + + pos-=newpos; + +if(newpos==0 || newpos!=s->pos){ + switch(s->type){ + case STREAMTYPE_FILE: + s->pos=newpos; // real seek + if(lseek(s->fd,s->pos,SEEK_SET)<0) s->eof=1; + break; + case STREAMTYPE_VCD: + s->pos=newpos; // real seek +#ifdef VCD_CACHE + vcd_cache_seek(s->pos/VCD_SECTOR_DATA); +#else + vcd_set_msf(s->pos/VCD_SECTOR_DATA); +#endif + break; +#ifdef USE_DVDREAD + case STREAMTYPE_DVD: + s->pos=newpos; // real seek + dvd_seek(s->priv,s->pos/2048); + break; +#endif + case STREAMTYPE_STREAM: + //s->pos=newpos; // real seek + if(newpos<s->pos){ + mp_msg(MSGT_STREAM,MSGL_INFO,"Cannot seek backward in linear streams!\n"); + return 1; + } + while(s->pos<newpos){ + if(stream_fill_buffer(s)<=0) break; // EOF + } + break; + } +// putchar('.');fflush(stdout); +//} else { +// putchar('%');fflush(stdout); +} + + stream_fill_buffer(s); + if(pos>=0 && pos<=s->buf_len){ + s->buf_pos=pos; // byte position in sector + return 1; + } + +// if(pos==s->buf_len) printf("XXX Seek to last byte of file -> EOF\n"); + +#ifdef _LARGEFILE_SOURCE + mp_msg(MSGT_STREAM,MSGL_V,"stream_seek: WARNING! Can't seek to 0x%llX !\n",(long long)(pos+newpos)); +#else + mp_msg(MSGT_STREAM,MSGL_V,"stream_seek: WARNING! Can't seek to 0x%X !\n",(pos+newpos)); +#endif + return 0; +} + + +void stream_reset(stream_t *s){ + if(s->eof){ + s->pos=0; //ftell(f); +// s->buf_pos=s->buf_len=0; + s->eof=0; + } + //stream_seek(s,0); +} + +stream_t* new_memory_stream(unsigned char* data,int len){ + stream_t *s=malloc(sizeof(stream_t)+len); + s->fd=-1; + s->type=STREAMTYPE_MEMORY; + s->buf_pos=0; s->buf_len=len; + s->start_pos=0; s->end_pos=len; + stream_reset(s); + s->pos=len; + memcpy(s->buffer,data,len); + return s; +} + +stream_t* new_stream(int fd,int type){ + stream_t *s=malloc(sizeof(stream_t)); + s->fd=fd; + s->type=type; + s->buf_pos=s->buf_len=0; + s->start_pos=s->end_pos=0; + s->priv=NULL; + stream_reset(s); + return s; +} + +void free_stream(stream_t *s){ + if(s->priv) free(s->priv); + free(s); +} + + diff --git a/libmpdemux/stream.h b/libmpdemux/stream.h new file mode 100644 index 0000000000..2859b68bc4 --- /dev/null +++ b/libmpdemux/stream.h @@ -0,0 +1,141 @@ +#ifndef __STREAM_H +#define __STREAM_H + +#define STREAM_BUFFER_SIZE 2048 + +#define STREAMTYPE_FILE 0 +#define STREAMTYPE_VCD 1 +#define STREAMTYPE_STREAM 2 // same as FILE but no seeking (for stdin) +#define STREAMTYPE_DVD 3 +#define STREAMTYPE_MEMORY 4 + +#define VCD_SECTOR_SIZE 2352 +#define VCD_SECTOR_OFFS 24 +#define VCD_SECTOR_DATA 2324 + +int vcd_seek_to_track(int fd,int track); +void vcd_read_toc(int fd); + +#ifdef VCD_CACHE +void vcd_cache_init(int s); +#endif + +typedef struct { + int fd; + off_t pos; + int eof; + int type; // 0=file 1=VCD + unsigned int buf_pos,buf_len; + off_t start_pos,end_pos; + void* priv; // used for DVD + unsigned char buffer[STREAM_BUFFER_SIZE>VCD_SECTOR_SIZE?STREAM_BUFFER_SIZE:VCD_SECTOR_SIZE]; +} stream_t; + +int stream_fill_buffer(stream_t *s); + +int stream_seek_long(stream_t *s,off_t pos); + +inline static int stream_read_char(stream_t *s){ + return (s->buf_pos<s->buf_len)?s->buffer[s->buf_pos++]: + (stream_fill_buffer(s)?s->buffer[s->buf_pos++]:-256); +// if(s->buf_pos<s->buf_len) return s->buffer[s->buf_pos++]; +// stream_fill_buffer(s); +// if(s->buf_pos<s->buf_len) return s->buffer[s->buf_pos++]; +// return 0; // EOF +} + +inline static unsigned int stream_read_word(stream_t *s){ + int x,y; + x=stream_read_char(s); + y=stream_read_char(s); + return (x<<8)|y; +} + +inline static unsigned int stream_read_dword(stream_t *s){ + unsigned int y; + y=stream_read_char(s); + y=(y<<8)|stream_read_char(s); + y=(y<<8)|stream_read_char(s); + y=(y<<8)|stream_read_char(s); + return y; +} + +inline static unsigned int stream_read_word_le(stream_t *s){ + int x,y; + x=stream_read_char(s); + y=stream_read_char(s); + return (y<<8)|x; +} + +inline static unsigned int stream_read_dword_le(stream_t *s){ + unsigned int y; + y=stream_read_char(s); + y|=stream_read_char(s)<<8; + y|=stream_read_char(s)<<16; + y|=stream_read_char(s)<<24; + return y; +} + +inline static void stream_read(stream_t *s,char* mem,int len){ + while(len>0){ + int x; + x=s->buf_len-s->buf_pos; + if(x==0){ + if(!stream_fill_buffer(s)) return; // EOF + x=s->buf_len-s->buf_pos; + } + if(s->buf_pos>s->buf_len) printf("stream_read: WARNING! s->buf_pos>s->buf_len\n"); + if(x>len) x=len; + memcpy(mem,&s->buffer[s->buf_pos],x); + s->buf_pos+=x; mem+=x; len-=x; + } +} + +inline static int stream_eof(stream_t *s){ + return s->eof; +} + +inline static off_t stream_tell(stream_t *s){ + return s->pos+s->buf_pos-s->buf_len; +} + +inline static int stream_seek(stream_t *s,off_t pos){ + +// if(verbose>=3) printf("seek to 0x%qX\n",(long long)pos); + + if(pos<s->pos){ + off_t x=pos-(s->pos-s->buf_len); + if(x>=0){ + s->buf_pos=x; +// putchar('*');fflush(stdout); + return 1; + } + } + + return stream_seek_long(s,pos); +} + +inline static int stream_skip(stream_t *s,int len){ + if(len<0 || (len>2*STREAM_BUFFER_SIZE && s->type!=STREAMTYPE_STREAM)){ + // negative or big skip! + return stream_seek(s,stream_tell(s)+len); + } + while(len>0){ + int x=s->buf_len-s->buf_pos; + if(x==0){ + if(!stream_fill_buffer(s)) return 0; // EOF + x=s->buf_len-s->buf_pos; + } + if(x>len) x=len; + //memcpy(mem,&s->buf[s->buf_pos],x); + s->buf_pos+=x; len-=x; + } + return 1; +} + +void stream_reset(stream_t *s); +stream_t* new_stream(int fd,int type); +void free_stream(stream_t *s); +stream_t* new_memory_stream(unsigned char* data,int len); + +#endif // __STREAM_H diff --git a/libmpdemux/test.c b/libmpdemux/test.c new file mode 100644 index 0000000000..ce59b19228 --- /dev/null +++ b/libmpdemux/test.c @@ -0,0 +1,66 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "mp_msg.h" + +#include "stream.h" +#include "demuxer.h" + +#include "wine/mmreg.h" +#include "wine/avifmt.h" +#include "wine/vfw.h" +#include "codec-cfg.h" +#include "stheader.h" + +//-------------------------- + +// audio stream skip/resync functions requires only for seeking. +// (they should be implemented in the audio codec layer) +void skip_audio_frame(sh_audio_t *sh_audio){ +} +void resync_audio_stream(sh_audio_t *sh_audio){ +} + +// some globals: +int verbose=1; + +// AVI demuxer parameters: +int index_mode=-1; // -1=untouched 0=don't use index 1=use (geneate) index +int force_ni=0; // force non-interleaved AVI parsing +int pts_from_bps=1; // PTS: 0=interleaved 1=BPS-based + +//--------------- + +extern stream_t* open_stream(char* filename,int vcd_track,int* file_format); + +int main(int argc,char* argv[]){ + +stream_t* stream=NULL; +demuxer_t* demuxer=NULL; +int file_format=DEMUXER_TYPE_UNKNOWN; + + mp_msg_init(verbose+MSGL_STATUS); + + if(argc>1) + stream=open_stream(argv[1],0,&file_format); + else +// stream=open_stream("/3d/divx/405divx_sm_v2[1].avi",0,&file_format); + stream=open_stream("/dev/cdrom",2,&file_format); // VCD track 2 + + if(!stream){ + printf("Cannot open file/device\n"); + exit(1); + } + + printf("success: format: %d data: 0x%X - 0x%X\n",file_format, (int)(stream->start_pos),(int)(stream->end_pos)); + + demuxer=demux_open(stream,file_format,-1,-1,-1); + if(!demuxer){ + printf("Cannot open demuxer\n"); + exit(1); + } + + +} diff --git a/libmpdemux/url.c b/libmpdemux/url.c new file mode 100644 index 0000000000..f4a506ec72 --- /dev/null +++ b/libmpdemux/url.c @@ -0,0 +1,117 @@ +/* + * URL Helper + * by Bertrand Baudet <bertrand_baudet@yahoo.com> + * (C) 2001, MPlayer team. + * + * TODO: + * Extract the username/password if present + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "url.h" + +URL_t* +url_new(char* url) { + int pos1, pos2; + URL_t* Curl; + char *ptr1, *ptr2; + + // Create the URL container + Curl = (URL_t*)malloc(sizeof(URL_t)); + if( Curl==NULL ) { + printf("Memory allocation failed!\n"); + return NULL; + } + // Initialisation of the URL container members + memset( Curl, 0, sizeof(URL_t) ); + + // Copy the url in the URL container + Curl->url = (char*)malloc(strlen(url)+1); + if( Curl->url==NULL ) { + printf("Memory allocation failed!\n"); + return NULL; + } + strcpy(Curl->url, url); + + // extract the protocol + ptr1 = strstr(url, "://"); + if( ptr1==NULL ) { + printf("Not an URL!\n"); + return NULL; + } + pos1 = ptr1-url; + Curl->protocol = (char*)malloc(pos1+1); + strncpy(Curl->protocol, url, pos1); + Curl->protocol[pos1] = '\0'; + + // look if the port is given + ptr2 = strstr(ptr1+3, ":"); + if( ptr2==NULL ) { + // No port is given + // Look if a path is given + ptr2 = strstr(ptr1+3, "/"); + if( ptr2==NULL ) { + // No path/filename + // So we have an URL like http://www.hostname.com + pos2 = strlen(url); + } else { + // We have an URL like http://www.hostname.com/file.txt + pos2 = ptr2-url; + } + } else { + // We have an URL beginning like http://www.hostname.com:1212 + // Get the port number + Curl->port = atoi(ptr2+1); + pos2 = ptr2-url; + } + // copy the hostname in the URL container + Curl->hostname = (char*)malloc(pos2-pos1-3+1); + if( Curl->hostname==NULL ) { + printf("Memory allocation failed!\n"); + return NULL; + } + strncpy(Curl->hostname, ptr1+3, pos2-pos1-3); + Curl->hostname[pos2-pos1-3] = '\0'; + + // Look if a path is given + ptr2 = strstr(ptr1+3, "/"); + if( ptr2!=NULL ) { + // A path/filename is given + // check if it's not a trailing '/' + if( strlen(ptr2)>1 ) { + // copy the path/filename in the URL container + Curl->file = (char*)malloc(strlen(ptr2)+1); + if( Curl->file==NULL ) { + printf("Memory allocation failed!\n"); + return NULL; + } + strcpy(Curl->file, ptr2); + } + } + // Check if a filenme was given or set, else set it with '/' + if( Curl->file==NULL ) { + Curl->file = (char*)malloc(2); + if( Curl->file==NULL ) { + printf("Memory allocation failed!\n"); + return NULL; + } + strcpy(Curl->file, "/"); + } + + return Curl; +} + +void +url_free(URL_t* url) { + if(!url) return; + if(url->url) free(url->url); + if(url->protocol) free(url->protocol); + if(url->hostname) free(url->hostname); + if(url->file) free(url->file); + if(url->username) free(url->username); + if(url->password) free(url->password); + free(url); +} diff --git a/libmpdemux/url.h b/libmpdemux/url.h new file mode 100644 index 0000000000..5855296c38 --- /dev/null +++ b/libmpdemux/url.h @@ -0,0 +1,23 @@ +/* + * URL Helper + * by Bertrand Baudet <bertrand_baudet@yahoo.com> + * (C) 2001, MPlayer team. + */ + +#ifndef __URL_H +#define __URL_H + +typedef struct { + char *url; + char *protocol; + char *hostname; + char *file; + unsigned int port; + char *username; + char *password; +} URL_t; + +URL_t* url_new(char* url); +void url_free(URL_t* url); + +#endif diff --git a/libmpdemux/vcd_read.h b/libmpdemux/vcd_read.h new file mode 100644 index 0000000000..9d6564be7a --- /dev/null +++ b/libmpdemux/vcd_read.h @@ -0,0 +1,280 @@ +//=================== VideoCD ========================== +#if defined(linux) || defined(sun) || defined(__bsdi__) + +#if defined(linux) +#include <linux/cdrom.h> +#elif defined(sun) +#include <sys/cdio.h> +static int sun_vcd_read(int, int*); +#elif defined(__bsdi__) +#include <dvd.h> +#endif + + +static struct cdrom_tocentry vcd_entry; + +static inline void vcd_set_msf(unsigned int sect){ + vcd_entry.cdte_addr.msf.frame=sect%75; + sect=sect/75; + vcd_entry.cdte_addr.msf.second=sect%60; + sect=sect/60; + vcd_entry.cdte_addr.msf.minute=sect; +} + +static inline unsigned int vcd_get_msf(){ + return vcd_entry.cdte_addr.msf.frame + + (vcd_entry.cdte_addr.msf.second+ + vcd_entry.cdte_addr.msf.minute*60)*75; +} + +int vcd_seek_to_track(int fd,int track){ + vcd_entry.cdte_format = CDROM_MSF; + vcd_entry.cdte_track = track; + if (ioctl(fd, CDROMREADTOCENTRY, &vcd_entry)) { + perror("ioctl dif1"); + return -1; + } + return VCD_SECTOR_DATA*vcd_get_msf(); +} + +int vcd_get_track_end(int fd,int track){ + struct cdrom_tochdr tochdr; + if (ioctl(fd,CDROMREADTOCHDR,&tochdr)==-1) + { perror("read CDROM toc header: "); return -1; } + vcd_entry.cdte_format = CDROM_MSF; + vcd_entry.cdte_track = track<tochdr.cdth_trk1?(track+1):CDROM_LEADOUT; + if (ioctl(fd, CDROMREADTOCENTRY, &vcd_entry)) { + perror("ioctl dif2"); + return -1; + } + return VCD_SECTOR_DATA*vcd_get_msf(); +} + +void vcd_read_toc(int fd){ + struct cdrom_tochdr tochdr; + int i; + if (ioctl(fd,CDROMREADTOCHDR,&tochdr)==-1) + { perror("read CDROM toc header: "); return; } + for (i=tochdr.cdth_trk0 ; i<=tochdr.cdth_trk1 ; i++){ + struct cdrom_tocentry tocentry; + + tocentry.cdte_track = i; + tocentry.cdte_format = CDROM_MSF; + + if (ioctl(fd,CDROMREADTOCENTRY,&tocentry)==-1) + { perror("read CDROM toc entry: "); return; } + + mp_msg(MSGT_OPEN,MSGL_INFO,"track %02d: adr=%d ctrl=%d format=%d %02d:%02d:%02d mode: %d\n", + (int)tocentry.cdte_track, + (int)tocentry.cdte_adr, + (int)tocentry.cdte_ctrl, + (int)tocentry.cdte_format, + (int)tocentry.cdte_addr.msf.minute, + (int)tocentry.cdte_addr.msf.second, + (int)tocentry.cdte_addr.msf.frame, + (int)tocentry.cdte_datamode + ); + } +} + + +static char vcd_buf[VCD_SECTOR_SIZE]; + +static int vcd_read(int fd,char *mem){ +#if defined(linux) || defined(__bsdi__) + memcpy(vcd_buf,&vcd_entry.cdte_addr.msf,sizeof(struct cdrom_msf)); + if(ioctl(fd,CDROMREADRAW,vcd_buf)==-1) return 0; // EOF? + memcpy(mem,&vcd_buf[VCD_SECTOR_OFFS],VCD_SECTOR_DATA); +#elif defined(sun) + { + int offset; + if (sun_vcd_read(fd, &offset) <= 0) return 0; + memcpy(mem,&vcd_buf[offset],VCD_SECTOR_DATA); + } +#endif + + vcd_entry.cdte_addr.msf.frame++; + if (vcd_entry.cdte_addr.msf.frame==75){ + vcd_entry.cdte_addr.msf.frame=0; + vcd_entry.cdte_addr.msf.second++; + if (vcd_entry.cdte_addr.msf.second==60){ + vcd_entry.cdte_addr.msf.second=0; + vcd_entry.cdte_addr.msf.minute++; + } + } + + return VCD_SECTOR_DATA; +} + + +#ifdef sun +#include <sys/scsi/generic/commands.h> +#include <sys/scsi/impl/uscsi.h> + +#define SUN_XAREAD 1 /*fails on atapi drives*/ +#define SUN_MODE2READ 2 /*fails on atapi drives*/ +#define SUN_SCSIREAD 3 +#define SUN_VCDREAD SUN_SCSIREAD + +static int sun_vcd_read(int fd, int *offset) +{ +#if SUN_VCDREAD == SUN_XAREAD + struct cdrom_cdxa cdxa; + cdxa.cdxa_addr = vcd_get_msf(); + cdxa.cdxa_length = 1; + cdxa.cdxa_data = vcd_buf; + cdxa.cdxa_format = CDROM_XA_SECTOR_DATA; + + if(ioctl(fd,CDROMCDXA,&cdxa)==-1) { + perror("CDROMCDXA"); + return 0; + } + *offset = 0; +#elif SUN_VCDREAD == SUN_MODE2READ + struct cdrom_read cdread; + cdread.cdread_lba = 4*vcd_get_msf(); + cdread.cdread_bufaddr = vcd_buf; + cdread.cdread_buflen = 2336; + + if(ioctl(fd,CDROMREADMODE2,&cdread)==-1) { + perror("CDROMREADMODE2"); + return 0; + } + *offset = 8; +#elif SUN_VCDREAD == SUN_SCSIREAD + struct uscsi_cmd sc; + union scsi_cdb cdb; + int lba = vcd_get_msf(); + int blocks = 1; + int sector_type; + int sync, header_code, user_data, edc_ecc, error_field; + int sub_channel; + + /* sector_type = 3; *//* mode2 */ + sector_type = 5; /* mode2/form2 */ + sync = 0; + header_code = 0; + user_data = 1; + edc_ecc = 0; + error_field = 0; + sub_channel = 0; + + memset(&cdb, 0, sizeof(cdb)); + memset(&sc, 0, sizeof(sc)); + cdb.scc_cmd = 0xBE; + cdb.cdb_opaque[1] = (sector_type) << 2; + cdb.cdb_opaque[2] = (lba >> 24) & 0xff; + cdb.cdb_opaque[3] = (lba >> 16) & 0xff; + cdb.cdb_opaque[4] = (lba >> 8) & 0xff; + cdb.cdb_opaque[5] = lba & 0xff; + cdb.cdb_opaque[6] = (blocks >> 16) & 0xff; + cdb.cdb_opaque[7] = (blocks >> 8) & 0xff; + cdb.cdb_opaque[8] = blocks & 0xff; + cdb.cdb_opaque[9] = (sync << 7) | + (header_code << 5) | + (user_data << 4) | + (edc_ecc << 3) | + (error_field << 1); + cdb.cdb_opaque[10] = sub_channel; + + sc.uscsi_cdb = (caddr_t)&cdb; + sc.uscsi_cdblen = 12; + sc.uscsi_bufaddr = vcd_buf; + sc.uscsi_buflen = 2336; + sc.uscsi_flags = USCSI_ISOLATE | USCSI_READ; + sc.uscsi_timeout = 20; + if (ioctl(fd, USCSICMD, &sc)) { + perror("USCSICMD: READ CD"); + return -1; + } + if (sc.uscsi_status) { + fprintf(stderr, "scsi command failed with status %d\n", sc.uscsi_status); + return -1; + } + *offset = 0; + return 1; +#else +#error SUN_VCDREAD +#endif +} +#endif /*sun*/ + + +//================== VCD CACHE ======================= +#ifdef VCD_CACHE + +static int vcd_cache_size=0; +static char *vcd_cache_data=NULL; +static int *vcd_cache_sectors=NULL; +static int vcd_cache_index=0; // index to first free (or oldest) cache sector +static int vcd_cache_current=-1; + +void vcd_cache_init(int s){ + vcd_cache_size=s; + vcd_cache_sectors=malloc(s*sizeof(int)); + vcd_cache_data=malloc(s*VCD_SECTOR_SIZE); + memset(vcd_cache_sectors,255,s*sizeof(int)); +} + +static inline void vcd_cache_seek(int sect){ + vcd_cache_current=sect; +} + +int vcd_cache_read(int fd,char* mem){ + int i; + char* vcd_buf; + + for(i=0;i<vcd_cache_size;i++) + if(vcd_cache_sectors[i]==vcd_cache_current){ + // found in the cache! :) + vcd_buf=&vcd_cache_data[i*VCD_SECTOR_SIZE]; + ++vcd_cache_current; + memcpy(mem,&vcd_buf[VCD_SECTOR_OFFS],VCD_SECTOR_DATA); + return VCD_SECTOR_DATA; + } + // NEW cache entry: + vcd_buf=&vcd_cache_data[vcd_cache_index*VCD_SECTOR_SIZE]; + vcd_cache_sectors[vcd_cache_index]=vcd_cache_current; + ++vcd_cache_index;if(vcd_cache_index>=vcd_cache_size)vcd_cache_index=0; + // read data! + vcd_set_msf(vcd_cache_current); +#if defined(linux) || defined(__bsdi__) + memcpy(vcd_buf,&vcd_entry.cdte_addr.msf,sizeof(struct cdrom_msf)); + if(ioctl(fd,CDROMREADRAW,vcd_buf)==-1) return 0; // EOF? + memcpy(mem,&vcd_buf[VCD_SECTOR_OFFS],VCD_SECTOR_DATA); +#elif defined(sun) + { + int offset; + if (sun_vcd_read(fd, &offset) <= 0) return 0; + memcpy(mem,&vcd_buf[offset],VCD_SECTOR_DATA); + } +#endif + ++vcd_cache_current; + return VCD_SECTOR_DATA; +} +#endif + +#else /* linux || sun */ + +int vcd_seek_to_track(int fd,int track) +{ + return -1; +} + +int vcd_get_track_end(int fd,int track) +{ + return -1; +} + +void vcd_read_toc(int fd) +{ +} + +static char vcd_buf[VCD_SECTOR_SIZE]; + +static int vcd_read(int fd,char *mem) +{ + return -1; +} + +#endif /* !linux && !sun */ diff --git a/libmpdemux/vcd_read_fbsd.h b/libmpdemux/vcd_read_fbsd.h new file mode 100644 index 0000000000..f21fb10b48 --- /dev/null +++ b/libmpdemux/vcd_read_fbsd.h @@ -0,0 +1,143 @@ +#include <sys/cdio.h> +#include <sys/cdrio.h> + +//=================== VideoCD ========================== +#define CDROM_LEADOUT 0xAA + +typedef struct { + uint8_t sync [12]; + uint8_t header [4]; + uint8_t subheader [8]; + uint8_t data [2324]; + uint8_t spare [4]; +} cdsector_t; + +static cdsector_t vcd_buf; +static struct ioc_read_toc_single_entry vcd_entry; + +static inline void vcd_set_msf(unsigned int sect){ + vcd_entry.entry.addr.msf.frame=sect%75; + sect=sect/75; + vcd_entry.entry.addr.msf.second=sect%60; + sect=sect/60; + vcd_entry.entry.addr.msf.minute=sect; +} + +static inline unsigned int vcd_get_msf(){ + return vcd_entry.entry.addr.msf.frame + + (vcd_entry.entry.addr.msf.second+ + vcd_entry.entry.addr.msf.minute*60)*75; +} + +int vcd_seek_to_track(int fd,int track){ + vcd_entry.address_format = CD_MSF_FORMAT; + vcd_entry.track = track; + if (ioctl(fd, CDIOREADTOCENTRY, &vcd_entry)) { + perror("ioctl dif1"); + return -1; + } + return VCD_SECTOR_DATA*vcd_get_msf(); +} + +int vcd_get_track_end(int fd,int track){ + struct ioc_toc_header tochdr; + if (ioctl(fd,CDIOREADTOCHEADER,&tochdr)==-1) + { perror("read CDROM toc header: "); return -1; } + vcd_entry.address_format = CD_MSF_FORMAT; + vcd_entry.track = track<tochdr.ending_track?(track+1):CDROM_LEADOUT; + if (ioctl(fd, CDIOREADTOCENTRY, &vcd_entry)) { + perror("ioctl dif2"); + return -1; + } + return VCD_SECTOR_DATA*vcd_get_msf(); +} + +void vcd_read_toc(int fd){ + struct ioc_toc_header tochdr; + int i; + if (ioctl(fd,CDIOREADTOCHEADER,&tochdr)==-1) + { perror("read CDROM toc header: "); return; } + for (i=tochdr.starting_track ; i<=tochdr.ending_track ; i++){ + struct ioc_read_toc_single_entry tocentry; + + tocentry.track = i; + tocentry.address_format = CD_MSF_FORMAT; + + if (ioctl(fd,CDIOREADTOCENTRY,&tocentry)==-1) + { perror("read CDROM toc entry: "); return; } + + printf("track %02d: adr=%d ctrl=%d format=%d %02d:%02d:%02d\n", + (int)tocentry.track, + (int)tocentry.entry.addr_type, + (int)tocentry.entry.control, + (int)tocentry.address_format, + (int)tocentry.entry.addr.msf.minute, + (int)tocentry.entry.addr.msf.second, + (int)tocentry.entry.addr.msf.frame + ); + } +} + +static int vcd_read(int fd,char *mem){ + + if (pread(fd,&vcd_buf,VCD_SECTOR_SIZE,vcd_get_msf()*VCD_SECTOR_SIZE) + != VCD_SECTOR_SIZE) return 0; // EOF? + + vcd_entry.entry.addr.msf.frame++; + if (vcd_entry.entry.addr.msf.frame==75){ + vcd_entry.entry.addr.msf.frame=0; + vcd_entry.entry.addr.msf.second++; + if (vcd_entry.entry.addr.msf.second==60){ + vcd_entry.entry.addr.msf.second=0; + vcd_entry.entry.addr.msf.minute++; + } + } + memcpy(mem,vcd_buf.data,VCD_SECTOR_DATA); + return VCD_SECTOR_DATA; +} + +//================== VCD CACHE ======================= +#ifdef VCD_CACHE + +static int vcd_cache_size=0; +static char *vcd_cache_data=NULL; +static int *vcd_cache_sectors=NULL; +static int vcd_cache_index=0; // index to first free (or oldest) cache sector +static int vcd_cache_current=-1; + +void vcd_cache_init(int s){ + vcd_cache_size=s; + vcd_cache_sectors=malloc(s*sizeof(int)); + vcd_cache_data=malloc(s*VCD_SECTOR_SIZE); + memset(vcd_cache_sectors,255,s*sizeof(int)); +} + +static inline void vcd_cache_seek(int sect){ + vcd_cache_current=sect; +} + +int vcd_cache_read(int fd,char* mem){ +int i; +char* vcd_buf; + for(i=0;i<vcd_cache_size;i++) + if(vcd_cache_sectors[i]==vcd_cache_current){ + // found in the cache! :) + vcd_buf=&vcd_cache_data[i*VCD_SECTOR_SIZE]; + ++vcd_cache_current; + memcpy(mem,&vcd_buf[VCD_SECTOR_OFFS],VCD_SECTOR_DATA); + return VCD_SECTOR_DATA; + } + // NEW cache entry: + vcd_buf=&vcd_cache_data[vcd_cache_index*VCD_SECTOR_SIZE]; + vcd_cache_sectors[vcd_cache_index]=vcd_cache_current; + ++vcd_cache_index;if(vcd_cache_index>=vcd_cache_size)vcd_cache_index=0; + // read data! + vcd_set_msf(vcd_cache_current); + memcpy(vcd_buf,&vcd_entry.entry.addr.msf,sizeof(struct cdrom_msf)); +/* if(ioctl(fd,CDROMREADRAW,vcd_buf)==-1) return 0; */ // EOF? + ++vcd_cache_current; + memcpy(mem,&vcd_buf[VCD_SECTOR_OFFS],VCD_SECTOR_DATA); + return VCD_SECTOR_DATA; +} + +#endif |