nengel@2: #include "config.h" nengel@2: nengel@2: #include "h264_types.h" nengel@2: #include "h264_parser.h" nengel@2: #include "h264_nal.h" nengel@2: #include "h264_entropy.h" nengel@2: #include "h264_rec.h" nengel@2: #include "h264_misc.h" nengel@2: // #undef NDEBUG nengel@2: #include nengel@2: #include nengel@2: nengel@2: #define XOANON 1 nengel@2: nengel@2: #ifdef XOANON nengel@2: static int ed_rec_affinity[40] = { 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, nengel@2: 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, nengel@2: 2, 6, 10, 14, 18, 22, 26, 30, 34, 38, nengel@2: 3, 7, 11, 15, 19, 23, 27, 31, 35, 39 }; nengel@2: static int ed_rec_smt_aff[80] = { 0, 40, 4, 44, 8, 48, 12, 52, 16, 56, 20, 60, 24, 64, 28, 68, 32, 72, 36, 76, nengel@2: 1, 41, 5, 45, 9, 49, 13, 53, 17, 57, 21, 61, 25, 65, 29, 69, 33, 73, 37, 77, nengel@2: 2, 42, 6, 46, 10, 50, 14, 54, 18, 58, 22, 62, 26, 66, 30, 70, 34, 74, 38, 78, nengel@2: 3, 43, 7, 47, 11, 51, 15, 55, 19, 59, 23, 63, 27, 67, 31, 71, 35, 75, 39, 79 }; nengel@2: #else nengel@2: static int ed_rec_affinity[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; nengel@2: static int ed_rec_smt_aff[20] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, }; nengel@2: #endif nengel@2: nengel@2: static int frames=0; nengel@2: nengel@2: static void notify_one_worker(H264Context *h){ nengel@2: pthread_mutex_lock(&h->task_lock); nengel@2: pthread_cond_signal(&h->task_cond); nengel@2: pthread_mutex_unlock(&h->task_lock); nengel@2: } nengel@2: nengel@2: static void notify_all_workers(H264Context *h){ nengel@2: pthread_mutex_lock(&h->task_lock); nengel@2: pthread_cond_broadcast(&h->task_cond); nengel@2: pthread_mutex_unlock(&h->task_lock); nengel@2: } nengel@2: nengel@2: static void push_sbe (SliceBufferQueue *sbq, SliceBufferEntry *sbe, int notify ){ nengel@2: pthread_mutex_lock(&sbq->lock); nengel@2: while (sbq->cnt >= sbq->size) nengel@2: pthread_cond_wait(&sbq->cond, &sbq->lock); nengel@2: sbq->queue[sbq->fi] = sbe; nengel@2: sbq->cnt++; nengel@2: sbq->fi++; sbq->fi %= sbq->size; nengel@2: if (notify) nengel@2: pthread_cond_signal(&sbq->cond); nengel@2: pthread_mutex_unlock(&sbq->lock); nengel@2: } nengel@2: nengel@2: static SliceBufferEntry* pop_sbe (SliceBufferQueue *sbq, int block){ nengel@2: SliceBufferEntry *sbe=NULL; nengel@2: nengel@2: pthread_mutex_lock(&sbq->lock); nengel@2: if (block){ nengel@2: while (sbq->cnt <= 0) nengel@2: pthread_cond_wait(&sbq->cond, &sbq->lock); nengel@2: }else { nengel@2: if (sbq->cnt <= 0) nengel@2: goto nonblock; nengel@2: } nengel@2: sbe = sbq->queue[sbq->fo]; nengel@2: sbq->cnt--; nengel@2: sbq->fo++; sbq->fo %= sbq->size; nengel@2: pthread_cond_signal(&sbq->cond); nengel@2: nonblock: nengel@2: pthread_mutex_unlock(&sbq->lock); nengel@2: nengel@2: return sbe; nengel@2: } nengel@2: nengel@2: // static void push_rle (RingLineQueue *rlq, SliceBufferEntry *sbe, int line, int notify){ nengel@2: // nengel@2: // //check for free slots nengel@2: // pthread_mutex_lock(&rlq->wslock); nengel@2: // while (rlq->free <= 0){ nengel@2: // pthread_cond_wait(&rlq->wscond, &rlq->wslock); nengel@2: // } nengel@2: // //free slot is available, decrement one in this lock nengel@2: // rlq->free--; nengel@2: // pthread_mutex_unlock(&rlq->wslock); nengel@2: // nengel@2: // pthread_mutex_lock(&rlq->swlock); nengel@2: // rlq->queue[rlq->fi]->sbe=sbe; nengel@2: // rlq->queue[rlq->fi]->line=line; nengel@2: // rlq->queue[rlq->fi]->mb_cnt=0; nengel@2: // rlq->fi++; rlq->fi %= rlq->size; nengel@2: // rlq->ready++; nengel@2: // if(notify) nengel@2: // pthread_cond_signal(&rlq->swcond); nengel@2: // pthread_mutex_unlock(&rlq->swlock); nengel@2: // } nengel@2: nengel@2: // static RingLineEntry* pop_rle (RingLineQueue *rlq, int block){ nengel@2: // RingLineEntry *rle=NULL; nengel@2: // nengel@2: // pthread_mutex_lock(&rlq->swlock); nengel@2: // if (block){ nengel@2: // while (rlq->ready <= 0) nengel@2: // pthread_cond_wait(&rlq->swcond, &rlq->swlock); nengel@2: // }else { nengel@2: // if (rlq->ready <= 0) nengel@2: // goto nonblock; nengel@2: // } nengel@2: // rle = rlq->queue[rlq->fo]; nengel@2: // rlq->fo++; rlq->fo %= rlq->size; nengel@2: // rlq->ready--; nengel@2: // nonblock: nengel@2: // pthread_mutex_unlock(&rlq->swlock); nengel@2: // nengel@2: // return rle; nengel@2: // } nengel@2: // nengel@2: // static void rel_rle (RingLineQueue *rlq){ nengel@2: // pthread_mutex_lock(&rlq->wslock); nengel@2: // rlq->free++; nengel@2: // pthread_cond_signal(&rlq->wscond); nengel@2: // pthread_mutex_unlock(&rlq->wslock); nengel@2: // } nengel@2: nengel@2: static RingLineEntry* pop_rle (SliceBufferQueue *sbq, RingLineQueue *rlq, int *has_token){ nengel@2: RingLineEntry *rle=NULL; nengel@2: SliceBufferEntry *sbe=NULL; nengel@2: int line=-1; nengel@2: nengel@2: pthread_mutex_lock(&sbq->lock); nengel@2: if (sbq->cnt <= 0) nengel@2: goto unlock; nengel@2: sbe = sbq->queue[sbq->fo]; nengel@2: line = sbe->lines_taken; nengel@2: nengel@2: nengel@2: pthread_mutex_lock(&rlq->swlock); nengel@2: if (!*has_token){ nengel@2: if (rlq->free <= 0) nengel@2: goto unlock2; nengel@2: rlq->free--; nengel@2: *has_token=1; nengel@2: } nengel@2: rle = rlq->queue[rlq->fo]; nengel@2: rlq->fo++; rlq->fo %= rlq->size; nengel@2: rle->sbe=sbe; nengel@2: rle->line = line; nengel@2: rle->mb_cnt =0; nengel@2: if (++sbe->lines_taken >= sbe->lines_total){ nengel@2: sbq->cnt--; nengel@2: sbq->fo++; sbq->fo %= sbq->size; nengel@2: pthread_cond_signal(&sbq->cond); nengel@2: } nengel@2: unlock2: nengel@2: pthread_mutex_unlock(&rlq->swlock); nengel@2: unlock: nengel@2: pthread_mutex_unlock(&sbq->lock); nengel@2: nengel@2: nengel@2: return rle; nengel@2: } nengel@2: nengel@2: static void rel_rle (RingLineQueue *rlq, int *rec_token){ nengel@2: pthread_mutex_lock(&rlq->swlock); nengel@2: rlq->free++; nengel@2: *rec_token=0; nengel@2: // pthread_cond_signal(&rlq->swcond); nengel@2: pthread_mutex_unlock(&rlq->swlock); nengel@2: nengel@2: } nengel@2: nengel@2: //get either a entropy or a line reconstruct task nengel@2: static void pop_next_task(H264Context *h, SliceBufferEntry **psbe, RingLineEntry **prle, int *rec_token){ nengel@2: nengel@2: pthread_mutex_lock(&h->task_lock); nengel@2: nengel@2: for(;;){ nengel@2: if ( (*psbe = pop_sbe(&h->sb_q[ENTROPY], 0)) ){ nengel@2: if (*rec_token){ nengel@2: rel_rle(&h->rl_q, rec_token); nengel@2: pthread_cond_signal(&h->task_cond); nengel@2: } nengel@2: break; nengel@2: } nengel@2: else if ( (*prle = pop_rle(&h->sb_q[MBDEC], &h->rl_q, rec_token)) ) nengel@2: break; nengel@2: pthread_cond_wait(&h->task_cond, &h->task_lock); nengel@2: } nengel@2: nengel@2: pthread_mutex_unlock(&h->task_lock); nengel@2: } nengel@2: nengel@2: void *parse_thread(void *arg){ nengel@2: H264Context *h = (H264Context *) arg; nengel@2: ParserContext *pc = get_parse_context(h->ifile); nengel@2: NalContext *nc = get_nal_context(h->width, h->height); nengel@2: H264Slice *s; nengel@2: SliceBufferEntry *sbe = NULL; nengel@2: nengel@2: while(!pc->final_frame && frames++ num_frames && !h->quit){ nengel@2: sbe = get_sb_entry(h); nengel@2: nengel@2: av_read_frame_internal(pc, &sbe->gb); nengel@2: s = &sbe->slice; nengel@2: nengel@2: decode_nal_units(nc, s, &sbe->gb); nengel@2: nengel@2: push_sbe(&h->sb_q[ENTROPY], sbe, 0); nengel@2: notify_one_worker(h); nengel@2: } nengel@2: nengel@2: if (!h->no_mbd){ nengel@2: sbe = get_sb_entry(h); nengel@2: sbe->state=-1; nengel@2: sbe->slice.coded_pic_num=nc->coded_pic_num; nengel@2: sbe->lines_total=h->threads; nengel@2: nengel@2: push_sbe(&h->sb_q[REORDER], sbe, 1); nengel@2: }else{ nengel@2: for (int i=0; ithreads; i++){ nengel@2: sbe = get_sb_entry(h); nengel@2: sbe->state=-1; nengel@2: push_sbe(&h->sb_q[ENTROPY], sbe, 1); nengel@2: notify_one_worker(h); nengel@2: } nengel@2: } nengel@2: free_nal_context(nc); nengel@2: free_parse_context(pc); nengel@2: nengel@2: pthread_exit(NULL); nengel@2: return NULL; nengel@2: } nengel@2: nengel@2: int decode_slice_entropy(EntropyContext *ec, SliceBufferEntry *sbe){ nengel@2: int i,j; nengel@2: H264Slice *s = &sbe->slice; nengel@2: GetBitContext *gb = &sbe->gb; nengel@2: CABACContext *c = &ec->c; nengel@2: H264Mb *mbs = sbe->mbs; nengel@2: nengel@2: if( !s->pps.cabac ){ nengel@2: av_log(AV_LOG_ERROR, "Only cabac encoded streams are supported\n"); nengel@2: return -1; nengel@2: } nengel@2: nengel@2: init_dequant_tables(s, ec); nengel@2: ec->curr_qscale = s->qscale; nengel@2: ec->last_qscale_diff = 0; nengel@2: ec->chroma_qp[0] = get_chroma_qp( s, 0, s->qscale); nengel@2: ec->chroma_qp[1] = get_chroma_qp( s, 1, s->qscale); nengel@2: nengel@2: /* realign */ nengel@2: align_get_bits( gb ); nengel@2: /* init cabac */ nengel@2: ff_init_cabac_decoder( c, gb->buffer + get_bits_count(gb)/8, (get_bits_left(gb) + 7)/8); nengel@2: nengel@2: ff_h264_init_cabac_states(ec, s, c); nengel@2: nengel@2: for(j=0; jmb_height; j++){ nengel@2: init_entropy_buf(ec, s, j); nengel@2: for(i=0; imb_width; i++){ nengel@2: int eos,ret; nengel@2: H264Mb *m = &mbs[i + j*ec->mb_width]; nengel@2: //memset(m, 0, sizeof(H264Mb)); nengel@2: m->mb_x=i; nengel@2: m->mb_y=j; nengel@2: ec->m = m; nengel@2: nengel@2: ret = ff_h264_decode_mb_cabac(ec, s, c); nengel@2: eos = get_cabac_terminate( c); (void) eos; nengel@2: nengel@2: if( ret < 0 || c->bytestream > c->bytestream_end + 2) { nengel@2: av_log(AV_LOG_ERROR, "error while decoding MB %d %d, bytestream (%td)\n", m->mb_x, m->mb_y, c->bytestream_end - c->bytestream); nengel@2: return -1; nengel@2: } nengel@2: } nengel@2: } nengel@2: nengel@2: return 0; nengel@2: } nengel@2: nengel@2: static int decode_slice_mb(MBRecContext *d, RingLineEntry *rle, int frames){ nengel@2: SliceBufferEntry *sbe= rle->sbe; nengel@2: H264Slice *s = &sbe->slice; nengel@2: H264Mb *mbs = sbe->mbs; nengel@2: nengel@2: int mb_width= d->mb_width; nengel@2: int i; nengel@2: const int line = rle->line; nengel@2: nengel@2: init_mbrec_context(d, d->mrs, s, line); nengel@2: nengel@2: H264Mb *m = &mbs[line*mb_width]; nengel@2: d->top=rle->prev_line->top; nengel@2: d->top_next=rle->top; nengel@2: nengel@2: // assert(rle->mb_cnt ==0); nengel@2: for(i=0; i< mb_width; i++){ nengel@2: if (frames || line>0){ nengel@2: while (rle->mb_cnt >= rle->prev_line->mb_cnt -1); nengel@2: } nengel@2: h264_decode_mb_internal( d, d->mrs, s, &m[i]); nengel@2: rle->mb_cnt++; nengel@2: } nengel@2: draw_edges(d, s, line); nengel@2: nengel@2: return 0; nengel@2: } nengel@2: nengel@2: // static int decode_slice_mb_static(MBRecContext *d, H264Slice *s, RLThreadContext *r, RLThreadContext *rp, int frames){ nengel@2: // int mb_height= d->mb_height; nengel@2: // int mb_width= d->mb_width; nengel@2: // int thread_num = r->thread_num; nengel@2: // int thread_total = r->thread_total; nengel@2: // int i; nengel@2: // int j = thread_num; nengel@2: // nengel@2: // r->mb_cnt=frames* mb_height*mb_width; nengel@2: // for(; jmbs[j*mb_width]; nengel@2: // for(i=0; i< mb_width; i++){ nengel@2: // if (j>0){ nengel@2: // while (r->mb_cnt- (thread_num? 0:mb_width) >= rp->mb_cnt-1); nengel@2: // } nengel@2: // h264_decode_mb_internal(d, s, m++); nengel@2: // r->mb_cnt++; nengel@2: // } nengel@2: // draw_edges(d, s, j); nengel@2: // } nengel@2: // return 0; nengel@2: // } nengel@2: nengel@2: static void *ed_rec_thread(void *arg){ nengel@2: H264Context *h = (H264Context*) arg; nengel@2: EntropyContext *ec=NULL; nengel@2: MBRecContext *mrc=NULL; nengel@2: nengel@2: RingLineEntry *rle=NULL; nengel@2: SliceBufferEntry *sbe=NULL; nengel@2: H264Slice *s; nengel@2: int rec_token=0; nengel@2: nengel@2: if (!h->no_mbd){ nengel@2: mrc = get_mbrec_context(h); nengel@2: } nengel@2: ec = get_entropy_context(h); nengel@2: nengel@2: for(;;){ nengel@2: pop_next_task(h, &sbe, &rle, &rec_token); nengel@2: if (sbe){ nengel@2: if (h->no_mbd && sbe->state<0){ nengel@2: break; nengel@2: } nengel@2: if (!sbe->initialized){ nengel@2: init_sb_entry(h, sbe); nengel@2: } nengel@2: decode_slice_entropy(ec, sbe); nengel@2: nengel@2: if (h->no_mbd){ nengel@2: release_sb_entry(h, sbe); nengel@2: sbe=NULL; nengel@2: } else { nengel@2: push_sbe(&h->sb_q[REORDER], sbe, 1); nengel@2: } nengel@2: } else if (rle){ nengel@2: if (rle->sbe->state<0) nengel@2: break; nengel@2: s = &rle->sbe->slice; nengel@2: nengel@2: decode_slice_mb(mrc, rle, s->coded_pic_num); nengel@2: nengel@2: if (rle->line == h->mb_height-1){ nengel@2: push_sbe(&h->sb_q[OUTPUT], rle->sbe, 1); nengel@2: } nengel@2: rle->mb_cnt++; nengel@2: } nengel@2: } nengel@2: nengel@2: //make sure threads quit in order of rle assignment nengel@2: if (!h->no_mbd){ nengel@2: while (rle->prev_line->mb_cnt <= h->mb_width); nengel@2: rel_rle(&h->rl_q, &rec_token); nengel@2: notify_one_worker(h); nengel@2: rle->mb_cnt = h->mb_width +1; nengel@2: if (rle->line == h->threads-1){ nengel@2: push_sbe(&h->sb_q[OUTPUT], rle->sbe, 1); nengel@2: } nengel@2: nengel@2: free_mbrec_context(mrc); nengel@2: } nengel@2: nengel@2: free_entropy_context(ec); nengel@2: nengel@2: pthread_exit(NULL); nengel@2: return NULL; nengel@2: } nengel@2: nengel@2: static void *reorder_thread(void *arg){ nengel@2: H264Context *h = (H264Context *) arg; nengel@2: int i; nengel@2: SliceBufferEntry *reorder[h->sb_size]; nengel@2: SliceBufferEntry *sbe, *next_sbe; nengel@2: H264Slice *s; nengel@2: int reorder_cnt=0; nengel@2: unsigned next_pic_num=0; nengel@2: nengel@2: for(;;){ nengel@2: nengel@2: sbe = pop_sbe(&h->sb_q[REORDER], 1); nengel@2: nengel@2: s = &sbe->slice; nengel@2: for(i=reorder_cnt; i>0; i--){ nengel@2: if (s->coded_pic_num < reorder[i-1]->slice.coded_pic_num) nengel@2: break; nengel@2: reorder[i]=reorder[i-1]; nengel@2: } nengel@2: reorder[i]=sbe; nengel@2: nengel@2: while(reorder_cnt>=0){ nengel@2: if (next_pic_num!=reorder[reorder_cnt]->slice.coded_pic_num){ nengel@2: break; nengel@2: } nengel@2: next_sbe = reorder[reorder_cnt]; nengel@2: H264Slice *es = &next_sbe->slice; nengel@2: nengel@2: if (next_sbe->state<0) nengel@2: goto end; nengel@2: nengel@2: for (int i=0; i<2; i++){ nengel@2: for(int j=0; j< es->ref_count[i]; j++){ nengel@2: if (es->ref_list_cpn[i][j] ==-1) nengel@2: continue; nengel@2: int k; nengel@2: for (k=0; kmax_dpb_cnt; k++){ nengel@2: if(h->dpb[k].reference >= 2 && h->dpb[k].cpn == es->ref_list_cpn[i][j]){ nengel@2: es->dp_ref_list[i][j] = &h->dpb[k]; nengel@2: break; nengel@2: } nengel@2: } nengel@2: } nengel@2: } nengel@2: next_sbe->dp = get_dpb_entry(h, es); nengel@2: nengel@2: push_sbe(&h->sb_q[MBDEC], next_sbe, 0); nengel@2: notify_all_workers(h); nengel@2: nengel@2: // for (int i=0; i< h->mb_height; i++){ nengel@2: // push_rle(&h->rl_q, next_sbe, i, 0); nengel@2: // notify_one_worker(h); nengel@2: // } nengel@2: nengel@2: nengel@2: next_pic_num++; nengel@2: reorder_cnt--; nengel@2: } nengel@2: reorder_cnt++; nengel@2: } nengel@2: nengel@2: end: nengel@2: { nengel@2: push_sbe(&h->sb_q[MBDEC], next_sbe, 0); nengel@2: notify_all_workers(h); nengel@2: if (h->no_mbd){ nengel@2: push_sbe(&h->sb_q[OUTPUT], next_sbe, 1); nengel@2: } nengel@2: // for (int i=0; i< h->threads; i++){ nengel@2: // push_rle(&h->rl_q, next_sbe, i, 0); nengel@2: // notify_one_worker(h); nengel@2: // } nengel@2: } nengel@2: nengel@2: pthread_exit(NULL); nengel@2: return NULL; nengel@2: } nengel@2: nengel@2: void create_ed_rec_threads(H264Context *h){ nengel@2: cpu_set_t cpuset; nengel@2: int* aff; nengel@2: nengel@2: if (h->setaff){ nengel@2: aff = h->smt ? ed_rec_smt_aff : ed_rec_affinity ; nengel@2: for (int i=0; ithreads; i++){ nengel@2: pthread_attr_init(&h->ed_rec_attr[i]); nengel@2: CPU_ZERO(&cpuset); nengel@2: CPU_SET(aff[i], &cpuset); nengel@2: pthread_attr_setaffinity_np(&h->ed_rec_attr[i], sizeof(cpu_set_t), &cpuset); nengel@2: pthread_create(&h->ed_rec_thr[i], &h->ed_rec_attr[i], ed_rec_thread, h); nengel@2: } nengel@2: } else { nengel@2: for (int i=0; ithreads; i++){ nengel@2: pthread_create(&h->ed_rec_thr[i], NULL, ed_rec_thread, h); nengel@2: } nengel@2: } nengel@2: } nengel@2: nengel@2: void join_ed_rec_threads(H264Context *h){ nengel@2: for (int i=0; i< h->threads; i++){ nengel@2: pthread_join(h->ed_rec_thr[i], NULL); nengel@2: } nengel@2: } nengel@2: nengel@2: void *output_thread(void *arg){ nengel@2: H264Context *h = (H264Context *) arg; nengel@2: nengel@2: OutputContext *oc = get_output_context( h ); nengel@2: nengel@2: SliceBufferEntry *sbe = NULL; nengel@2: H264Slice *s=NULL; nengel@2: for(;;) { nengel@2: DecodedPicture *out, *dp; nengel@2: sbe = pop_sbe(&h->sb_q[OUTPUT], 1); nengel@2: nengel@2: if (sbe->state <0) nengel@2: break; nengel@2: nengel@2: s = &sbe->slice; nengel@2: for (int i=0; irelease_cnt; i++){ nengel@2: for(int j=0; jmax_dpb_cnt; j++){ nengel@2: if(h->dpb[j].cpn== s->release_ref_cpn[i]){ nengel@2: release_dpb_entry(h, &h->dpb[j], 2); nengel@2: break; nengel@2: } nengel@2: } nengel@2: } nengel@2: nengel@2: dp=sbe->dp; nengel@2: release_sb_entry(h, sbe); nengel@2: nengel@2: out =output_frame(h, oc, dp, h->ofile, h->frame_width, h->frame_height); nengel@2: if (out){ nengel@2: release_dpb_entry(h, out, 1); nengel@2: } nengel@2: nengel@2: print_report(oc->frame_number, oc->video_size, 0, h->verbose); nengel@2: nengel@2: } nengel@2: /* at the end of stream, we must flush the decoder buffers */ nengel@2: while (output_frame(h, oc, NULL, h->ofile, h->frame_width, h->frame_height)); nengel@2: print_report(oc->frame_number, oc->video_size, 1, h->verbose); nengel@2: nengel@2: free_output_context(oc); nengel@2: nengel@2: pthread_exit(NULL); nengel@2: return NULL; nengel@2: } nengel@2: nengel@2: /* nengel@2: * The following code is the main loop of the file converter nengel@2: */ nengel@2: int h264_decode_pthread(H264Context *h) { nengel@2: pthread_t parse_thr, reorder_thr, output_thr; nengel@2: nengel@2: av_start_timer(); nengel@2: nengel@2: pthread_create(&parse_thr, NULL, parse_thread, h); nengel@2: if (!h->no_mbd){ nengel@2: pthread_create(&reorder_thr, NULL, reorder_thread, h); nengel@2: pthread_create(&output_thr, NULL, output_thread, h); nengel@2: } nengel@2: #if HAVE_LIBSDL2 nengel@2: pthread_t sdl_thr; nengel@2: if (h->display){ nengel@2: pthread_create(&sdl_thr, NULL, sdl_thread, h); nengel@2: } nengel@2: #endif nengel@2: create_ed_rec_threads(h); nengel@2: nengel@2: nengel@2: if (h->rl_side_touch){ nengel@2: pthread_mutex_lock(&h->ilock); nengel@2: while (h->init_threads< h->threads) nengel@2: pthread_cond_wait(&h->icond, &h->ilock); nengel@2: pthread_mutex_unlock(&h->ilock); nengel@2: nengel@2: pthread_mutex_lock(&h->tlock); nengel@2: h->touch_start =1; nengel@2: pthread_cond_broadcast(&h->tcond); nengel@2: pthread_mutex_unlock(&h->tlock); nengel@2: nengel@2: pthread_mutex_lock(&h->tdlock); nengel@2: while (h->touch_done < h->threads) nengel@2: pthread_cond_wait(&h->tdcond, &h->tdlock); nengel@2: pthread_mutex_unlock(&h->tdlock); nengel@2: nengel@2: pthread_mutex_lock(&h->slock); nengel@2: h->start =1; nengel@2: pthread_cond_broadcast(&h->scond); nengel@2: pthread_mutex_unlock(&h->slock); nengel@2: } nengel@2: join_ed_rec_threads(h); nengel@2: pthread_join(parse_thr, NULL); nengel@2: if (!h->no_mbd){ nengel@2: pthread_join(reorder_thr, NULL); nengel@2: pthread_join(output_thr, NULL); nengel@2: } nengel@2: #if HAVE_LIBSDL2 nengel@2: if (h->display) nengel@2: signal_sdl_exit(h); nengel@2: pthread_join(sdl_thr, NULL); nengel@2: #endif nengel@2: nengel@2: nengel@2: return 0; nengel@2: }