changeset 14402:53a70339c70c

Real multirate files support
author rtognimp
date Thu, 06 Jan 2005 16:29:46 +0000
parents ddf3ac9a7539
children 5dac1eeb8d6b
files libmpdemux/demux_real.c
diffstat 1 files changed, 232 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/libmpdemux/demux_real.c	Thu Jan 06 16:24:58 2005 +0000
+++ b/libmpdemux/demux_real.c	Thu Jan 06 16:29:46 2005 +0000
@@ -77,6 +77,21 @@
 //    int 	a_streams[MAX_STREAMS];
 //    int		last_v_stream;
 //    int 	v_streams[MAX_STREAMS];
+
+    /**
+     * Used to demux multirate files
+     */
+    int is_multirate; ///< != 0 for multirate files
+    int str_data_offset[MAX_STREAMS]; ///< Data chunk offset for every audio/video stream
+    int audio_curpos; ///< Current file position for audio demuxing
+    int video_curpos; ///< Current file position for video demuxing
+    int a_num_of_packets; ///< Number of audio packets
+    int v_num_of_packets; ///< Number of video packets
+    int a_idx_ptr; ///< Audio index position pointer
+    int v_idx_ptr; ///< Video index position pointer
+    int a_bitrate; ///< Audio bitrate
+    int v_bitrate; ///< Video bitrate
+    int stream_switch; ///< Flag used to switch audio/video demuxing
 } real_priv_t;
 
 /* originally from FFmpeg */
@@ -514,11 +529,33 @@
 
   while(1){
 
+    /* Handle audio/video demxing switch for multirate files (non-interleaved) */
+    if (priv->is_multirate && priv->stream_switch) {
+        if (priv->a_idx_ptr >= priv->index_table_size[demuxer->audio->id])
+            demuxer->audio->eof = 1;
+        if (priv->v_idx_ptr >= priv->index_table_size[demuxer->video->id])
+            demuxer->video->eof = 1;
+        if (demuxer->audio->eof && demuxer->video->eof)
+            return 0;
+        else if (!demuxer->audio->eof && demuxer->video->eof)
+            stream_seek(demuxer->stream, priv->audio_curpos); // Get audio
+        else if (demuxer->audio->eof && !demuxer->video->eof)
+            stream_seek(demuxer->stream, priv->video_curpos); // Get video
+        else if (priv->index_table[demuxer->audio->id][priv->a_idx_ptr].timestamp <
+            priv->index_table[demuxer->video->id][priv->v_idx_ptr].timestamp)
+            stream_seek(demuxer->stream, priv->audio_curpos); // Get audio
+        else
+            stream_seek(demuxer->stream, priv->video_curpos); // Get video
+        priv->stream_switch = 0;
+    }
+
     demuxer->filepos = stream_tell(demuxer->stream);
     version = stream_read_word(demuxer->stream); /* version */
     len = stream_read_word(demuxer->stream);
     if ((version==0x4441) && (len==0x5441)) { // new data chunk
 	mp_msg(MSGT_DEMUX,MSGL_INFO,"demux_real: New data chunk is coming!!!\n");
+    if (priv->is_multirate)
+        return 0; // EOF
 	stream_skip(demuxer->stream,14); 
 	demuxer->filepos = stream_tell(demuxer->stream);
         version = stream_read_word(demuxer->stream); /* version */
@@ -548,6 +585,13 @@
     /*  0x1 - reliable  */
     /* 	0x2 - keyframe	*/
 
+    if (version == 1) {
+        int tmp;
+        tmp = stream_read_char(demuxer->stream);
+        mp_msg(MSGT_DEMUX, MSGL_DBG2,"Version: %d, skipped byte is %d\n", version, tmp);
+        len--;
+    }
+
     if (flags & 2)
       add_index_item(demuxer, stream_id, timestamp, demuxer->filepos);
 
@@ -649,6 +693,14 @@
 		       timestamp > priv->index_table[demuxer->audio->id][priv->current_apacket].timestamp)
 			priv->current_apacket += 1;
 	
+	if(priv->is_multirate)
+		while (priv->a_idx_ptr + 1 < priv->index_table_size[demuxer->audio->id] &&
+		       timestamp > priv->index_table[demuxer->audio->id][priv->a_idx_ptr + 1].timestamp) {
+			priv->a_idx_ptr++;
+			priv->audio_curpos = stream_tell(demuxer->stream);
+			priv->stream_switch = 1;
+		}
+	
 	return 1;
     }
     
@@ -858,6 +910,14 @@
 		       timestamp > priv->index_table[demuxer->video->id][priv->current_vpacket + 1].timestamp)
 			priv->current_vpacket += 1;
 
+	if(priv->is_multirate)
+		while (priv->v_idx_ptr + 1 < priv->index_table_size[demuxer->video->id] &&
+		       timestamp > priv->index_table[demuxer->video->id][priv->v_idx_ptr + 1].timestamp) {
+			priv->v_idx_ptr++;
+			priv->video_curpos = stream_tell(demuxer->stream);
+			priv->stream_switch = 1;
+		}
+	
 	return 1;
     }
 
@@ -1025,6 +1085,8 @@
 		int codec_data_size;
 		int codec_pos;
 		int tmp;
+		int len;
+		char *descr, *mimet;
 
 		stream_id = stream_read_word(demuxer->stream);
 		mp_msg(MSGT_DEMUX,MSGL_V,"Found new stream (id: %d)\n", stream_id);
@@ -1037,40 +1099,45 @@
 		stream_skip(demuxer->stream, 4); /* preroll */
 		stream_skip(demuxer->stream, 4); /* duration */
 		
-		skip_str(1, demuxer);	/* stream description (name) */
-		skip_str(1, demuxer);	/* mimetype */
+//		skip_str(1, demuxer);	/* stream description (name) */
+		if ((len = stream_read_char(demuxer->stream)) > 0) {
+		    descr = malloc(len+1);
+	    	stream_read(demuxer->stream, descr, len);
+		    descr[len] = 0;
+		    printf("Stream description: %s\n", descr);
+		    free(descr);
+		}
+//		skip_str(1, demuxer);	/* mimetype */
+		if ((len = stream_read_char(demuxer->stream)) > 0) {
+		    mimet = malloc(len+1);
+	    	stream_read(demuxer->stream, mimet, len);
+		    mimet[len] = 0;
+		    printf("Stream mimetype: %s\n", mimet);
+		}
 		
 		/* Type specific header */
 		codec_data_size = stream_read_dword(demuxer->stream);
 		codec_pos = stream_tell(demuxer->stream);
 
-		if (!codec_data_size) {
-		  mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_real: no codec data in MDPR chunk at fpos=0x%X\n", codec_pos);
-		  break;
-		}
-
-		tmp = stream_read_dword(demuxer->stream);
-		
-		mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_real: type_spec: len=%d  fpos=0x%X  first_dword=0x%X (%.4s)  \n",
-		    (int)codec_data_size,(int)codec_pos,tmp,&tmp);
-
-#if 1
-		// skip unknown shit - FIXME: find a better/cleaner way!
-		{   int len=codec_data_size;
-		    while(--len>=8){
-			if(tmp==MKTAG(0xfd, 'a', 'r', '.')) break; // audio
-			if(tmp==MKTAG('O', 'D', 'I', 'V')) break;  // video
-			tmp=(tmp<<8)|stream_read_char(demuxer->stream);
-		    }
-		}
-#endif
-
 #ifdef MP_DEBUG
 #define stream_skip(st,siz) { int i; for(i=0;i<siz;i++) mp_msg(MSGT_DEMUX,MSGL_V," %02X",stream_read_char(st)); mp_msg(MSGT_DEMUX,MSGL_V,"\n");}
 #endif
 
-		if (tmp == MKTAG(0xfd, 'a', 'r', '.'))
+	if (!strncmp(mimet,"audio/",6)) {
+	  if (strstr(mimet,"x-pn-realaudio") || strstr(mimet,"x-pn-multirate-realaudio")) {
+		// skip unknown shit - FIXME: find a better/cleaner way!
+		len=codec_data_size;
+		tmp = stream_read_dword(demuxer->stream);
+//		mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_real: type_spec: len=%d  fpos=0x%X  first_dword=0x%X (%.4s)  \n",
+//		    (int)codec_data_size,(int)codec_pos,tmp,&tmp);
+		while(--len>=8){
+			if(tmp==MKTAG(0xfd, 'a', 'r', '.')) break; // audio
+			tmp=(tmp<<8)|stream_read_char(demuxer->stream);
+		}
+		if (tmp != MKTAG(0xfd, 'a', 'r', '.'))
 		{
+		    mp_msg(MSGT_DEMUX,MSGL_V,"Audio: can't find .ra in codec data\n");
+		} else {
 		    /* audio header */
 		    sh_audio_t *sh = new_sh_audio(demuxer, stream_id);
 		    char buf[128]; /* for codec name */
@@ -1313,6 +1380,14 @@
 		    if (verbose > 0)
 		    print_wave_header(sh->wf);
 
+		    /* Select audio stream with highest bitrate if multirate file*/
+		    if (priv->is_multirate && ((demuxer->audio->id == -1) ||
+		                               ((demuxer->audio->id >= 0) && priv->a_bitrate && (bitrate > priv->a_bitrate)))) {
+			    demuxer->audio->id = stream_id;
+			    priv->a_bitrate = bitrate;
+			    mp_msg(MSGT_DEMUX,MSGL_DBG2,"Multirate autoselected audio id %d with bitrate %d\n", stream_id, bitrate);
+		    }
+
 		    if(demuxer->audio->id==stream_id){
 			demuxer->audio->id=stream_id;
 			sh->ds=demuxer->audio;
@@ -1325,10 +1400,48 @@
 #undef stream_skip
 #endif
 		}
-		else
-//		case MKTAG('V', 'I', 'D', 'O'):
-		if(tmp == MKTAG('O', 'D', 'I', 'V'))
+#if 0
+	  } else if (strstr(mimet,"X-MP3")) {
+		    sh_audio_t *sh = new_sh_audio(demuxer, stream_id);
+
+		    /* Emulate WAVEFORMATEX struct: */
+		    sh->wf = malloc(sizeof(WAVEFORMATEX));
+		    memset(sh->wf, 0, sizeof(WAVEFORMATEX));
+		    sh->wf->nChannels = 0;//sh->channels;
+		    sh->wf->wBitsPerSample = 16;
+		    sh->wf->nSamplesPerSec = 0;//sh->samplerate;
+		    sh->wf->nAvgBytesPerSec = 0;//bitrate;
+		    sh->wf->nBlockAlign = 0;//frame_size;
+		    sh->wf->cbSize = 0;
+		    sh->wf->wFormatTag = sh->format = 0x55;
+		    
+		    if(demuxer->audio->id==stream_id){
+			    sh->ds=demuxer->audio;
+			    demuxer->audio->sh=sh;
+		    }
+		    
+		    ++a_streams;
+#endif
+	  } else if (strstr(mimet,"x-ralf-mpeg4")) {
+		 mp_msg(MSGT_DEMUX,MSGL_ERR,"Real lossless audio not supported yet\n");
+	  } else {
+		 mp_msg(MSGT_DEMUX,MSGL_V,"Unknown audio stream format\n");
+		}
+	} else if (!strncmp(mimet,"video/",6)) {
+	  if (strstr(mimet,"x-pn-realvideo") || strstr(mimet,"x-pn-multirate-realvideo")) {
+		tmp = stream_read_dword(demuxer->stream);
+//		mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_real: type_spec: len=%d  fpos=0x%X  first_dword=0x%X (%.4s)  \n",
+//		    (int)codec_data_size,(int)codec_pos,tmp,&tmp);
+		// skip unknown shit - FIXME: find a better/cleaner way!
+		len=codec_data_size;
+		while(--len>=8){
+			if(tmp==MKTAG('O', 'D', 'I', 'V')) break;  // video
+			tmp=(tmp<<8)|stream_read_char(demuxer->stream);
+		}
+		if(tmp != MKTAG('O', 'D', 'I', 'V'))
 		{
+		    mp_msg(MSGT_DEMUX,MSGL_V,"Video: can't find VIDO in codec data\n");
+		} else {
 		    /* video header */
 		    sh_video_t *sh = new_sh_video(demuxer, stream_id);
 
@@ -1411,6 +1524,14 @@
 			((unsigned short*)(sh->bih+1))[5]=4*(unsigned short)stream_read_char(demuxer->stream); //height
 		    } 
 		    
+		    /* Select video stream with highest bitrate if multirate file*/
+		    if (priv->is_multirate && ((demuxer->video->id == -1) ||
+		                               ((demuxer->video->id >= 0) && priv->v_bitrate && (bitrate > priv->v_bitrate)))) {
+			    demuxer->video->id = stream_id;
+			    priv->v_bitrate = bitrate;
+			    mp_msg(MSGT_DEMUX,MSGL_DBG2,"Multirate autoselected video id %d with bitrate %d\n", stream_id, bitrate);
+		    }
+
 		    if(demuxer->video->id==stream_id){
 			demuxer->video->id=stream_id;
 			sh->ds=demuxer->video;
@@ -1420,12 +1541,43 @@
 		    ++v_streams;
 
 		}
+	  } else {
+		 mp_msg(MSGT_DEMUX,MSGL_V,"Unknown video stream format\n");
+	  }
+	} else if (strstr(mimet,"logical-")) {
+		 if (strstr(mimet,"fileinfo")) {
+		     mp_msg(MSGT_DEMUX,MSGL_V,"Got a logical-fileinfo chunk\n");
+		 } else if (strstr(mimet,"-audio") || strstr(mimet,"-video")) {
+		    int i, stream_cnt;
+		    int stream_list[MAX_STREAMS];
+		    
+		    priv->is_multirate = 1;
+		    stream_skip(demuxer->stream, 4); // Length of codec data (repeated)
+		    stream_cnt = stream_read_dword(demuxer->stream); // Get number of audio or video streams
+		    if (stream_cnt >= MAX_STREAMS) {
+		        mp_msg(MSGT_DEMUX,MSGL_ERR,"Too many streams in %s. Big troubles ahead.\n", mimet);
+		        goto skip_this_chunk;
+		    }
+		    for (i = 0; i < stream_cnt; i++)
+		        stream_list[i] = stream_read_word(demuxer->stream);
+		    for (i = 0; i < stream_cnt; i++)
+		        if (stream_list[i] >= MAX_STREAMS) {
+		            mp_msg(MSGT_DEMUX,MSGL_ERR,"Stream id out of range: %d. Ignored.\n", stream_list[i]);
+		            stream_skip(demuxer->stream, 4); // Skip DATA offset for broken stream
+		        } else {
+		            priv->str_data_offset[stream_list[i]] = stream_read_dword(demuxer->stream);
+		            mp_msg(MSGT_DEMUX,MSGL_V,"Stream %d with DATA offset 0x%08x\n", stream_list[i], priv->str_data_offset[stream_list[i]]);
+		        }
+		    // Skip the rest of this chunk
+		 } else 
+		     mp_msg(MSGT_DEMUX,MSGL_V,"Unknown logical stream\n");
+		}
 		else {
 		    mp_msg(MSGT_DEMUX, MSGL_ERR, "Not audio/video stream or unsupported!\n");
 		}
 //		break;
 //	    default:
-//skip_this_chunk:
+skip_this_chunk:
 		/* skip codec info */
 		tmp = stream_tell(demuxer->stream) - codec_pos;
 		mp_msg(MSGT_DEMUX,MSGL_V,"### skipping %d bytes of codec info\n", codec_data_size - tmp);
@@ -1438,6 +1590,8 @@
 #else
 		stream_skip(demuxer->stream, codec_data_size - tmp);
 #endif
+		if (mimet)
+		    free (mimet);
 		break;
 //	    }
 	    }
@@ -1452,6 +1606,40 @@
     }
 
 header_end:
+    if(priv->is_multirate) {
+        mp_msg(MSGT_DEMUX,MSGL_V,"Selected video id %d audio id %d\n", demuxer->video->id, demuxer->audio->id);
+        /* Perform some sanity checks to avoid checking streams id all over the code*/
+        if (demuxer->audio->id >= MAX_STREAMS) {
+            mp_msg(MSGT_DEMUX,MSGL_ERR,"Invalid audio stream %d. No sound will be played.\n", demuxer->audio->id);
+            demuxer->audio->id = -2;
+        } else if ((demuxer->audio->id >= 0) && (priv->str_data_offset[demuxer->audio->id] == 0)) {
+            mp_msg(MSGT_DEMUX,MSGL_ERR,"Audio stream %d not found. No sound will be played.\n", demuxer->audio->id);
+            demuxer->audio->id = -2;
+        }
+        if (demuxer->video->id >= MAX_STREAMS) {
+            mp_msg(MSGT_DEMUX,MSGL_ERR,"Invalid video stream %d. No video will be played.\n", demuxer->video->id);
+            demuxer->video->id = -2;
+        } else if ((demuxer->video->id >= 0) && (priv->str_data_offset[demuxer->video->id] == 0)) {
+            mp_msg(MSGT_DEMUX,MSGL_ERR,"Video stream %d not found. No video will be played.\n", demuxer->video->id);
+            demuxer->video->id = -2;
+        }
+    }
+
+    if(priv->is_multirate && ((demuxer->video->id >= 0) || (demuxer->audio->id  >=0))) {
+        /* If audio or video only, seek to right place and behave like standard file */
+        if (demuxer->video->id < 0) {
+            // Stream is audio only, or -novideo
+            stream_seek(demuxer->stream, priv->data_chunk_offset = priv->str_data_offset[demuxer->audio->id]+10);
+            priv->is_multirate = 0;
+        }
+        if (demuxer->audio->id < 0) {
+            // Stream is video only, or -nosound
+            stream_seek(demuxer->stream, priv->data_chunk_offset = priv->str_data_offset[demuxer->video->id]+10);
+            priv->is_multirate = 0;
+        }
+    }
+
+  if(!priv->is_multirate) {
 //    printf("i=%d num_of_headers=%d   \n",i,num_of_headers);
     priv->num_of_packets = stream_read_dword(demuxer->stream);
 //    stream_skip(demuxer->stream, 4); /* number of packets */
@@ -1461,6 +1649,19 @@
 
     if (priv->num_of_packets == 0)
 	priv->num_of_packets = -10;
+  } else {
+        priv->audio_curpos = priv->str_data_offset[demuxer->audio->id] + 18;
+        stream_seek(demuxer->stream, priv->str_data_offset[demuxer->audio->id]+10);
+        priv->a_num_of_packets=priv->a_num_of_packets = stream_read_dword(demuxer->stream);
+        priv->video_curpos = priv->str_data_offset[demuxer->video->id] + 18;
+        stream_seek(demuxer->stream, priv->str_data_offset[demuxer->video->id]+10);
+        priv->v_num_of_packets = stream_read_dword(demuxer->stream);
+        priv->stream_switch = 1;
+        /* Index required for multirate playback, force building if it's not there */
+        /* but respect user request to force index regeneration */
+        if (index_mode == -1)
+            index_mode = 1;
+    }
 
 
     priv->audio_need_keyframe = 0;
@@ -1492,6 +1693,9 @@
     	    break;
     }
 
+    if(priv->is_multirate)
+        demuxer->seekable = 0; // No seeking yet for multirate streams
+
     // detect streams:
     if(demuxer->video->id==-1 && v_streams>0){
 	// find the valid video stream: