annotate libavcodec/h264_misc.c @ 9:ea1ba68cf0ed

update to match api changes + add sscc produced source
author Nina Engelhardt <nengel@mailbox.tu-berlin.de>
date Wed, 05 Jun 2013 14:43:26 +0200
parents 0b056460c67d
children
rev   line source
nengel@2 1 #include "config.h"
nengel@2 2
nengel@2 3 #include "h264_types.h"
nengel@2 4
nengel@2 5 #include <unistd.h>
nengel@2 6 #include <sys/resource.h>
nengel@2 7 #include <sys/time.h>
nengel@2 8 #include <time.h>
nengel@2 9 #include <pthread.h>
nengel@2 10 #undef NDEBUG
nengel@2 11 #include <assert.h>
nengel@2 12
nengel@2 13 #if HAVE_LIBSDL2
nengel@2 14 #include <SDL2/SDL.h>
nengel@2 15 #if HAVE_LIBSDL_TTF
nengel@2 16 #include <SDL/SDL_ttf.h>
nengel@2 17 #endif
nengel@2 18 #endif
nengel@2 19
nengel@2 20 void start_timer(H264Context *h, int stage){
nengel@2 21 clock_gettime(CLOCK_REALTIME, &h->start_time[stage]);
nengel@2 22 }
nengel@2 23
nengel@2 24 void stop_timer(H264Context *h, int stage){
nengel@2 25 clock_gettime(CLOCK_REALTIME, &h->end_time[stage]);
nengel@2 26 double time = (double) 1.e3*(h->end_time[stage].tv_sec - h->start_time[stage].tv_sec) + 1.e-6*(h->end_time[stage].tv_nsec - h->start_time[stage].tv_nsec);
nengel@2 27 h->last_time [stage] = time;
nengel@2 28 h->total_time[stage] += time;
nengel@2 29 }
nengel@2 30
nengel@2 31 void init_sb_entry(H264Context *h, SliceBufferEntry *sbe){
nengel@2 32 sbe->mbs = av_malloc(h->mb_width*h->mb_height* sizeof(H264Mb));
nengel@2 33 sbe->initialized = 1;
nengel@2 34 }
nengel@2 35
nengel@2 36 void free_sb_entry(SliceBufferEntry *sbe){
nengel@2 37 av_free(sbe->mbs);
nengel@2 38 av_freep(&sbe->gb.raw);
nengel@2 39 if (sbe->gb.rbsp)
nengel@2 40 av_freep(&sbe->gb.rbsp);
nengel@2 41 sbe->initialized = 0;
nengel@2 42 }
nengel@2 43
nengel@2 44 SliceBufferEntry *get_sb_entry(H264Context *h){
nengel@2 45 SliceBufferEntry *sb = NULL;
nengel@2 46
nengel@2 47 pthread_mutex_lock(&h->lock[PARSE]);
nengel@2 48 while (h->free_sb_cnt<=0)
nengel@2 49 pthread_cond_wait(&h->cond[PARSE], &h->lock[PARSE]);
nengel@2 50 /* use first free picture */
nengel@2 51 for(int i=0; i<h->sb_size; i++){
nengel@2 52 if(h->sb[i].state==0){
nengel@2 53 sb= &h->sb[i];
nengel@2 54 sb->state=1;
nengel@2 55 sb->lines_taken=0;
nengel@2 56 sb->lines_total=h->mb_height;
nengel@2 57 break;
nengel@2 58 }
nengel@2 59 }
nengel@2 60 h->free_sb_cnt--;
nengel@2 61
nengel@2 62 pthread_mutex_unlock(&h->lock[PARSE]);
nengel@2 63
nengel@2 64 memset (&sb->slice, 0, sizeof(H264Slice));
nengel@2 65
nengel@2 66 return sb;
nengel@2 67 }
nengel@2 68
nengel@2 69 void release_sb_entry(H264Context *h, SliceBufferEntry *sb){
nengel@2 70 pthread_mutex_lock(&h->lock[PARSE]);
nengel@2 71
nengel@2 72 sb->state = 0;
nengel@2 73 h->free_sb_cnt++;
nengel@2 74 pthread_cond_signal(&h->cond[PARSE]);
nengel@2 75
nengel@2 76 pthread_mutex_unlock(&h->lock[PARSE]);
nengel@2 77 }
nengel@2 78
nengel@2 79 int init_dpb_entry(H264Context *h, DecodedPicture *pic, H264Slice *s, int width, int height){
nengel@2 80 int i;
nengel@2 81
nengel@2 82 s->curr_pic=pic;
nengel@2 83 pic->poc = s->poc;
nengel@2 84 pic->key_frame = s->key_frame;
nengel@2 85 pic->mmco_reset = s->mmco_reset;
nengel@2 86 pic->reference = s->nal_ref_idc? 3:1;
nengel@2 87 pic->cpn = s->coded_pic_num;
nengel@2 88
nengel@2 89 if(pic->data[0]==NULL) {
nengel@2 90 int size[3] = {0};
nengel@2 91
nengel@2 92 width+= EDGE_WIDTH*2;
nengel@2 93 height+= EDGE_WIDTH*2;
nengel@2 94
nengel@2 95 pic->linesize[0]= width;
nengel@2 96 pic->linesize[1]= pic->linesize[2] = width>>1;
nengel@2 97
nengel@2 98 size[0] = width*height;
nengel@2 99 size[1] = size[2] = width*height>>2;
nengel@2 100
nengel@2 101 for(i=0; i<3; i++){
nengel@2 102 pic->base[i]= av_malloc(size[i]);
nengel@2 103 }
nengel@2 104
nengel@2 105 pic->data[0] = pic->base[0] + (pic->linesize[0]*EDGE_WIDTH) + EDGE_WIDTH;
nengel@2 106 pic->data[1] = pic->base[1] + (pic->linesize[1]*EDGE_WIDTH>>1) + (EDGE_WIDTH>>1);
nengel@2 107 pic->data[2] = pic->base[2] + (pic->linesize[2]*EDGE_WIDTH>>1) + (EDGE_WIDTH>>1);
nengel@2 108 }
nengel@2 109
nengel@2 110 const int big_mb_num= h->mb_stride*(h->mb_height+1) + 1; //the +1 is needed so memset(,,stride*height) does not sig11
nengel@2 111 const int mb_array_size= h->mb_stride*h->mb_height;
nengel@2 112 const int b4_array_size= h->b4_stride*h->mb_height*4;
nengel@2 113
nengel@2 114 if(pic->mb_type_base==NULL){
nengel@2 115 FF_ALLOCZ_OR_GOTO(pic->mb_type_base , big_mb_num * sizeof(uint32_t), fail)
nengel@2 116 pic->mb_type= pic->mb_type_base + h->mb_stride+1;
nengel@2 117
nengel@2 118 for(int i=0; i<2; i++){
nengel@2 119 FF_ALLOCZ_OR_GOTO(pic->motion_val_base[i], 2 * (b4_array_size+4) * sizeof(int16_t), fail)
nengel@2 120 pic->motion_val[i]= pic->motion_val_base[i]+4;
nengel@2 121 FF_ALLOCZ_OR_GOTO(pic->ref_index[i], 4*mb_array_size * sizeof(uint8_t), fail)
nengel@2 122 }
nengel@2 123 FF_ALLOCZ_OR_GOTO(pic->intra4x4_pred_mode, h->mb_width*h->mb_height * 4* sizeof(int8_t), fail)
nengel@2 124 }
nengel@2 125
nengel@2 126 return 0;
nengel@2 127 fail:
nengel@2 128 return -1;
nengel@2 129 }
nengel@2 130
nengel@2 131 void free_dp(DecodedPicture *pic){
nengel@2 132 if(pic->base[0]){
nengel@2 133 for (int i=0; i<3; i++){
nengel@2 134 av_free(pic->base[i]);
nengel@2 135 pic->data[i]= NULL;
nengel@2 136 }
nengel@2 137 }
nengel@2 138 if (pic->mb_type_base){
nengel@2 139 av_free(pic->mb_type_base);
nengel@2 140 pic->mb_type= NULL;
nengel@2 141 for(int i=0; i<2; i++){
nengel@2 142 av_free(pic->motion_val_base[i]);
nengel@2 143 av_free(pic->ref_index[i]);
nengel@2 144 }
nengel@2 145 av_free(pic->intra4x4_pred_mode);
nengel@2 146 }
nengel@2 147 }
nengel@2 148
nengel@2 149 DecodedPicture *get_dpb_entry(H264Context *h, H264Slice *s){
nengel@2 150 DecodedPicture *dp = NULL;
nengel@2 151
nengel@2 152 pthread_mutex_lock(&h->lock[REORDER2]);
nengel@2 153 while (h->free_dpb_cnt<=0){
nengel@2 154 #if OMPSS
nengel@2 155 assert(0);
nengel@2 156 #endif
nengel@2 157 pthread_cond_wait(&h->cond[REORDER2], &h->lock[REORDER2]);
nengel@2 158 }
nengel@2 159 /* use first free picture */
nengel@2 160 for(int i=0; i<h->max_dpb_cnt; i++){
nengel@2 161 if(h->dpb[i].reference==0){
nengel@2 162 dp= &h->dpb[i];
nengel@2 163 break;
nengel@2 164 }
nengel@2 165 }
nengel@2 166 assert(dp);
nengel@2 167 init_dpb_entry(h, dp, s, h->width, h->height);
nengel@2 168 h->free_dpb_cnt--;
nengel@2 169 h->acdpb_cnt++; //debug
nengel@2 170 pthread_mutex_unlock(&h->lock[REORDER2]);
nengel@2 171
nengel@2 172 return dp;
nengel@2 173 }
nengel@2 174
nengel@2 175 void release_dpb_entry(H264Context *h, DecodedPicture *pic, int mode){
nengel@2 176 pthread_mutex_lock(&h->lock[REORDER2]);
nengel@2 177 pic->reference &= ~mode;
nengel@2 178 if (pic->reference == 0){
nengel@2 179 h->free_dpb_cnt++;
nengel@2 180 h->reldpb_cnt++; //debug
nengel@2 181 pthread_cond_signal(&h->cond[REORDER2]);
nengel@2 182 }
nengel@2 183 pthread_mutex_unlock(&h->lock[REORDER2]);
nengel@2 184 }
nengel@2 185
nengel@2 186
nengel@2 187 /**
nengel@2 188 * Extends the edges of a macroblock line.
nengel@2 189 */
nengel@2 190 void draw_edges(MBRecContext *d, H264Slice *s, int line){
nengel@2 191 int i;
nengel@2 192 int mb_width=d->mb_width;
nengel@2 193 int mb_height=d->mb_height;
nengel@2 194 int last = (line+1 == mb_height);
nengel@2 195 int lines = last?16:12;
nengel@2 196 int linesize = d->linesize;
nengel@2 197 int uvlinesize = d->uvlinesize;
nengel@2 198 uint8_t *y = s->curr_pic->data[0] + 16*line*linesize;
nengel@2 199 uint8_t *cb = s->curr_pic->data[1] + 8*line*uvlinesize;
nengel@2 200 uint8_t *cr = s->curr_pic->data[2] + 8*line*uvlinesize;
nengel@2 201
nengel@2 202 for (i=-4; i<lines; i++){
nengel@2 203 memset(y + i*linesize - EDGE_WIDTH, y[i*linesize], EDGE_WIDTH);
nengel@2 204 memset(y + i*linesize + mb_width*16, y[i*linesize +mb_width*16 -1], EDGE_WIDTH);
nengel@2 205 }
nengel@2 206 for (i=-2; i<lines/2; i++){
nengel@2 207 memset(cb + i*uvlinesize - EDGE_WIDTH/2, cb[i*uvlinesize], EDGE_WIDTH/2);
nengel@2 208 memset(cb + i*uvlinesize + mb_width*8, cb[i*uvlinesize +mb_width*8 -1], EDGE_WIDTH/2);
nengel@2 209 memset(cr + i*uvlinesize - EDGE_WIDTH/2, cr[i*uvlinesize], EDGE_WIDTH/2);
nengel@2 210 memset(cr + i*uvlinesize + mb_width*8, cr[i*uvlinesize +mb_width*8 -1], EDGE_WIDTH/2);
nengel@2 211 }
nengel@2 212
nengel@2 213 if (line==0){
nengel@2 214 y -= EDGE_WIDTH;
nengel@2 215 cb -= EDGE_WIDTH/2;
nengel@2 216 cr -= EDGE_WIDTH/2;
nengel@2 217 for (i=1; i<=21; i++){
nengel@2 218 memcpy(y -i*linesize, y, linesize);
nengel@2 219 }
nengel@2 220 for (i=1; i<=9; i++){
nengel@2 221 memcpy(cb -i*uvlinesize, cb, uvlinesize);
nengel@2 222 memcpy(cr -i*uvlinesize, cr, uvlinesize);
nengel@2 223 }
nengel@2 224 }else if (last){
nengel@2 225 y += -EDGE_WIDTH + 15*linesize;
nengel@2 226 cb += -EDGE_WIDTH/2 + 7*uvlinesize;
nengel@2 227 cr += -EDGE_WIDTH/2 + 7*uvlinesize;
nengel@2 228 for (i=1; i<=21; i++){
nengel@2 229 memcpy(y +i*linesize, y, linesize);
nengel@2 230 }
nengel@2 231 for (i=1; i<=9; i++){
nengel@2 232 memcpy(cb +i*uvlinesize, cb, uvlinesize);
nengel@2 233 memcpy(cr +i*uvlinesize, cr, uvlinesize);
nengel@2 234 }
nengel@2 235 }
nengel@2 236 }
nengel@2 237
nengel@2 238 static int64_t timer_start;
nengel@2 239 int64_t av_gettime(void) {
nengel@2 240 struct timeval tv;
nengel@2 241 gettimeofday(&tv,NULL);
nengel@2 242 return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
nengel@2 243 }
nengel@2 244
nengel@2 245 void av_start_timer(){
nengel@2 246 timer_start = av_gettime();
nengel@2 247 }
nengel@2 248
nengel@2 249 void print_report(int frame_number, uint64_t video_size, int is_last_report, int verbose) {
nengel@2 250 static int64_t last_time = -1;
nengel@2 251 static int64_t last_frame_number = 0;
nengel@2 252 float t=0, t2=0;
nengel@2 253 int64_t cur_time=0;
nengel@2 254
nengel@2 255 if (!is_last_report) {
nengel@2 256 /* display the report every 0.5 seconds */
nengel@2 257 cur_time = av_gettime();
nengel@2 258 if (last_time == -1) {
nengel@2 259 last_time = cur_time;
nengel@2 260 return;
nengel@2 261 }
nengel@2 262 if ((cur_time - last_time) < 500000)
nengel@2 263 return;
nengel@2 264 t = (cur_time-timer_start) / 1000000.0;
nengel@2 265 t2 = (cur_time-last_time) / 1000000.0;
nengel@2 266 }
nengel@2 267
nengel@2 268 if (verbose){
nengel@6 269 fprintf(stderr, "frame=%5d avgfps=%3d curfps=%3d\r", frame_number, (int)(frame_number/t+0.5), (int)((frame_number - last_frame_number)/t2+0.5) );
nengel@6 270 fflush(stderr);
nengel@2 271 }
nengel@2 272 last_frame_number = frame_number;
nengel@2 273 last_time = cur_time;
nengel@2 274
nengel@2 275 if (is_last_report){
nengel@2 276 t = (av_gettime()-timer_start) / 1000000.0;
nengel@6 277 fprintf(stderr, "%c[2Kframe=%5d avgfps=%3d\r", 27, frame_number, (int)(frame_number/t+0.5));
nengel@6 278 fprintf(stderr, "\n");
nengel@6 279 fprintf(stderr, "video:%1.0fkB\n", video_size/1024.0);
nengel@6 280 fflush(stderr);
nengel@2 281 }
nengel@2 282 }
nengel@2 283
nengel@2 284 /* Sort B-frames into display order */
nengel@2 285 static DecodedPicture *get_reordered_picture(OutputContext *w, int flush){
nengel@2 286 int i;
nengel@2 287 int out_idx = 0;
nengel@2 288 DecodedPicture *out = w->delayed_pic[0];
nengel@2 289
nengel@2 290 if (!out)
nengel@2 291 return NULL;
nengel@2 292
nengel@2 293 for(i=1; w->delayed_pic[i] && !w->delayed_pic[i]->key_frame && !w->delayed_pic[i]->mmco_reset; i++){
nengel@2 294 if(w->delayed_pic[i]->poc < out->poc){
nengel@2 295 out = w->delayed_pic[i];
nengel@2 296 out_idx = i;
nengel@2 297 }
nengel@2 298 }
nengel@2 299
nengel@2 300 if(w->dp_cnt > MAX_DELAYED_PIC_COUNT || flush) {
nengel@2 301 for(i=out_idx; w->delayed_pic[i]; i++)
nengel@2 302 w->delayed_pic[i] = w->delayed_pic[i+1];
nengel@2 303 w->dp_cnt--;
nengel@2 304 return out;
nengel@2 305 }
nengel@2 306 return NULL;
nengel@2 307 }
nengel@2 308
nengel@2 309 /**
nengel@2 310 * Remove the extra borders, and places the three parts of the image after each other.
nengel@2 311 */
nengel@2 312 static int raw_encode(const DecodedPicture* src, int width, int height, unsigned char *dest) {
nengel@2 313 int i, j;
nengel@2 314 /** To write entire image including extra borders*/
nengel@2 315 // int w = src->linesize[0];
nengel@2 316 // int h = height+64;
nengel@2 317 // int w2 = w>>1;
nengel@2 318 // int h2 = h>>1;
nengel@2 319 // int data_planes=3;
nengel@2 320 // int size = w * h + 2 *w2*h2;
nengel@2 321 // const unsigned char* s;
nengel@2 322 // for (i=0; i<data_planes; i++) {
nengel@2 323 // if (i == 1) {
nengel@2 324 // w = w2;
nengel@2 325 // h = h2;
nengel@2 326 // }
nengel@2 327 // s = src->base[i];
nengel@2 328 // for(j=0; j<h; j++) {
nengel@2 329 // memcpy(dest, s, src->linesize[i]);
nengel@2 330 // dest += w;
nengel@2 331 // s += src->linesize[i];
nengel@2 332 // }
nengel@2 333 // }
nengel@2 334
nengel@2 335 int w = (width*8 + 7)/8;
nengel@2 336 int h = height;
nengel@2 337 int w2 =((width >>1) * 8 + 7) / 8;
nengel@2 338 int h2 = ((height+1) >>1); //not sure about +1
nengel@2 339 int data_planes=3;
nengel@2 340 int size = w * h + 2 *w2*h2;
nengel@2 341 const unsigned char* s;
nengel@2 342
nengel@2 343
nengel@2 344 for (i=0; i<data_planes; i++) {
nengel@2 345 if (i == 1) {
nengel@2 346 w = w2;
nengel@2 347 h = h2;
nengel@2 348 }
nengel@2 349 s = src->data[i];
nengel@2 350 for(j=0; j<h; j++) {
nengel@2 351 memcpy(dest, s, w);
nengel@2 352 dest += w;
nengel@2 353 s += src->linesize[i];
nengel@2 354 }
nengel@2 355 }
nengel@2 356 return size;
nengel@2 357 }
nengel@2 358
nengel@2 359 #ifdef HAVE_LIBSDL2
nengel@2 360 static SDL_Texture *get_next_texture(H264Context *h, int side){
nengel@2 361 SDLTextureQueue *sdlq = &h->sdlq;
nengel@2 362 SDL_Texture *texture;
nengel@2 363 pthread_mutex_lock (&sdlq->sdl_lock);
nengel@2 364 if (side ){ //send
nengel@2 365 while (sdlq->ready >= sdlq->size)
nengel@2 366 pthread_cond_wait(&sdlq->sdl_cond, &sdlq->sdl_lock);
nengel@2 367 texture = sdlq->queue[sdlq->fi];
nengel@2 368 sdlq->fi++; sdlq->fi %= sdlq->size;
nengel@2 369 } else { //recv
nengel@2 370 while (sdlq->ready <= 0 && !sdlq->exit)
nengel@2 371 pthread_cond_wait(&sdlq->sdl_cond, &sdlq->sdl_lock);
nengel@2 372
nengel@2 373 if (sdlq->ready == 0 && sdlq->exit){
nengel@2 374 texture = NULL;
nengel@2 375 }else{
nengel@2 376 texture = sdlq->queue[sdlq->fo];
nengel@2 377 sdlq->fo++; sdlq->fo %= sdlq->size;
nengel@2 378 }
nengel@2 379 }
nengel@2 380 pthread_mutex_unlock(&sdlq->sdl_lock);
nengel@2 381
nengel@2 382 return texture;
nengel@2 383 }
nengel@2 384
nengel@2 385 static void signal_texture(H264Context *h, int side){
nengel@2 386 SDLTextureQueue *sdlq = &h->sdlq;
nengel@2 387 pthread_mutex_lock (&sdlq->sdl_lock);
nengel@2 388 if (side)
nengel@2 389 sdlq->ready++;
nengel@2 390 else
nengel@2 391 sdlq->ready--;
nengel@2 392 pthread_cond_signal(&sdlq->sdl_cond);
nengel@2 393 pthread_mutex_unlock(&sdlq->sdl_lock);
nengel@2 394 }
nengel@2 395
nengel@2 396 void signal_sdl_exit(H264Context *h){
nengel@2 397 SDLTextureQueue *sdlq = &h->sdlq;
nengel@2 398 pthread_mutex_lock (&sdlq->sdl_lock);
nengel@2 399 sdlq->exit=1;
nengel@2 400 pthread_cond_signal(&sdlq->sdl_cond);
nengel@2 401 pthread_mutex_unlock(&sdlq->sdl_lock);
nengel@2 402 }
nengel@2 403
nengel@2 404 static void display_frame(H264Context *h, OutputContext *w, int fd, DecodedPicture *in_picture, int frame_width, int frame_height, int dropable){
nengel@2 405 static int64_t last_time = -1;
nengel@2 406 int64_t cur_time;
nengel@2 407 // SDLContext *sdlc = h->sdlc;
nengel@2 408 uint8_t *iyuv_pixels;
nengel@2 409 int pitch;
nengel@2 410
nengel@2 411
nengel@2 412 if (last_time == -1){
nengel@2 413 last_time = av_gettime();
nengel@2 414 }
nengel@2 415
nengel@2 416
nengel@2 417 /* do not display frames that are less than 8.125 ms apart (120fps)*/
nengel@2 418 if (dropable){
nengel@2 419 cur_time = av_gettime();
nengel@2 420
nengel@2 421 if ((cur_time - last_time) < 8125)
nengel@2 422 return;
nengel@2 423
nengel@2 424 last_time =cur_time;
nengel@2 425 }
nengel@2 426
nengel@2 427 if(in_picture){
nengel@2 428
nengel@2 429 SDL_Texture *texture= get_next_texture(h, 1);
nengel@2 430
nengel@2 431 SDL_LockTexture( texture, NULL, (void **)&iyuv_pixels, &pitch );
nengel@2 432
nengel@2 433 raw_encode(in_picture, frame_width, frame_height, iyuv_pixels);
nengel@2 434
nengel@2 435 signal_texture(h, 1);
nengel@2 436 }
nengel@2 437 }
nengel@2 438 #endif
nengel@2 439
nengel@2 440 // TODO: Parallelize the raw_encode (either split frame or over frames)
nengel@2 441 static void do_video_out(OutputContext *w, int fd, DecodedPicture *in_picture, int frame_width, int frame_height) {
nengel@2 442 int size=0;
nengel@2 443 //remove extra borders
nengel@2 444
nengel@2 445 if(in_picture)
nengel@2 446 size= raw_encode(in_picture, frame_width, frame_height, w->bit_buffer);
nengel@2 447
nengel@2 448 if (size < 0) {
nengel@2 449 fprintf(stderr, "Video encoding failed\n");
nengel@2 450 }else {
nengel@2 451 if (write(fd, w->bit_buffer, size)<0)
nengel@2 452 fprintf(stderr, "Write frame failed\n");
nengel@2 453 }
nengel@2 454
nengel@2 455 w->video_size += size;
nengel@2 456 }
nengel@2 457
nengel@2 458 DecodedPicture *output_frame(H264Context *h, OutputContext *oc, DecodedPicture *pic, int fd, int frame_width, int frame_height) {
nengel@2 459 DecodedPicture *out;
nengel@2 460
nengel@2 461 if (pic){
nengel@2 462 oc->delayed_pic[oc->dp_cnt++]=pic;
nengel@2 463 out = get_reordered_picture(oc, 0);
nengel@2 464 }else{
nengel@2 465 out = get_reordered_picture(oc, 1);
nengel@2 466 }
nengel@2 467
nengel@2 468 if (out){
nengel@2 469 if (fd){
nengel@2 470 do_video_out(oc, fd, out, frame_width, frame_height);
nengel@2 471 }else{
nengel@2 472 #ifdef HAVE_LIBSDL2
nengel@2 473 if (h->display){
nengel@2 474 display_frame(h, oc, fd, out, frame_width, frame_height, !(pic==NULL));
nengel@2 475 }
nengel@2 476 #endif
nengel@2 477 }
nengel@2 478 oc->frame_number++;
nengel@2 479 }
nengel@2 480
nengel@2 481 return out;
nengel@2 482 }
nengel@2 483
nengel@2 484 OutputContext *get_output_context(H264Context *h){
nengel@2 485 const int frame_width=h->frame_width;
nengel@2 486 const int frame_height=h->frame_height;
nengel@2 487 const int frame_size = frame_width*frame_height;
nengel@2 488
nengel@2 489 OutputContext *oc = av_mallocz(sizeof(OutputContext));
nengel@2 490 oc->bit_buffer_size= FFMAX(1024*256, frame_size*2); // oversize a little bit to allow extra border write
nengel@2 491 oc->bit_buffer= av_mallocz(oc->bit_buffer_size);
nengel@2 492
nengel@2 493 return oc;
nengel@2 494 }
nengel@2 495
nengel@2 496 void free_output_context(OutputContext *oc){
nengel@2 497
nengel@2 498 av_free(oc->bit_buffer);
nengel@2 499 av_free(oc);
nengel@2 500 }
nengel@2 501
nengel@2 502 SuperMBContext *getSuperMBContext(H264Context *h, int smb_width, int smb_height){
nengel@2 503 SuperMBContext *smbc = av_mallocz(sizeof(SuperMBContext));
nengel@2 504
nengel@2 505 smbc->smb_width = smb_width;
nengel@2 506 smbc->smb_height = smb_height;
nengel@2 507
nengel@2 508 smbc->nsmb_height = h->mb_height / smbc->smb_height + (h->mb_height%smbc->smb_height ? 1:0); //only need one extra if mb_height was not dividable
nengel@2 509 smbc->nsmb_width = h->mb_width / smbc->smb_width;
nengel@2 510 while ( (smbc->nsmb_width * smbc->smb_width)-(smbc->smb_height-1) < h->mb_width )
nengel@2 511 smbc->nsmb_width++;
nengel@2 512
nengel@2 513 smbc->nsmb_3dheight= smbc->nsmb_height - ((h->mb_height/2)/smbc->smb_height +1); //assuming max motion vector of half the height
nengel@2 514
nengel@2 515 smbc->smbs[0] = av_malloc (smbc->nsmb_width * smbc->nsmb_height * sizeof(SuperMBTask));
nengel@2 516 smbc->smbs[1] = av_malloc (smbc->nsmb_width * smbc->nsmb_height * sizeof(SuperMBTask));
nengel@2 517 for (int y=0, i=0; i<smbc->nsmb_height; i++, y+=smbc->smb_height){
nengel@2 518 for (int x=0, j=0; j<smbc->nsmb_width; j++, x+=smbc->smb_width){
nengel@2 519 smbc->smbs[0][i*smbc->nsmb_width +j].smb_y = y;
nengel@2 520 smbc->smbs[0][i*smbc->nsmb_width +j].smb_x = x;
nengel@2 521 smbc->smbs[1][i*smbc->nsmb_width +j].smb_y = y;
nengel@2 522 smbc->smbs[1][i*smbc->nsmb_width +j].smb_x = x;
nengel@2 523 }
nengel@2 524 }
nengel@2 525
nengel@2 526 smbc->refcount = 1;
nengel@2 527
nengel@2 528 return smbc;
nengel@2 529 }
nengel@2 530
nengel@2 531 void freeSuperMBContext(SuperMBContext *smbc){
nengel@2 532 av_free(smbc->smbs[0]);
nengel@2 533 av_free(smbc->smbs[1]);
nengel@2 534 av_free(smbc);
nengel@2 535 }
nengel@2 536
nengel@2 537 SuperMBContext * acquire_smbc(H264Context *h ){
nengel@2 538 SuperMBContext *smbc;
nengel@2 539
nengel@2 540 pthread_mutex_lock (&h->smb_lock);
nengel@2 541 smbc = h->smbc;
nengel@2 542 smbc->refcount++;
nengel@2 543 pthread_mutex_unlock(&h->smb_lock);
nengel@2 544 return smbc;
nengel@2 545 }
nengel@2 546
nengel@2 547 void release_smbc(H264Context *h, SuperMBContext *smbc){
nengel@2 548 pthread_mutex_lock (&h->smb_lock);
nengel@2 549 smbc->refcount--;
nengel@2 550 if (smbc->refcount==0){
nengel@2 551 freeSuperMBContext(smbc);
nengel@2 552 }
nengel@2 553 pthread_mutex_unlock(&h->smb_lock);
nengel@2 554
nengel@2 555 }
nengel@2 556
nengel@2 557
nengel@2 558 #ifdef HAVE_LIBSDL2
nengel@2 559
nengel@2 560 // #if OMPSS
nengel@2 561 static void draw_sb_border(H264Context *h, uint32_t *rgba_pixels, int smb_x, int smb_y){
nengel@2 562 int mb_width = h->mb_width;
nengel@2 563 int mb_height = h->mb_height;
nengel@2 564 int width = h->frame_width;
nengel@2 565 int height = h->frame_height;
nengel@2 566
nengel@2 567 int mb_x = smb_x * h->smb_width;
nengel@2 568 int mb_y = smb_y * h->smb_height;
nengel@2 569
nengel@2 570 uint32_t pix= 0x0000FFC0;
nengel@2 571
nengel@2 572 for (int k=0, i=mb_y; i< mb_y + h->smb_height; i++, k++){
nengel@2 573 for (int l=0, j=mb_x -k ; j< mb_x - k + h->smb_width; j++, l++){
nengel@2 574 //outside frame
nengel@2 575 if (i<0 || i>=mb_height || j<0 || j>=mb_width) {
nengel@2 576 continue;
nengel@2 577 }
nengel@2 578
nengel@2 579 //draw top
nengel@2 580 if (i==0 || k==0 || l==0){
nengel@2 581 int mx = j*16;
nengel@2 582 int my = i*16;
nengel@2 583 uint32_t *top = rgba_pixels + my*width + mx;
nengel@2 584 int endx = mx+16 < width? 16: width-mx;
nengel@2 585
nengel@2 586 for (int x = 0; x<endx; x++){
nengel@2 587 top[x] = pix;
nengel@2 588 }
nengel@2 589 }
nengel@2 590
nengel@2 591 //draw bottom
nengel@2 592 if (i==mb_height-1 || k==h->smb_height-1 || l==h->smb_width-1){
nengel@2 593 int mx = j*16;
nengel@2 594 int my = i*16 + 15; my = my < height ? my: height-1;
nengel@2 595 uint32_t *bottom = rgba_pixels + my*width + mx;
nengel@2 596 int endx = mx+16 < width? 16: width-mx;
nengel@2 597
nengel@2 598 for (int x = 0; x<endx; x++){
nengel@2 599 bottom[x] = pix;
nengel@2 600 }
nengel@2 601 }
nengel@2 602
nengel@2 603 //draw left
nengel@2 604 if (j==0 || l==0 ){
nengel@2 605 int mx = j*16;
nengel@2 606 int my = i*16;
nengel@2 607 uint32_t *left = rgba_pixels + my*width + mx;
nengel@2 608 int endy = my +16 < height ? 16: height - my;
nengel@2 609
nengel@2 610 for (int y = 0; y<endy; y++){
nengel@2 611 left[y*width] = pix;
nengel@2 612 }
nengel@2 613 }
nengel@2 614
nengel@2 615 //draw right
nengel@2 616 if (j==mb_width -1 || l==h->smb_width-1 ){
nengel@2 617 int mx = j*16 + 15; mx = mx < width ? mx: width-1;
nengel@2 618 int my = i*16;
nengel@2 619 uint32_t *right = rgba_pixels + my*width + mx;
nengel@2 620 int endy = my +16 < height ? 16: height - my;
nengel@2 621
nengel@2 622 for (int y = 0; y<endy; y++){
nengel@2 623 right[y*width] = pix;
nengel@2 624 }
nengel@2 625 }
nengel@2 626 }
nengel@2 627 }
nengel@2 628 }
nengel@2 629
nengel@2 630 static void draw_sbmap (H264Context *h, SuperMBContext *smbc, SDLContext *sdlc){
nengel@2 631 int pitch;
nengel@2 632 uint32_t *rgba_pixels;
nengel@2 633 SDL_Texture *sbmap= sdlc->sbmap_texture;
nengel@2 634
nengel@2 635 SDL_LockTexture( sbmap, NULL, (void **)&rgba_pixels, &pitch );
nengel@2 636
nengel@2 637 memset (rgba_pixels, 0, pitch * h->height);
nengel@2 638 for (int i=0; i< smbc->nsmb_height; i++){
nengel@2 639 for (int j=0; j< smbc->nsmb_width; j++){
nengel@2 640 draw_sb_border(h, rgba_pixels, j, i);
nengel@2 641 }
nengel@2 642 }
nengel@2 643
nengel@2 644 SDL_UnlockTexture( sbmap );
nengel@2 645 }
nengel@2 646 // #endif
nengel@2 647
nengel@2 648 // static void calc_sb_sizes (H264Context *h, SuperMBContext *smbc){
nengel@2 649 // smbc->smb_height = h->smb_height;
nengel@2 650 // smbc->smb_width = h->smb_width;
nengel@2 651 //
nengel@2 652 // smbc->nsmb_height = h->mb_height / smbc->smb_height + (h->mb_height%smbc->smb_height ? 1:0); //only need one extra if mb_height was not dividable
nengel@2 653 // smbc->nsmb_width = h->mb_width / smbc->smb_width;
nengel@2 654 // while ( (smbc->nsmb_width * smbc->smb_width)-(smbc->smb_height-1) < h->mb_width )
nengel@2 655 // smbc->nsmb_width++;
nengel@2 656 // }
nengel@2 657
nengel@2 658
nengel@2 659 static void handle_key_event(H264Context *h, SDLContext *sdlc, SDL_Keysym keysym){
nengel@2 660 int arrow=0;
nengel@2 661
nengel@2 662 switch (keysym.sym){
nengel@2 663 case SDLK_ESCAPE:
nengel@2 664 if (sdlc->fullscreen){
nengel@2 665 SDL_SetWindowFullscreen(sdlc->window, SDL_FALSE);
nengel@2 666 sdlc->fullscreen = 0;
nengel@2 667 }
nengel@2 668 break;
nengel@2 669 case SDLK_SPACE:
nengel@2 670 pthread_mutex_lock(&h->sdl_lock);
nengel@2 671 sdlc->pause = !sdlc->pause;
nengel@2 672 pthread_cond_signal(&h->sdl_cond);
nengel@2 673 pthread_mutex_unlock(&h->sdl_lock);
nengel@2 674 break;
nengel@2 675 case SDLK_f:
nengel@2 676 if (!sdlc->fullscreen){
nengel@2 677 if (keysym.mod == KMOD_LCTRL){
nengel@2 678 // SDL_SetWindowDisplayMode (sdlc->window, &sdlc->full);
nengel@2 679 SDL_SetWindowFullscreen(sdlc->window, SDL_TRUE);
nengel@2 680
nengel@2 681 sdlc->fullscreen = 1;
nengel@2 682 }
nengel@2 683 }
nengel@2 684 break;
nengel@2 685 case SDLK_m:
nengel@2 686 sdlc->showmap = !sdlc->showmap;
nengel@2 687 break;
nengel@2 688 case SDLK_UP:
nengel@2 689 if (keysym.mod == KMOD_NONE && sdlc->showmap && h->smb_height < h->mb_height && h->smb_height < h->smb_width){
nengel@2 690 h->smb_height++;
nengel@2 691 arrow =1;
nengel@2 692 }
nengel@2 693 break;
nengel@2 694 case SDLK_DOWN:
nengel@2 695 if (keysym.mod == KMOD_NONE && sdlc->showmap && h->smb_height > 1 ){
nengel@2 696 h->smb_height--;
nengel@2 697 arrow =1;
nengel@2 698 }
nengel@2 699 break;
nengel@2 700 case SDLK_LEFT:
nengel@2 701 if (keysym.mod == KMOD_NONE && sdlc->showmap && h->smb_width > 1 && h->smb_width > h->smb_height){
nengel@2 702 h->smb_width--;
nengel@2 703 arrow =1;
nengel@2 704 }
nengel@2 705 break;
nengel@2 706 case SDLK_RIGHT:
nengel@2 707 if (keysym.mod == KMOD_NONE && sdlc->showmap && h->smb_width < h->mb_width){
nengel@2 708 h->smb_width++;
nengel@2 709 arrow =1;
nengel@2 710 }
nengel@2 711 break;
nengel@2 712 }
nengel@2 713
nengel@2 714 if (arrow){
nengel@2 715 SuperMBContext *smbc = getSuperMBContext(h, h->smb_width, h->smb_height);
nengel@2 716 pthread_mutex_lock(&h->smb_lock);
nengel@2 717 h->smbc->refcount--;
nengel@2 718 if (h->smbc->refcount == 0)
nengel@2 719 freeSuperMBContext(h->smbc);
nengel@2 720 h->smbc = smbc;
nengel@2 721 sdlc->updatemap =1;
nengel@2 722 pthread_mutex_unlock(&h->smb_lock);
nengel@2 723 }
nengel@2 724 }
nengel@2 725
nengel@2 726 void handle_window_event(H264Context *h, SDLContext *sdlc, SDL_WindowEvent winevent){
nengel@2 727 SDL_Rect nrect;
nengel@2 728 switch (winevent.event){
nengel@2 729 case SDL_WINDOWEVENT_RESIZED:
nengel@2 730
nengel@2 731 sdlc->win_w = winevent.data1;
nengel@2 732 sdlc->win_h = winevent.data2;
nengel@2 733
nengel@2 734 double aspect = (double) sdlc->win_w/ sdlc->win_h;
nengel@2 735 if ( aspect < sdlc->aspect){
nengel@2 736 double r = (double) sdlc->win_w / sdlc->rect.w;
nengel@2 737 double h = (double) sdlc->rect.h * r;
nengel@2 738
nengel@2 739 nrect.y = lrint(( (double) sdlc->win_h - h)/2);
nengel@2 740 nrect.h = lrint(h);
nengel@2 741
nengel@2 742 nrect.x=0;
nengel@2 743 nrect.w= sdlc->win_w;
nengel@2 744
nengel@2 745 }else {
nengel@2 746 double r = (double) sdlc->win_h / sdlc->rect.h;
nengel@2 747 double w = (double) sdlc->rect.w * r;
nengel@2 748
nengel@2 749 nrect.x = lrint(( (double) sdlc->win_w - w)/2);
nengel@2 750 nrect.w = lrint(w);
nengel@2 751
nengel@2 752 nrect.y=0;
nengel@2 753 nrect.h= sdlc->win_h;
nengel@2 754 }
nengel@2 755 //prob better to lock
nengel@2 756 sdlc->win_rect = nrect;
nengel@2 757 sdlc->resized=1;
nengel@2 758 break;
nengel@2 759 }
nengel@2 760 }
nengel@2 761
nengel@2 762 void *sdl_event_listen_thread(void *arg){
nengel@2 763 H264Context *h = (H264Context *) arg;
nengel@2 764 SDLContext *sdlc = h->sdlc;
nengel@2 765 SDL_Event event;
nengel@2 766
nengel@2 767 while ( SDL_WaitEvent(&event) ) {
nengel@2 768 switch (event.type) {
nengel@2 769 case SDL_KEYDOWN:
nengel@2 770 handle_key_event(h, sdlc, event.key.keysym);
nengel@2 771 break;
nengel@2 772 case SDL_WINDOWEVENT:
nengel@2 773 handle_window_event(h, sdlc, event.window);
nengel@2 774 break;
nengel@2 775 case SDL_QUIT:
nengel@2 776 h->quit=1;
nengel@2 777 goto finish;
nengel@2 778 }
nengel@2 779 }
nengel@2 780 finish:
nengel@2 781 pthread_exit(NULL);
nengel@2 782 return NULL;
nengel@2 783 }
nengel@2 784
nengel@2 785 //XInitThreads not called in SDL2 library, causes crash
nengel@2 786 //remove in future when fixed ...
nengel@2 787 #include <X11/Xlib.h>
nengel@2 788
nengel@2 789 SDLContext *get_SDL_context(H264Context *h){
nengel@2 790 const int frame_width=h->frame_width;
nengel@2 791 const int frame_height=h->frame_height;
nengel@2 792
nengel@2 793 SDLContext *sdlc = av_mallocz(sizeof(SDLContext));
nengel@2 794 sdlc->display = h->display;
nengel@2 795 sdlc->fullscreen = h->fullscreen;
nengel@2 796
nengel@2 797 sdlc->aspect = (double) frame_width / (double) frame_height;
nengel@2 798 sdlc->rect.x =0;
nengel@2 799 sdlc->rect.y =0;
nengel@2 800 sdlc->rect.w =frame_width;
nengel@2 801 sdlc->rect.h =frame_height;
nengel@2 802
nengel@2 803 XInitThreads(); //workaround
nengel@2 804
nengel@2 805 // Initializes the video subsystem
nengel@2 806 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
nengel@2 807 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
nengel@2 808 #undef exit
nengel@2 809 exit(-1);
nengel@2 810 }
nengel@2 811 SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "best");
nengel@2 812 SDL_SetHint("SDL_HINT_RENDER_OPENGL_SHADERS", "1");
nengel@2 813
nengel@2 814 SDL_GetDesktopDisplayMode(0, &sdlc->full);
nengel@2 815 sdlc->full.format = SDL_PIXELFORMAT_IYUV;
nengel@2 816
nengel@2 817 sdlc->wind = sdlc->full;
nengel@2 818 if (sdlc->wind.w > frame_width) sdlc->wind.w = frame_width;
nengel@2 819 if (sdlc->wind.h > frame_height) sdlc->wind.h = frame_height;
nengel@2 820
nengel@2 821 sdlc->win_rect.x =0;
nengel@2 822 sdlc->win_rect.y =0;
nengel@2 823 sdlc->win_rect.w =sdlc->wind.w;
nengel@2 824 sdlc->win_rect.h =sdlc->wind.h;
nengel@2 825
nengel@2 826 if (sdlc->fullscreen){
nengel@2 827 sdlc->window = SDL_CreateWindow( h->file_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, sdlc->full.w, sdlc->full.h, SDL_WINDOW_FULLSCREEN|SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
nengel@2 828 SDL_SetWindowDisplayMode (sdlc->window, &sdlc->full);
nengel@2 829 } else {
nengel@2 830 sdlc->window = SDL_CreateWindow( h->file_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, sdlc->wind.w, sdlc->wind.h, SDL_WINDOW_RESIZABLE|SDL_WINDOW_SHOWN);
nengel@2 831 SDL_SetWindowDisplayMode (sdlc->window, &sdlc->wind);
nengel@2 832 }
nengel@2 833
nengel@2 834 sdlc->renderer = SDL_CreateRenderer(sdlc->window, -1, SDL_RENDERER_ACCELERATED);
nengel@2 835 // sdlc->renderer = SDL_CreateRenderer(sdlc->window, -1, SDL_RENDERER_SOFTWARE);
nengel@2 836
nengel@2 837 h->sdlq.queue[0] = SDL_CreateTexture (sdlc->renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, frame_width, frame_height);
nengel@2 838 h->sdlq.queue[1] = SDL_CreateTexture (sdlc->renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, frame_width, frame_height);
nengel@2 839
nengel@2 840 sdlc->sbmap_texture = SDL_CreateTexture (sdlc->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, frame_width, frame_height);
nengel@2 841 SDL_SetTextureBlendMode(sdlc->sbmap_texture, SDL_BLENDMODE_BLEND);
nengel@2 842 sdlc->updatemap = 1;
nengel@2 843
nengel@2 844 #if HAVE_LIBSDL_TTF
nengel@2 845 //not working with SDL 2.0, try again in future when supported
nengel@2 846 if(TTF_Init()==-1) {
nengel@2 847 printf("TTF_Init: %s\n", TTF_GetError());
nengel@2 848 exit(2);
nengel@2 849 }
nengel@2 850
nengel@2 851 // Load a font
nengel@2 852 TTF_Font *font;
nengel@2 853 font = TTF_OpenFont("/usr/share/fonts/truetype/freefont/FreeSans.ttf", 24);
nengel@2 854 if (font == NULL)
nengel@2 855 {
nengel@2 856 printf("TTF_OpenFont() Failed: %s\n", TTF_GetError());
nengel@2 857 TTF_Quit();
nengel@2 858 exit(1);
nengel@2 859 }
nengel@2 860 #endif
nengel@2 861
nengel@2 862 pthread_create(&sdlc->listen_thread, NULL, sdl_event_listen_thread, h);
nengel@2 863
nengel@2 864 return sdlc;
nengel@2 865
nengel@2 866 }
nengel@2 867
nengel@2 868 void free_SDL_context(H264Context *h){
nengel@2 869 SDLContext *sdlc = h->sdlc;
nengel@2 870 pthread_join(sdlc->listen_thread, NULL);
nengel@2 871
nengel@2 872 #if HAVE_LIBSDL_TTF
nengel@2 873 TTF_Quit();
nengel@2 874 #endif
nengel@2 875 SDL_DestroyTexture(h->sdlq.queue[0]);
nengel@2 876 SDL_DestroyTexture(h->sdlq.queue[1]);
nengel@2 877 SDL_DestroyTexture(sdlc->sbmap_texture);
nengel@2 878 SDL_DestroyRenderer(sdlc->renderer);
nengel@2 879 SDL_DestroyWindow(sdlc->window);
nengel@2 880 SDL_Quit();
nengel@2 881
nengel@2 882 }
nengel@2 883
nengel@2 884 void *sdl_thread(void *arg){
nengel@2 885 H264Context *h = (H264Context *) arg;
nengel@2 886
nengel@2 887 SDLContext *sdlc = get_SDL_context(h);
nengel@2 888 h->sdlc = sdlc;
nengel@2 889
nengel@2 890 signal_texture(h, 0);
nengel@2 891 signal_texture(h, 0);
nengel@2 892
nengel@2 893 SDL_Texture *texture;
nengel@2 894 for (;;){
nengel@2 895 pthread_mutex_lock(&h->sdl_lock);
nengel@2 896 while (sdlc->pause){
nengel@2 897 pthread_cond_wait(&h->sdl_cond, &h->sdl_lock);
nengel@2 898 }
nengel@2 899 pthread_mutex_unlock(&h->sdl_lock);
nengel@2 900
nengel@2 901 texture = get_next_texture(h, 0);
nengel@2 902 if (texture == NULL)
nengel@2 903 break;
nengel@2 904
nengel@2 905 SDL_UnlockTexture(texture);
nengel@2 906
nengel@2 907 //clear if resized
nengel@2 908 if (sdlc->resized){
nengel@2 909 // KDE bug prob, reset viewport change after resize from max
nengel@2 910 SDL_RenderSetViewport(sdlc->renderer, NULL);
nengel@2 911 SDL_SetRenderDrawColor(sdlc->renderer, 0, 0, 0, 255);
nengel@2 912 SDL_RenderClear(sdlc->renderer);
nengel@2 913 sdlc->resized = 0;
nengel@2 914 }
nengel@2 915
nengel@2 916 SDL_RenderCopy(sdlc->renderer, texture, &sdlc->rect, &sdlc->win_rect);
nengel@2 917
nengel@2 918 if (sdlc->showmap){
nengel@2 919 if (sdlc->updatemap){
nengel@2 920 SuperMBContext *smbc;
nengel@2 921 pthread_mutex_lock (&h->smb_lock);
nengel@2 922 smbc = h->smbc;
nengel@2 923 smbc->refcount++;
nengel@2 924 sdlc->updatemap=0;
nengel@2 925 pthread_mutex_unlock(&h->smb_lock);
nengel@2 926
nengel@2 927 draw_sbmap(h, smbc, sdlc);
nengel@2 928
nengel@2 929 release_smbc(h, smbc);
nengel@2 930 }
nengel@2 931 SDL_RenderCopy(sdlc->renderer, sdlc->sbmap_texture, &sdlc->rect, &sdlc->win_rect);
nengel@2 932 }
nengel@2 933
nengel@2 934 SDL_RenderPresent(sdlc->renderer);
nengel@2 935 signal_texture(h, 0);
nengel@2 936 }
nengel@2 937
nengel@2 938 free_SDL_context(h);
nengel@2 939
nengel@2 940 pthread_exit(NULL);
nengel@2 941 return NULL;
nengel@2 942 }
nengel@2 943 #endif
nengel@2 944