vobs with multiple mpeg / ac3 audio streams

2008-07-25
2013-05-30
  • Bernhard Oemer

    Bernhard Oemer - 2008-07-25

    Forgive me if this is an old hat: Since my Zyxel DMA1000 is too dumb to allow the client side switching of audio tracks in vob files and transcoding disables the ability to seek, I came up with a simple server-sided solution: Simply mask out certain audio streams on the fly by converting them into pad streams. Since this doesn't change the length or layout of the file, seeking is still possible. I made a small filter program for illustraion:

    #include <stdio.h>

    void maskaid(int len,unsigned char *buf,int hide) {
      unsigned char *p=buf,*pend=buf+len,*q;
      for(p=buf;p<pend-3;p++) {
        if(p[0] || p[1] || p[2]!=0x01 || p[3]<0xbd || p[3]>=0xf0) continue;
        unsigned char *pnext= p<pend-5 ? p+6+((p[4]<<8)|p[5]) : pend;
        if(pnext>pend) pnext=pend;
        if(p[3]>=0xc0 && p[3]<0xc0+hide ||
           p[3]==0xbd && p<pend-8 && (p[6]&0xc0)==0x80 &&
           (q=p+9+p[8])<pend && q[0]>=0x80 && q[0]<0x80+hide) {
          p[3]=0xbe;             /* change SID to 0xBE (padding stream) */
          for(q=p+6;q<pnext;q++) *q=0xff;    /* blank stream with 1s */
        }
        p=pnext-1;
      }
    }

    int main(int argc,char **argv) {
      int hide=1;
      unsigned char buf[2048];
      size_t len;
      if(argc>2 || argc==2 && (hide=atoi(argv[1]))<0) {
        fprintf(stderr,
          "USAGE: maskaid [n] < in.mpeg > out.mpeg\n"
          "mask out the first [n] mpeg and ac3 audio ID(s).\n"
        );
        return 1;
      }
      do {
        maskaid(len=fread(buf,1,2048,stdin),buf,hide);
        if(fwrite(buf,1,len,stdout)!=len) break;
      } while(len==2048);
      return ferror(stdin) || ferror(stdout);

    I plan to patch the FileIOHandler::read method to transparently apply the filter (after  rewinding to the last pack boundary if necessary) based on filename extension of symlinks (like *.mpeg.1 -> mask 1st aid, *.mpeg.2 -> mask 2nd aid, etc.). But while I don't see any reason why this shouldn't work and while it would solve the problem for me, obviously, this would be a really dirty hack, and a "clean" integration into MT would be preferable if there is any interest in such a functionality. So any ideas or comments?

     
    • Jin

      Jin - 2008-07-25

      Very smart idea, this padding!

      Actually, your post comes at a very good moment. I currently imeplented the parsing of DVD ISO images, I'm using libdvdread. I create a container structure, so that the DVD is presented by titles, chapters and audio track. So far I am getting an MPEG PES that corresponds to the users selection (i.e. title/chapter), however I did not yet manage to strip away unneeded audio channels. The fact that I know very little about MPEG and codecs in general is surely part of the problem :>

      I originally wanted to use ffmpeg but received a hint that it may be easier to strip away those streams natively, that was next on my TODO list. You seem to have some knowledge in the area, so the code comes very handy and I'd appreciate further help in this matter.

      I think I understand what you are generally doing; here's what I need:

      from libdvdread I get the number of audio streams, also the corresponding language and encoding type for the stream, I then present this in the virtual layout, so when the user plays a chapter from such a container he should get the desired audio stream along with the video. So, I get an index of the desired audio stream (i.e. 0 or 1 or 2, etc.) and I need to mask out the stream by the given index.

      As I understand, the streams have IDs and not an index, the IDs are different depending if its ac3 or pcm, etc.

      Do you see any easy way to figure out such a mapping, or would i have to provide other data instead of just an index?

      Regarding seeking... there's another problem, DVD only supports time based seeks, in order to figure out the actual length of the stream I have to parse the whole DVD by reading the NAV packets, so we correctly follow selected angle. This takes.. well.. 10 seconds and more. I still need to post to the libdvdread fork mailing list, maybe they can suggest a better way. So, if you also have some DVD knoweldge - please share :)

      I suggest that you contact me via email or drop by on IRC so we can have a talk, might be easier than via forums.

      Thanks!

      Kind regards,
      Jin

       
    • Bernhard Oemer

      Bernhard Oemer - 2008-07-26

      hi Jin

      > from libdvdread I get the number of audio streams, also the corresponding language and encoding type for the stream, I then present this in the virtual layout, so when the user plays a chapter from such a container he should get the desired audio stream along with the video. So, I get an index of the desired audio stream (i.e. 0 or 1 or 2, etc.) and I need to mask out the stream by the given index.

      That would be OK, basically that's exactly what the code does, provided that the index is in order of aids, the audio streams are all either mpeg audio or ac3 and the aids start at their usual values (0xc0 for mpeg and 0x80 for ac3). Pass the index as the hide parameter to

        void maskaid(int len,unsigned char *buf,int hide)

      the function will mask out the the first hide streams. I don't know if there is an explicit mapping from index to aid or what happens when both mpeg and ac3 audio is present, but a look in the mplayer code might help ...

      OK, got it. MPlayer-1.0rc1 in stream_dvd.c in function

        int open_s(stream_t *stream,int mode, void* opts, int* file_format)

      I found a switch statement which seems to hint that aid = index + stream_type_specific_offset, so the maskaid code should work fine (only for mpeg and ac3, not pcm or dts [whatever that is]). Maybe it's better to also mask all higher remaining aids in case a higher aid comes first (can be done by simply replacing the < with !=).

      > Regarding seeking... there's another problem, DVD only supports time based seeks, in order to figure out the actual length of the stream I have to parse the whole DVD by reading the NAV packets, so we correctly follow selected angle. This takes.. well.. 10 seconds and more. I still need to post to the libdvdread fork mailing list, maybe they can suggest a better way. So, if you also have some DVD knoweldge - please share :)

      No idea .. sorry. No way to avoid having to know the length of the stream beforehand?

      > I suggest that you contact me via email or drop by on IRC so we can have a talk, might be easier than via forums.

      done - I sent you the above as mail including a fragment of the MPlayer code. I'm still posting here so that others can join in.

      btw: do you, by any chance, know whether there is functionality in the UPNP protocol for the player to initiate a server side switch of the audio track. My DMA1000 remote has an audio key which might try to do this (unlikely, but one can hope ...)

      cu

      Bernhard

       

Log in to post a comment.