changeset 30088:4977e04f3a18

Add support for parsing audio streams (though should be easy to extend to video) via libavcodec. Parsing can be done at the demuxer stage (currently disabled) or at the decoder (ad_ffmpeg, enabled). Should allow using the libavcodec AAC, DTS, ... decoders independent of container format.
author reimar
date Sun, 27 Dec 2009 15:28:01 +0000
parents 081f53690522
children ae0de5bf29e8
files libmpcodecs/ad_ffmpeg.c libmpdemux/aviheader.c libmpdemux/demux_mpg.c libmpdemux/demux_ts.c libmpdemux/demuxer.c libmpdemux/demuxer.h libmpdemux/stheader.h
diffstat 7 files changed, 156 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/libmpcodecs/ad_ffmpeg.c	Sun Dec 27 14:42:37 2009 +0000
+++ b/libmpcodecs/ad_ffmpeg.c	Sun Dec 27 15:28:01 2009 +0000
@@ -167,7 +167,17 @@
 	int len2=maxlen;
 	double pts;
 	int x=ds_get_packet_pts(sh_audio->ds,&start, &pts);
-	if(x<=0) break; // error
+	if(x<=0) {
+	    start = NULL;
+	    x = 0;
+	    ds_parse(sh_audio->ds, &start, &x, MP_NOPTS_VALUE, 0);
+	    if (x <= 0)
+	        break; // error
+	} else {
+	    int in_size = x;
+	    int consumed = ds_parse(sh_audio->ds, &start, &x, pts, 0);
+	    sh_audio->ds->buffer_pos -= in_size - consumed;
+	}
 	av_init_packet(&pkt);
 	pkt.data = start;
 	pkt.size = x;
@@ -178,7 +188,8 @@
 	y=avcodec_decode_audio3(sh_audio->context,(int16_t*)buf,&len2,&pkt);
 //printf("return:%d samples_out:%d bitstream_in:%d sample_sum:%d\n", y, len2, x, len); fflush(stdout);
 	if(y<0){ mp_msg(MSGT_DECAUDIO,MSGL_V,"lavc_audio: error\n");break; }
-	if(y<x) sh_audio->ds->buffer_pos+=y-x;  // put back data (HACK!)
+	if(!sh_audio->needs_parsing && y<x)
+	    sh_audio->ds->buffer_pos+=y-x;  // put back data (HACK!)
 	if(len2>0){
 	  if (((AVCodecContext *)sh_audio->context)->channels >= 5) {
             int samplesize = av_get_bits_per_sample_format(((AVCodecContext *)
--- a/libmpdemux/aviheader.c	Sun Dec 27 14:42:37 2009 +0000
+++ b/libmpdemux/aviheader.c	Sun Dec 27 15:28:01 2009 +0000
@@ -219,6 +219,7 @@
         mp_msg(MSGT_DEMUX, MSGL_INFO, MSGTR_AudioID, "aviheader", stream_id);
         memcpy(&sh_audio->audio,&h,sizeof(h));
         sh_audio->stream_delay = (float)sh_audio->audio.dwStart * sh_audio->audio.dwScale/sh_audio->audio.dwRate;
+        sh_audio->needs_parsing = 1;
       }
       last_fccType=h.fccType;
       last_fccHandler=h.fccHandler;
--- a/libmpdemux/demux_mpg.c	Sun Dec 27 14:42:37 2009 +0000
+++ b/libmpdemux/demux_mpg.c	Sun Dec 27 15:28:01 2009 +0000
@@ -270,6 +270,7 @@
     sh_audio_t* sh_a;
     new_sh_audio(demux,aid);
     sh_a = (sh_audio_t*)demux->a_streams[aid];
+    sh_a->needs_parsing = 1;
     switch(aid & 0xE0){  // 1110 0000 b  (high 3 bit: type  low 5: id)
       case 0x00: sh_a->format=0x50;break; // mpeg
       case 0xA0: sh_a->format=0x10001;break;  // dvd pcm
--- a/libmpdemux/demux_ts.c	Sun Dec 27 14:42:37 2009 +0000
+++ b/libmpdemux/demux_ts.c	Sun Dec 27 15:28:01 2009 +0000
@@ -305,6 +305,7 @@
 		if(sh)
 		{
 			const char *lang = pid_lang_from_pmt(priv, es->pid);
+			sh->needs_parsing = 1;
 			sh->format = IS_AUDIO(es->type) ? es->type : es->subtype;
 			sh->ds = demuxer->audio;
 
--- a/libmpdemux/demuxer.c	Sun Dec 27 14:42:37 2009 +0000
+++ b/libmpdemux/demuxer.c	Sun Dec 27 15:28:01 2009 +0000
@@ -53,6 +53,13 @@
 #endif
 #endif
 
+// This is quite experimental, in particular it will mess up the pts values
+// in the queue - on the other hand it might fix some issues like generating
+// broken files with mencoder and stream copy.
+// Better leave it disabled for now, if we find no use for it this code should
+// just be removed again.
+#define PARSE_ON_ADD 0
+
 void resync_video_stream(sh_video_t *sh_video);
 void resync_audio_stream(sh_audio_t *sh_audio);
 
@@ -278,6 +285,10 @@
         ass_free_track(sh->ass_track);
 #endif
     free(sh->lang);
+#ifdef CONFIG_LIBAVCODEC
+    av_parser_close(sh->parser);
+    av_freep(&sh->avctx);
+#endif
     free(sh);
 }
 
@@ -315,6 +326,10 @@
     free(sh->wf);
     free(sh->codecdata);
     free(sh->lang);
+#ifdef CONFIG_LIBAVCODEC
+    av_parser_close(sh->parser);
+    av_freep(&sh->avctx);
+#endif
     free(sh);
 }
 
@@ -343,6 +358,10 @@
 {
     mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_video at %p\n", sh);
     free(sh->bih);
+#ifdef CONFIG_LIBAVCODEC
+    av_parser_close(sh->parser);
+    av_freep(&sh->avctx);
+#endif
     free(sh);
 }
 
@@ -396,7 +415,7 @@
 }
 
 
-void ds_add_packet(demux_stream_t *ds, demux_packet_t *dp)
+static void ds_add_packet_internal(demux_stream_t *ds, demux_packet_t *dp)
 {
     // append packet to DS stream:
     ++ds->packs;
@@ -416,6 +435,109 @@
            ds->demuxer->video->packs);
 }
 
+#ifdef CONFIG_LIBAVCODEC
+static void allocate_parser(AVCodecContext **avctx, AVCodecParserContext **parser, unsigned format)
+{
+    enum CodecID codec_id = CODEC_ID_NONE;
+    extern int avcodec_initialized;
+    if (!avcodec_initialized) {
+        avcodec_init();
+        avcodec_register_all();
+        avcodec_initialized = 1;
+    }
+    switch (format) {
+    case 0x2000:
+    case 0x332D6361:
+    case 0x332D4341:
+    case MKTAG('d', 'n', 'e', 't'):
+    case MKTAG('s', 'a', 'c', '3'):
+        codec_id = CODEC_ID_AC3;
+        break;
+    case MKTAG('E', 'A', 'C', '3'):
+        codec_id = CODEC_ID_EAC3;
+        break;
+    case 0x2001:
+    case 0x86:
+        codec_id = CODEC_ID_DTS;
+        break;
+    case 0x55:
+    case 0x5500736d:
+    case MKTAG('.', 'm', 'p', '3'):
+    case MKTAG('M', 'P', 'E', ' '):
+    case MKTAG('L', 'A', 'M', 'E'):
+        codec_id = CODEC_ID_MP3;
+        break;
+    case 0x50:
+    case MKTAG('.', 'm', 'p', '2'):
+    case MKTAG('.', 'm', 'p', '1'):
+        codec_id = CODEC_ID_MP2;
+        break;
+    }
+    if (codec_id != CODEC_ID_NONE) {
+        *avctx = avcodec_alloc_context();
+        if (!*avctx)
+            return;
+        *parser = av_parser_init(codec_id);
+        if (!*parser)
+            av_freep(avctx);
+    }
+}
+
+static void get_parser(sh_common_t *sh, AVCodecContext **avctx, AVCodecParserContext **parser)
+{
+    *avctx  = NULL;
+    *parser = NULL;
+
+    if (!sh || !sh->needs_parsing)
+        return;
+
+    *avctx  = sh->avctx;
+    *parser = sh->parser;
+    if (*parser)
+        return;
+
+    allocate_parser(avctx, parser, sh->format);
+    sh->avctx  = *avctx;
+    sh->parser = *parser;
+}
+
+int ds_parse(demux_stream_t *ds, uint8_t **buffer, int *len, double pts, off_t pos)
+{
+    AVCodecContext *avctx;
+    AVCodecParserContext *parser;
+    get_parser(ds->sh, &avctx, &parser);
+    if (!parser)
+        return *len;
+    return av_parser_parse2(parser, avctx, buffer, len, *buffer, *len, pts, pts, pos);
+}
+#endif
+
+void ds_add_packet(demux_stream_t *ds, demux_packet_t *dp)
+{
+#if PARSE_ON_ADD && defined(CONFIG_LIBAVCODEC)
+    int len = dp->len;
+    int pos = 0;
+    while (len > 0) {
+        uint8_t *parsed_start = dp->buffer + pos;
+        int parsed_len = len;
+        int consumed = ds_parse(ds->sh, &parsed_start, &parsed_len, dp->pts, dp->pos);
+        pos += consumed;
+        len -= consumed;
+        if (parsed_start == dp->buffer && parsed_len == dp->len) {
+            ds_add_packet_internal(ds, dp);
+        } else if (parsed_len) {
+            demux_packet_t *dp2 = new_demux_packet(parsed_len);
+            dp2->pos = dp->pos;
+            dp2->pts = dp->pts; // should be parser->pts but that works badly
+            memcpy(dp2->buffer, parsed_start, parsed_len);
+            ds_add_packet_internal(ds, dp2);
+        }
+    }
+#else
+    ds_add_packet_internal(ds, dp);
+#endif
+}
+
 void ds_read_packet(demux_stream_t *ds, stream_t *stream, int len,
                     double pts, off_t pos, int flags)
 {
@@ -514,6 +636,18 @@
             break;
         }
         if (!demux_fill_buffer(demux, ds)) {
+#if PARSE_ON_ADD && defined(CONFIG_LIBAVCODEC)
+            uint8_t *parsed_start = NULL;
+            int parsed_len = 0;
+            ds_parse(ds->sh, &parsed_start, &parsed_len, MP_NOPTS_VALUE, 0);
+            if (parsed_len) {
+                demux_packet_t *dp2 = new_demux_packet(parsed_len);
+                dp2->pts = MP_NOPTS_VALUE;
+                memcpy(dp2->buffer, parsed_start, parsed_len);
+                ds_add_packet_internal(ds, dp2);
+                continue;
+            }
+#endif
             mp_dbg(MSGT_DEMUXER, MSGL_DBG2,
                    "ds_fill_buffer()->demux_fill_buffer() failed\n");
             break; // EOF
--- a/libmpdemux/demuxer.h	Sun Dec 27 14:42:37 2009 +0000
+++ b/libmpdemux/demuxer.h	Sun Dec 27 15:28:01 2009 +0000
@@ -382,6 +382,7 @@
 int ds_get_packet_pts(demux_stream_t *ds, unsigned char **start, double *pts);
 int ds_get_packet_sub(demux_stream_t *ds,unsigned char **start);
 double ds_get_next_pts(demux_stream_t *ds);
+int ds_parse(demux_stream_t *sh, uint8_t **buffer, int *len, double pts, off_t pos);
 
 // This is defined here because demux_stream_t ins't defined in stream.h
 stream_t* new_ds_stream(demux_stream_t *ds);
--- a/libmpdemux/stheader.h	Sun Dec 27 14:42:37 2009 +0000
+++ b/libmpdemux/stheader.h	Sun Dec 27 15:28:01 2009 +0000
@@ -31,6 +31,10 @@
   unsigned int format; \
   int initialized; \
   float stream_delay; /* number of seconds stream should be delayed (according to dwStart or similar) */ \
+  /* things needed for parsing */ \
+  int needs_parsing; \
+  struct AVCodecContext *avctx; \
+  struct AVCodecParserContext *parser; \
   /* audio: last known pts value in output from decoder \
    * video: predicted/interpolated PTS of the current frame */ \
   double pts; \