summaryrefslogtreecommitdiffstats
path: root/libmpcodecs/vd_libmpeg2.c
blob: 3e525172b54903099b8393c875d60984215cb629 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#include <stdio.h>
#include <stdlib.h>

#include "config.h"
#ifdef USE_LIBMPEG2

#include "mp_msg.h"

#include "vd_internal.h"

static vd_info_t info = 
{
	"MPEG 1/2 Video decoder v2.0",
	"libmpeg2",
	"A'rpi",
	"Aaron & Walken",
	"native"
};

LIBVD_EXTERN(libmpeg2)

#define USE_SIGJMP_TRICK

#ifdef USE_SIGJMP_TRICK
#include <signal.h>
#include <setjmp.h>
#endif

//#include "libmpdemux/parse_es.h"

#include "libvo/video_out.h"	// FIXME!!!

#include "libmpeg2/mpeg2.h"
#include "libmpeg2/mpeg2_internal.h"
#include "libmpeg2/mm_accel.h"

#include "../cpudetect.h"

mpeg2_config_t config;	// FIXME!!!
static picture_t *picture=NULL;	// exported from libmpeg2/decode.c

static int table_init_state=0;

// to set/get/query special features/parameters
static int control(sh_video_t *sh,int cmd,void* arg,...){
    return CONTROL_UNKNOWN;
}

static vo_frame_t frames[3];

// init driver
static int init(sh_video_t *sh){

    config.flags = 0;
if(gCpuCaps.hasMMX)
    config.flags |= MM_ACCEL_X86_MMX;
if(gCpuCaps.hasMMX2)
    config.flags |= MM_ACCEL_X86_MMXEXT;
if(gCpuCaps.has3DNow)
    config.flags |= MM_ACCEL_X86_3DNOW;
#ifdef HAVE_MLIB
    config.flags |= MM_ACCEL_MLIB;
#endif

    picture=malloc(sizeof(picture_t)); // !!! NEW HACK :) !!!
    memset(picture,0,sizeof(picture_t));
    header_state_init (picture);

    if(!table_init_state){
	idct_init ();
	motion_comp_init ();
	table_init_state=1;
    }

    picture->pp_options=divx_quality;
    
    memset(frames,0,3*sizeof(vo_frame_t));
    
    picture->forward_reference_frame=&frames[0];
    picture->backward_reference_frame=&frames[1];
    picture->temp_frame=&frames[2];
    picture->current_frame=NULL;
    
    // send seq header to the decoder:  *** HACK ***
//    mpeg2_decode_data(NULL,videobuffer,videobuffer+videobuf_len,0);
//    mpeg2_allocate_image_buffers (picture);
    return mpcodecs_config_vo(sh,sh->disp_w,sh->disp_h,IMGFMT_YV12);
}

// uninit driver
static void uninit(sh_video_t *sh){
//    mpeg2_free_image_buffers (picture);
}

static void draw_slice (vo_frame_t * frame, uint8_t ** src){
    int stride[3];
    int y=picture->slice<<4;

    stride[0]=picture->coded_picture_width;
    stride[1]=stride[2]=stride[0]/2;

    mpcodecs_draw_slice(frame->vo, src,
		stride, picture->display_picture_width,
		(y+16<=picture->display_picture_height) ? 16 :
		    picture->display_picture_height-y,
		0, y);
    
    ++picture->slice;
}

static int in_slice_flag=0; // FIXME! move to picture struct
static int drop_frame=0;    // FIXME! move to picture struct

static mp_image_t* parse_chunk (sh_video_t* sh, int code, uint8_t * buffer, int framedrop){
    mp_image_t* mpi=NULL;

//    stats_header (code, buffer);

    if (in_slice_flag && ((!code) || (code >= 0xb0))) {
	// ok, we've completed decoding a frame/field!
	in_slice_flag = 0;
	mpi=picture->display_frame->mpi;
	if(picture->picture_structure!=FRAME_PICTURE && !picture->second_field)
	    mpi=NULL; // we don't draw first fields!
    }

    switch (code) {
    case 0x00:	/* picture_start_code */
	if (header_process_picture_header (picture, buffer)) {
	    printf ("bad picture header\n");
	}
	drop_frame = framedrop && (picture->picture_coding_type == B_TYPE);
	drop_frame |= framedrop>=2; // hard drop
	break;

    case 0xb3:	/* sequence_header_code */
	if (header_process_sequence_header (picture, buffer)) {
	    printf ("bad sequence header\n");
	}
	break;

    case 0xb5:	/* extension_start_code */
	if (header_process_extension (picture, buffer)) {
	    printf ("bad extension\n");
	}
	break;

    default:
	if (code >= 0xb0)  break;

	if (!in_slice_flag) {
	    in_slice_flag = 1;

	    // set current_frame pointer:
	    if (!picture->second_field){
		mp_image_t* mpi;
		int flags;
		if (picture->picture_coding_type == B_TYPE){
		    flags=(!framedrop && vd_use_slices && 
			picture->picture_structure==FRAME_PICTURE) ?
			    MP_IMGFLAG_DRAW_CALLBACK:0;
		    picture->display_frame=
		    picture->current_frame = picture->temp_frame;
		} else {
		    flags=MP_IMGFLAG_PRESERVE|MP_IMGFLAG_READABLE;
		    picture->current_frame = picture->forward_reference_frame;
		    picture->display_frame=
		    picture->forward_reference_frame = picture->backward_reference_frame;
		    picture->backward_reference_frame = picture->current_frame;
		}
		mpi=mpcodecs_get_image(sh,MP_IMGTYPE_IPB, flags,
			picture->coded_picture_width,
			picture->coded_picture_height);
		// ok, lets see what did we get:
		if(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK &&
		 !(mpi->flags&MP_IMGFLAG_DIRECT)){
		    // nice, filter/vo likes draw_callback :)
		    picture->current_frame->copy=draw_slice;
		} else
		    picture->current_frame->copy=NULL;
		// let's, setup pointers!
		picture->current_frame->base[0]=mpi->planes[0];
		picture->current_frame->base[1]=mpi->planes[1];
		picture->current_frame->base[2]=mpi->planes[2];
		picture->current_frame->mpi=mpi;	// tricky!
#if 1
		if(!picture->forward_reference_frame->base[0]){
		    // workaround for sig11
		    picture->forward_reference_frame->base[0]=mpi->planes[0];
		    picture->forward_reference_frame->base[1]=mpi->planes[1];
		    picture->forward_reference_frame->base[2]=mpi->planes[2];
		}
		if(!picture->backward_reference_frame->base[0]){
		    // workaround for sig11
		    picture->backward_reference_frame->base[0]=mpi->planes[0];
		    picture->backward_reference_frame->base[1]=mpi->planes[1];
		    picture->backward_reference_frame->base[2]=mpi->planes[2];
		}
#endif
#ifdef MPEG12_POSTPROC
		mpi->qscale=&picture->current_frame->quant_store[1][1];
		mpi->qstride=(MPEG2_MBC+1);
#endif
		mp_msg(MSGT_DECVIDEO,MSGL_DBG2,"mpeg2: [%c] %p  %s  \n",
		    (picture->picture_coding_type == B_TYPE) ? 'B':'P',
		    mpi, (mpi->flags&MP_IMGFLAG_DIRECT)?"DR!":"");
	    }

	    picture->current_frame->vo=sh;
	    picture->slice=0;

	}

	if (!drop_frame) {
	    slice_process (picture, code, buffer);
#ifdef ARCH_X86
	    if (config.flags & MM_ACCEL_X86_MMX) __asm__ __volatile__ ("emms");
#endif
	}

    }
    return mpi;
}

#ifdef USE_SIGJMP_TRICK

static jmp_buf mpeg2_jmp_buf;

static void mpeg2_sighandler(int sig){
    longjmp(mpeg2_jmp_buf,1);
}
#endif

// decode a frame
static mp_image_t* decode(sh_video_t *sh,void* data,int len,int flags){
    static uint32_t code;
    static uint8_t* pos;
    static uint8_t* current;
    uint8_t* end=data+len;
    static mp_image_t* mpi;
    mp_image_t* ret=NULL;
    int framedrop=flags&3;
    void* old_sigh;

    // Note: static is REQUIRED because of longjmp() may destroy stack!
    pos=NULL;
    current=data;
    mpi=NULL;

#ifdef USE_SIGJMP_TRICK
    old_sigh=signal(SIGSEGV,mpeg2_sighandler);
#endif

while(current<end){
  // FIND NEXT HEAD:
  static unsigned int head;
  static uint8_t c;
  head=-1;
  //--------------------
  while(current<end){
      c=current[0];
      ++current;
      head<<=8;
      if(head==0x100) break; // synced
      head|=c;
  }
  //--------------------
  if(pos){
#ifdef USE_SIGJMP_TRICK
    if(setjmp(mpeg2_jmp_buf)){
#ifdef ARCH_X86
	if (config.flags & MM_ACCEL_X86_MMX) __asm__ __volatile__ ("emms");
#endif
	printf("@@@ libmpeg2 returned from sig11... (bad file?) @@@\n");
    } else
#endif
    {
	ret=parse_chunk(sh, code&0xFF, pos, framedrop);
	if(ret) mpi=ret;
    }
  }
  //--------------------
  pos=current;code=head|c;
}

#ifdef USE_SIGJMP_TRICK
    signal(SIGSEGV,old_sigh); // restore sighandler
#endif

//    if(code==0x1FF){
	ret=parse_chunk(sh, 0xFF, NULL, framedrop); // send 'end of frame'
	if(ret) mpi=ret;
//    }

    if(mpi){
        static int last_non_b_type= 0;

	if(picture->picture_coding_type == B_TYPE)
	    mpi->pict_type= B_TYPE;
	else{
	    mpi->pict_type= last_non_b_type;
	    last_non_b_type= picture->picture_coding_type;
	}
    }

    return mpi;
}
#endif