static void outbuf_flush(Iso2022Ptr is, int fd) { int rc; int i = 0; if(olog >= 0) write(olog, is->outbuf, is->outbuf_count); while(i < is->outbuf_count) { rc = write(fd, is->outbuf + i, is->outbuf_count - i); if(rc > 0) { i += rc; } else { if(rc < 0 && errno == EINTR) continue; else if((rc == 0) || ((rc < 0) && (errno == EAGAIN))) { waitForOutput(fd); continue; } else break; } } is->outbuf_count = 0; }
/** * @brief VLC filter callback * @return picture(s) containing the filtered frames */ static picture_t* y4m_filter(filter_t* intf, picture_t* srcPic) { filter_sys_t* sys = intf->p_sys; //msg_Info(intf, ">>>> filter"); if (!srcPic) { //msg_Info(intf, ">>> filter: NULL INPUT"); return NULL; } // will be stored to sys->lastDate on return mtime_t currDate = srcPic->date; // if there was a problem with the subprocess then send back // the picture unmodified, at least we won't freeze up vlc if (sys->startFailed || sys->threadExit) goto ECHO_RETURN; // start subprocess and write the y4m header // fixme: this can go in open() if fmt_in matches the srcPic if (!sys->startFailed && !sys->childPid) { sys->startFailed = !startProcess(intf); if (sys->startFailed) goto ECHO_RETURN; if (0 >= writeY4mHeader(intf, srcPic, sys->stdin)) { msg_Err(intf, "writeY4mHeader failed: errno=%d %s", errno, strerror(errno)); stopProcess(intf); sys->startFailed = true; goto ECHO_RETURN; } } // // control the buffering level by monitoring the input/output fifos // // the input/output fifos are emptied/filled by input/output threads // in response to the subprocess reading/writing frames // // if the input fifo is empty, then probably the subprocess wants // more input, so we should buffer more input // // if the output fifo is empty, and the input fifo is not empty, then // probably the subprocess is about to write out a frame and we // can wait for it to arrive. in practice, most of the time there // is no waiting needed, unless the filter is too slow to keep up. // bool inputEmpty = true; bool outputEmpty = true; picture_t* tmp = picture_fifo_Peek(sys->inputFifo); if (tmp) { picture_Release(tmp); inputEmpty = false; } tmp = picture_fifo_Peek(sys->outputFifo); if (tmp) { picture_Release(tmp); outputEmpty = false; } // copy picture to input fifo, we can't use picture_Hold or else // the decoder or vout would run out of pictures in its pool picture_t* inPic = picture_NewFromFormat(&srcPic->format); picture_Copy(inPic, srcPic); picture_fifo_Push(sys->inputFifo, inPic); // signal input thread to wake up and write some more data out vlc_mutex_lock(&sys->inputMutex); sys->bufferedIn++; vlc_cond_signal(&sys->inputCond); vlc_mutex_unlock(&sys->inputMutex); // if echo is enabled, we're done // todo: there should be a limiter on the input buffering in case // the subprocess can't keep up if (sys->echo) goto ECHO_RETURN; // keeps track of the number of buffered output pictures, assumes // an integer ratio // fixme: needs modification to support non-integer ratios // and ratios < 1 sys->bufferedOut += sys->bufferRatio; // handle buffering if (outputEmpty && inputEmpty) { // we haven't supplied enough input, raise the minimum // level of buffer to keep and return sys->minBuffered += sys->bufferRatio; msg_Info(intf, "buffer more input: buffers:%d:%d:%d", sys->bufferedIn, sys->bufferedOut, sys->minBuffered); goto NULL_RETURN; } if (outputEmpty) waitForOutput(intf); // if we don't know what the frame interval is, make it 0 which // probably causes the next frames out to drop // note: this happens at least every time y4m_flush() is called // for example when seeking if (currDate <= sys->lastDate || sys->lastDate == 0) { //msg_Err(intf, "currDate <= lastDate"); //goto ECHO_RETURN; sys->lastDate = currDate; } // reference to first and last picture we are returning picture_t* first = NULL; picture_t* last = NULL; picture_t* pic; while( (pic = picture_fifo_Pop(sys->outputFifo)) ) { // do output setup when we see the first frame out from the filter, // it could have a different frame rate, chroma, size, etc than // the frame going in if (!sys->gotFirstOutput) { sys->gotFirstOutput = true; // get the in/out frame ratio by comparing frame rates float speed = ((float)srcPic->format.i_frame_rate_base * (float)pic->format.i_frame_rate) / ((float)srcPic->format.i_frame_rate * (float)pic->format.i_frame_rate_base); if (speed < 1.0) { msg_Err(intf, "frame rate reduction isn't supported yet"); } else if (speed > 1.0) { if (ceil(speed) != speed) msg_Err(intf, "frame rate change must be integer ratio"); sys->bufferRatio = speed; // initial ratio was 1.0, need to correct the number of buffered frames // now that we know what it is sys->bufferedOut *= sys->bufferRatio; sys->minBuffered *= sys->bufferRatio; } intf->fmt_out.video.i_frame_rate = pic->format.i_frame_rate; intf->fmt_out.video.i_frame_rate_base = pic->format.i_frame_rate_base; if (intf->fmt_out.video.i_chroma != pic->format.i_chroma) msg_Err(intf, "filter changed the chroma, expect corruption"); // this can't be changed after open, crashes the GLX vout //intf->fmt_out.i_codec = pic->format.i_chroma; //intf->fmt_out.video.i_chroma = pic->format.i_chroma; msg_Info(intf, "first output: buffers=%d:%d", sys->bufferedOut, sys->minBuffered); } sys->numFrames++; sys->bufferedOut--; // it seems filter_NewPicture is required now. however, // sometimes it returns null in which case it seems like // the best thing to do is dump frames picture_t* copy = first == NULL ? srcPic : filter_NewPicture(intf); if (!copy) { picture_Release(pic); // throw away frames // vlc already prints warning for this //msg_Err(intf, "filter_NewPicture returns null"); if (sys->bufferedOut < sys->minBuffered) break; else continue; } else { picture_CopyPixels(copy, pic); picture_Release(pic); pic = copy; } // the time per output frame interval is a fraction of the input frame time int frameTime = (currDate - sys->lastDate) / sys->bufferRatio; // the pts is whatever the current pts is minus any buffering // introduced by the filter pic->date = currDate - sys->bufferedOut*frameTime; // msg_Info(intf, "frame=%d buffered=%d:%d frameTime=%d ratio:%d:1 fin=%d:%d fout=%d:%d pts=%u", // sys->numFrames, sys->bufferedOut, sys->minBuffered, // frameTime, sys->bufferRatio, // srcPic->format.i_frame_rate, srcPic->format.i_frame_rate_base, // pic->format.i_frame_rate, pic->format.i_frame_rate_base, // (unsigned int)pic->date); if (last) last->p_next = pic; else first = pic; last = pic; // if we read too many frames on this iteration, on the next // one we might not have any frames available which would be // bad as vlc would think our intent was to drop frames // // if we stop reading before the output is completely empty, // there will always be some frames for the next iteration, // assuming the filter is fast enough to keep up if (sys->bufferedOut < sys->minBuffered) break; // if there is still some input buffer left, but the fifo is // empty, wait for next frame to arrive. otherwise we can // build too much input buffering if (sys->bufferedIn > 1) waitForOutput(intf); } if (!first) { // the buffer checks should prevent from getting here, but // just in case prevent leaking the input picture picture_Release(srcPic); sys->minBuffered++; } sys->lastDate = currDate; return first; ECHO_RETURN: sys->lastDate = currDate; //msg_Info(intf, "<<<< filter: ECHO"); return srcPic; NULL_RETURN: sys->lastDate = currDate; picture_Release(srcPic); //msg_Info(intf, "<<<< filter: NULL"); return NULL; }