[mythtv] [patch] hdtv+xvmc error recovery

Daniel Thor Kristjansson danielk at cat.nyu.edu
Wed Feb 18 22:35:50 EST 2004


This patch deals with two files tv_play.cpp and xvmcvideo.c, it
increases the robustness of Myth when dealing with bad MPEG2 data.


The change to tv_play is small, this simply reverts the state to
kState_None if IsDecoderThreadAlive() fails shortly after startup.
This happens when the hdtvrecorder can not start, because there
are not enough valid transport stream packets or because it can not
decode the pid's or for some other yet to be reported problem. All this
really does for me is just avoid a segfault, the signalcheck that will
be in a future patch is more useful.


The changes to xvmcvideo are more extensive. First off, XVMC_field_start
sets the render->flags variable correctly, this is useful for hardware
deinterlacing and is also just the correct thing to do.

I also set a new variable in render called pict_type, which simply
mirrors the encoding context's pict_type, it can be useful later on
to know if a frame is an I, P or B frame. This can be guessed from
whether we have past or future frames assigned, but this can be
misleading.

The next change is to the finding of past and future I or P frames.
Instead of just bailing if we cannot find these frames, we march on.
This is important because sometimes we don't have past or future frames
as we should. I believe this is due to the low buffer situation we have
with XvMC, but may be due to some error in the avcodec library. Either
way, it is better to have our picture degrade for a moment with an
error message on the console, than for mythtv to just exit when this
happens.

The final set of changes are to XVMC_decode_mb. This deals with
malformed blocks. Basically it prints error messages if we get blocks
that we shouldn't be getting, such as a MV_TYPE_16X8 field motion vector
in a frame encoded block. Or, if we get any MPEG4 motion vectors in this
MPEG2 decoder. Or, a MV_TYPE_16x16 frame motion vector in a field encoded
block. My guess is this happens because corrupted TS packets are not
disposed of earlier, but it happens and we should at least output some
error messages when it does.

-- Daniel
  << When truth is outlawed; only outlaws will tell the truth. >> - RLiegh
-------------- next part --------------
Index: libs/libavcodec/xvmc_render.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libavcodec/xvmc_render.h,v
retrieving revision 1.2
diff -u -r1.2 xvmc_render.h
--- libs/libavcodec/xvmc_render.h	30 Aug 2003 18:26:02 -0000	1.2
+++ libs/libavcodec/xvmc_render.h	19 Feb 2004 02:44:57 -0000
@@ -46,5 +46,6 @@
   int filled_mv_blocks_num;//processed mv block in this slice,change by decoder
   
   int next_free_data_block_num;//used in add_mv_block, pointer to next free block
-
+//this is for skipping frames...
+  int pict_type;
 } xvmc_render_state_t;
Index: libs/libavcodec/xvmcvideo.c
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libavcodec/xvmcvideo.c,v
retrieving revision 1.4
diff -u -r1.4 xvmcvideo.c
--- libs/libavcodec/xvmcvideo.c	28 Oct 2003 21:48:28 -0000	1.4
+++ libs/libavcodec/xvmcvideo.c	19 Feb 2004 02:44:58 -0000
@@ -26,6 +26,7 @@
 
 #undef NDEBUG
 #include <assert.h>
+#include <stdio.h>
 
 #ifdef USE_FASTMEMCPY
 #include "fastmemcpy.h"
@@ -81,6 +82,48 @@
 
 
 
+XvMCSurface* findPastSurface(MpegEncContext *s, xvmc_render_state_t *render) {
+    Picture *lastp = s->last_picture_ptr;
+    xvmc_render_state_t *last = NULL;
+    
+    if (NULL!=lastp) {
+        last = (xvmc_render_state_t*)(lastp->data[2]);
+        if (B_TYPE==last->pict_type)
+            fprintf(stderr, "Past frame is a B frame in findPastSurface, this is bad.\n");
+        //assert(B_TYPE!=last->pict_type);
+    }
+
+    if (NULL==last) 
+        if (!s->first_field)
+            last = render; // predict second field from the first
+        else
+            return 0;
+    
+    if (last->magic != MP_XVMC_RENDER_MAGIC) 
+        return 0;
+
+    return (last->state & MP_XVMC_STATE_PREDICTION) ? last->p_surface : 0;
+}
+
+XvMCSurface* findFutureSurface(MpegEncContext *s) {
+    Picture *nextp = s->next_picture_ptr;
+    xvmc_render_state_t *next = NULL;
+    
+    if (NULL!=nextp) {
+        next = (xvmc_render_state_t*)(nextp->data[2]);
+        if (B_TYPE==next->pict_type)
+            fprintf(stderr, "Next frame is a B frame in findFutureSurface, this is bad.\n");
+        //assert(B_TYPE!=next->pict_type);
+    }
+
+    assert(NULL!=next);
+
+    if (next->magic != MP_XVMC_RENDER_MAGIC)
+        return 0;
+
+    return (next->state & MP_XVMC_STATE_PREDICTION) ? next->p_surface : 0;
+}
+
 //these functions should be called on every new field or/and frame
 //They should be safe if they are called few times for same field!
 int XVMC_field_start(MpegEncContext*s, AVCodecContext *avctx){
@@ -94,7 +137,14 @@
         return -1;//make sure that this is render packet
 
     render->picture_structure = s->picture_structure;
-    render->flags = (s->first_field)? 0: XVMC_SECOND_FIELD;
+    if (render->picture_structure==PICT_FRAME)
+        render->flags = XVMC_FRAME_PICTURE;
+    else if (render->picture_structure==PICT_TOP_FIELD)
+        render->flags = XVMC_TOP_FIELD;
+    else if (render->picture_structure==PICT_BOTTOM_FIELD)
+        render->flags = XVMC_BOTTOM_FIELD;
+    if (!s->first_field) 
+        render->flags |= XVMC_SECOND_FIELD;
 
 //make sure that all data is drawn by XVMC_end_frame
     assert(render->filled_mv_blocks_num==0);
@@ -102,25 +152,25 @@
     render->p_future_surface = NULL;
     render->p_past_surface = NULL;
 
+    render->pict_type=s->pict_type; // for later frame dropping use
     switch(s->pict_type){
         case  I_TYPE:
             return 0;// no prediction from other frames
         case  B_TYPE:
-            next = (xvmc_render_state_t*)s->next_picture.data[2];
-            assert(next!=NULL);
-            assert(next->state & MP_XVMC_STATE_PREDICTION);
-            if(next == NULL) return -1;
-            if(next->magic != MP_XVMC_RENDER_MAGIC) return -1;
-            render->p_future_surface = next->p_surface;
-            //no return here, going to set forward prediction
+            render->p_past_surface = findPastSurface(s, render);
+            render->p_future_surface = findFutureSurface(s);
+            if (!render->p_past_surface)
+                fprintf(stderr, "error: decoding B frame and past frame is null!");
+            else if (!render->p_future_surface)
+                fprintf(stderr, "error: decoding B frame and future frame is null!");
+            else return 0;
+            return 0; // pretend things are ok even if they aren't
         case  P_TYPE:
-            last = (xvmc_render_state_t*)s->last_picture.data[2];
-            if(last == NULL)// && !s->first_field)
-                last = render;//predict second field from the first
-            if(last->magic != MP_XVMC_RENDER_MAGIC) return -1;
-            assert(last->state & MP_XVMC_STATE_PREDICTION);
-            render->p_past_surface = last->p_surface;
-            return 0;
+            render->p_past_surface = findPastSurface(s, render);
+            if (!render->p_past_surface)
+                fprintf(stderr, "error: decoding P frame and past frame is null!");
+            else return 0;
+            return 0; // pretend things are ok even if they aren't
      }
 
 return -1;
@@ -180,16 +230,31 @@
 
     mv_block->x = s->mb_x;
     mv_block->y = s->mb_y;
-    mv_block->dct_type = s->interlaced_dct;//XVMC_DCT_TYPE_FRAME/FIELD;
-//    mv_block->motion_type = 0;  //zero to silense warnings
-    if(s->mb_intra){
-        mv_block->macroblock_type = XVMC_MB_TYPE_INTRA;//no MC, all done
-    }else{
-        mv_block->macroblock_type = XVMC_MB_TYPE_PATTERN;
 
-        if(s->mv_dir & MV_DIR_FORWARD){
+    if (0) {
+        static unsigned char orig=0xff;
+        unsigned char newc = s->picture_structure;
+        if (orig!=newc)
+            fprintf(stderr, "XVMC_DCT_TYPE_FRAME/FIELD: %i\n", (int) newc);
+        orig=newc;
+    }
+
+    int frametype = (PICT_FRAME==s->picture_structure) ? XVMC_DCT_TYPE_FRAME : XVMC_DCT_TYPE_FIELD;
+    mv_block->dct_type = s->interlaced_dct;
+    /*
+      Quote from XvMC standard:
+    dct_type -  This field indicates whether frame pictures are frame DCT
+                coded or field DCT coded. ie XVMC_DCT_TYPE_FIELD or
+                XVMC_DCT_TYPE_FRAME.
+    */
+
+    mv_block->macroblock_type = XVMC_MB_TYPE_INTRA;
+    if (!s->mb_intra) {
+        mv_block->macroblock_type = XVMC_MB_TYPE_PATTERN;
+        if (s->mv_dir & MV_DIR_FORWARD) {
             mv_block->macroblock_type|= XVMC_MB_TYPE_MOTION_FORWARD;
             //pmv[n][dir][xy]=mv[dir][n][xy]
+            //PMV[hozontal|vertical][forward|backward][first|second vector]
             mv_block->PMV[0][0][0] = s->mv[0][0][0];
             mv_block->PMV[0][0][1] = s->mv[0][0][1];
             mv_block->PMV[1][0][0] = s->mv[0][1][0];
@@ -202,90 +267,93 @@
             mv_block->PMV[1][1][0] = s->mv[1][1][0];
             mv_block->PMV[1][1][1] = s->mv[1][1][1];
         }
-
-        switch(s->mv_type){
-            case  MV_TYPE_16X16:
-                mv_block->motion_type = XVMC_PREDICTION_FRAME;
-                break;
-            case  MV_TYPE_16X8:
-                mv_block->motion_type = XVMC_PREDICTION_16x8;
-                break;
-            case  MV_TYPE_FIELD:
-                mv_block->motion_type = XVMC_PREDICTION_FIELD;
-                if(s->picture_structure == PICT_FRAME){
-                    mv_block->PMV[0][0][1]<<=1;
-                    mv_block->PMV[1][0][1]<<=1;
-                    mv_block->PMV[0][1][1]<<=1;
-                    mv_block->PMV[1][1][1]<<=1;
-                }
-                break;
-            case  MV_TYPE_DMV:
-                mv_block->motion_type = XVMC_PREDICTION_DUAL_PRIME;
-                if(s->picture_structure == PICT_FRAME){
-
-                    mv_block->PMV[0][0][0] = s->mv[0][0][0];//top from top
-                    mv_block->PMV[0][0][1] = s->mv[0][0][1]<<1;
-
-                    mv_block->PMV[0][1][0] = s->mv[0][0][0];//bottom from bottom
-                    mv_block->PMV[0][1][1] = s->mv[0][0][1]<<1;
-
-                    mv_block->PMV[1][0][0] = s->mv[0][2][0];//dmv00, top from bottom
-                    mv_block->PMV[1][0][1] = s->mv[0][2][1]<<1;//dmv01
-
-                    mv_block->PMV[1][1][0] = s->mv[0][3][0];//dmv10, bottom from top
-                    mv_block->PMV[1][1][1] = s->mv[0][3][1]<<1;//dmv11
-
-                }else{
-                    mv_block->PMV[0][1][0] = s->mv[0][2][0];//dmv00
-                    mv_block->PMV[0][1][1] = s->mv[0][2][1];//dmv01
-                }
-                break;
-            default:
-                assert(0);
-        }
-
-        mv_block->motion_vertical_field_select = 0;
-
-//set correct field referenses
+        int mvfs=0;
+        //set correct field referenses
         if(s->mv_type == MV_TYPE_FIELD || s->mv_type == MV_TYPE_16X8){
-            if( s->field_select[0][0] ) mv_block->motion_vertical_field_select|=1;
-            if( s->field_select[1][0] ) mv_block->motion_vertical_field_select|=2;
-            if( s->field_select[0][1] ) mv_block->motion_vertical_field_select|=4;
-            if( s->field_select[1][1] ) mv_block->motion_vertical_field_select|=8;
+            if (s->field_select[0][0]) mvfs |= XVMC_SELECT_FIRST_FORWARD;
+            if (s->field_select[1][0]) mvfs |= XVMC_SELECT_FIRST_BACKWARD;
+            if (s->field_select[0][1]) mvfs |= XVMC_SELECT_SECOND_FORWARD;
+            if (s->field_select[1][1]) mvfs |= XVMC_SELECT_SECOND_BACKWARD;
         }
-    }//!intra
-//time to handle data blocks;
-    mv_block->index = render->next_free_data_block_num;
-    blocks_per_mb = 6;
-/*
-    switch( s->chroma_format){
-        case CHROMA_422:
-            blocks_per_mb = 8;
+        mv_block->motion_vertical_field_select = mvfs;
+    } //!intra
+    
+    int mt = XVMC_PREDICTION_FRAME;
+    if (XVMC_DCT_TYPE_FRAME==frametype) {
+        switch (s->mv_type) {
+        case MV_TYPE_16X16: mt=XVMC_PREDICTION_FRAME;      break;
+        case MV_TYPE_FIELD: mt=XVMC_PREDICTION_FIELD; 
+            mv_block->PMV[0][0][1]<<=1;
+            mv_block->PMV[1][0][1]<<=1;
+            mv_block->PMV[0][1][1]<<=1;
+            mv_block->PMV[1][1][1]<<=1;
+            break;
+        case MV_TYPE_DMV:   mt=XVMC_PREDICTION_DUAL_PRIME;
+            mv_block->PMV[0][0][0] = s->mv[0][0][0]; // top    from top
+            mv_block->PMV[0][1][0] = s->mv[0][0][0]; // bottom from bottom
+            mv_block->PMV[1][0][0] = s->mv[0][2][0]; // top    from bottom
+            mv_block->PMV[1][1][0] = s->mv[0][3][0]; // bottom from top
+            mv_block->PMV[0][0][1] = s->mv[0][0][1]<<1;
+            mv_block->PMV[0][1][1] = s->mv[0][0][1]<<1;
+            mv_block->PMV[1][0][1] = s->mv[0][2][1]<<1;
+            mv_block->PMV[1][1][1] = s->mv[0][3][1]<<1;
+            break;
+        case MV_TYPE_16X8:  fprintf(stderr, "XVMC: Field motion vector type with frame picture!\n");
+            break;
+        case MV_TYPE_8X8:   fprintf(stderr, "XVMC: H263/MPEG4 motion vector type in MPEG2 decoder!\n"); 
+            break;
+        default:            fprintf(stderr, "XVMC: Unknown motion vector type!\n");
+        }
+    } else {
+        switch (s->mv_type) {
+        case MV_TYPE_16X8:  mt=XVMC_PREDICTION_16x8;       break;
+        case MV_TYPE_FIELD: mt=XVMC_PREDICTION_FIELD;      break;
+        case MV_TYPE_DMV:   mt=XVMC_PREDICTION_DUAL_PRIME;
+            //pmv[n][dir][xy]=mv[dir][n][xy]
+            //PMV[hozontal|vertical][forward|backward][first|second vector]
+            mv_block->PMV[0][1][0] = s->mv[0][2][0];//dmv00
+            mv_block->PMV[0][1][1] = s->mv[0][2][1];//dmv01
             break;
-        case CHROMA_444:
-            blocks_per_mb = 12;
+        case MV_TYPE_16X16: fprintf(stderr, "XVMC: Frame motion vector type with field picture!\n");
             break;
+        case MV_TYPE_8X8:   fprintf(stderr, "XVMC: H263/MPEG4 motion vector type in MPEG2 decoder!\n"); 
+            break;
+        default:            fprintf(stderr, "XVMC: Unknown motion vector type!\n");
+        }
     }
-*/
-    if(s->flags & CODEC_FLAG_GRAY){
-        if(s->mb_intra){//intra frames are alwasy full chroma block
-            for(i=4; i<blocks_per_mb; i++){
-                memset(s->pblocks[i],0,sizeof(short)*8*8);//so we need to clear them
+    mv_block->motion_type = mt;
+
+
+    // Handle data blocks
+
+
+    // Offset in units of 128 bytes from start of block array to this macroblocks DCT blocks begin
+    mv_block->index = render->next_free_data_block_num;
+
+    if (s->flags & CODEC_FLAG_GRAY) {
+        if (s->mb_intra) { //intra frames are always full chroma block
+            blocks_per_mb = 6;
+            for (i=4; i<blocks_per_mb; i++) {
+                memset(s->pblocks[i],0,sizeof(short)*8*8); //so we need to clear them
                 if(!render->unsigned_intra)
                     s->pblocks[i][0] = 1<<10;
             }
-        }else
-            blocks_per_mb = 4;//Luminance blocks only
+        } else blocks_per_mb = 4; //Luminance blocks only
+    } else blocks_per_mb = 6; // 4 luminance + 2 color blocks in macroblock
+
+    if ((mv_block->macroblock_type & XVMC_MB_TYPE_PATTERN) || 
+        (mv_block->macroblock_type & XVMC_MB_TYPE_INTRA)) {
+        cbp = calc_cbp(s,blocks_per_mb);
+        mv_block->coded_block_pattern = cbp;
+
+        // ????
+        if (0==cbp) mv_block->macroblock_type &= ~XVMC_MB_TYPE_PATTERN;
     }
-    cbp = calc_cbp(s,blocks_per_mb);
-    mv_block->coded_block_pattern = cbp;
-    if(cbp == 0)
-        mv_block->macroblock_type &= ~XVMC_MB_TYPE_PATTERN;
 
-    for(i=0; i<blocks_per_mb; i++){
-        if(s->block_last_index[i] >= 0){
+    for (i=0; i<blocks_per_mb; i++){
+        if (s->block_last_index[i] >= 0){
             // i do not have unsigned_intra MOCO to test, hope it is OK
-            if( (s->mb_intra) && ( render->idct || (!render->idct && !render->unsigned_intra)) )
+            if ( (s->mb_intra) && (render->idct || (!render->idct && !render->unsigned_intra)) )
                 s->pblocks[i][0]-=1<<10;
             if(!render->idct){
                 s->dsp.idct(s->pblocks[i]);
Index: libs/libmythtv/tv_play.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/tv_play.cpp,v
retrieving revision 1.158
diff -u -r1.158 tv_play.cpp
--- libs/libmythtv/tv_play.cpp	18 Feb 2004 03:52:50 -0000	1.158
+++ libs/libmythtv/tv_play.cpp	19 Feb 2004 02:45:00 -0000
@@ -634,6 +634,17 @@
     {
         VERBOSE(VB_GENERAL, QString("Changing from %1 to %2")
                 .arg(origname).arg(statename));
+        if (!activenvp->IsDecoderThreadAlive() && kState_None!=nextState)
+        {
+            VERBOSE(VB_IMPORTANT, "Decoder not alive and state not None");
+            if (kState_WatchingLiveTV==nextState)
+            {
+                VERBOSE(VB_IMPORTANT, "Stopping recorder");
+                StopPlayerAndRecorder(false, true);
+                recorder=false;
+            }
+            tmpInternalState=kState_None;
+        }
     }
 
     internalState = tmpInternalState;


More information about the mythtv-dev mailing list