diff --git a/pvr_rec.c b/pvr_rec.c index 820e2936..1aa6f257 100644 --- a/pvr_rec.c +++ b/pvr_rec.c @@ -125,11 +125,11 @@ pvr_recorder_thread(void *aux) time(&now); if(now >= pvrr->pvrr_start) - pvrr->pvrr_rec_status = PVR_REC_WAIT_KEY_FRAME; - + pvrr->pvrr_rec_status = PVR_REC_WAIT_AUDIO_LOCK; break; - case PVR_REC_WAIT_KEY_FRAME: + case PVR_REC_WAIT_AUDIO_LOCK: + case PVR_REC_WAIT_VIDEO_LOCK: case PVR_REC_RUNNING: case PVR_REC_COMMERCIAL: break; @@ -240,7 +240,7 @@ pvr_generate_filename(pvr_rec_t *pvrr) } free(pvrr->pvrr_format); - pvrr->pvrr_format = strdup("asf"); + pvrr->pvrr_format = strdup("nut"); filename = utf8tofilename(name && name[0] ? name : "untitled"); deslashify(filename); @@ -413,24 +413,29 @@ typedef struct pwo_ffmpeg { AVFormatContext *fctx; struct { - int64_t source_dts; - int64_t dts; int streamid; int decoded; + enum CodecType codec_type; + int64_t next_dts; + int64_t next_pts; + int64_t last_dts; + int64_t duration; + + } pids[PWO_FFMPEG_MAXPIDS]; int64_t ref_clock; - int iframe_lock; int hdr_written; - int decode_ctd; + int audio_pids; + int video_pids; - pvrr_rec_status_t logged_status; + int prologue; + int header_written; } pwo_ffmpeg_t; - static void * pwo_init(th_subscription_t *s, pvr_rec_t *pvrr) { @@ -454,6 +459,7 @@ pwo_init(th_subscription_t *s, pvr_rec_t *pvrr) return NULL; } + pf->ref_clock = AV_NOPTS_VALUE; pf->fctx = av_alloc_format_context(); av_strlcpy(pf->fctx->title, pvrr->pvrr_title ?: "", @@ -510,24 +516,28 @@ pwo_init(th_subscription_t *s, pvr_rec_t *pvrr) st->codec->codec_id = CODEC_ID_MPEG2VIDEO; st->codec->codec_type = CODEC_TYPE_VIDEO; cname = "mpeg2 video"; + pf->video_pids++; break; case HTSTV_MPEG2AUDIO: st->codec->codec_id = CODEC_ID_MP2; st->codec->codec_type = CODEC_TYPE_AUDIO; cname = "mpeg2 audio"; + pf->audio_pids++; break; case HTSTV_AC3: st->codec->codec_id = CODEC_ID_AC3; st->codec->codec_type = CODEC_TYPE_AUDIO; cname = "ac3 audio"; + pf->audio_pids++; break; case HTSTV_H264: st->codec->codec_id = CODEC_ID_H264; st->codec->codec_type = CODEC_TYPE_VIDEO; cname = "h.264 video"; + pf->video_pids++; break; } @@ -547,10 +557,14 @@ pwo_init(th_subscription_t *s, pvr_rec_t *pvrr) } st->parser = av_parser_init(st->codec->codec_id); - + pf->pids[i].codec_type = st->codec->codec_type; pf->pids[i].streamid = pf->fctx->nb_streams; + pf->pids[i].next_dts = AV_NOPTS_VALUE; + pf->pids[i].next_pts = AV_NOPTS_VALUE; + pf->pids[i].last_dts = AV_NOPTS_VALUE; + pf->pids[i].duration = 0; + pf->fctx->nb_streams++; - pf->decode_ctd++; } pvrr->pvrr_opaque = pf; return pf; @@ -567,21 +581,18 @@ pwo_writepkt(pvr_rec_t *pvrr, th_subscription_t *s, uint32_t startcode, th_transport_t *th = s->ths_transport; uint8_t flags, hlen, x; int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE; - int r, rlen, i, g, fs; + int r, rlen, i, g, j; int lavf_index = pf->pids[pidindex].streamid; AVStream *st, *stx; AVPacket pkt; uint8_t *pbuf; - int pbuflen, data_size; + int pbuflen, data_size, duration; char txt[100]; - const char *tp; void *abuf; AVFrame pic; AVRational mpeg_tc = {1, 90000}; - int64_t pdelta; - if(lavf_index == -1) return 0; @@ -604,19 +615,12 @@ pwo_writepkt(pvr_rec_t *pvrr, th_subscription_t *s, uint32_t startcode, hlen -= 10; - pdelta = pts - dts; - } else if((flags & 0xc0) == 0x80) { if(hlen < 5) return 0; dts = pts = getpts(buf, len); hlen -= 5; - pdelta = 0; - } else { - - pdelta = 0; - } buf += hlen; @@ -624,189 +628,201 @@ pwo_writepkt(pvr_rec_t *pvrr, th_subscription_t *s, uint32_t startcode, st = pf->fctx->streams[lavf_index]; + if(dts != AV_NOPTS_VALUE && pf->ref_clock == AV_NOPTS_VALUE) + pf->ref_clock = dts; + + if(dts == AV_NOPTS_VALUE) + dts = pf->pids[pidindex].next_dts; + if(dts != AV_NOPTS_VALUE) { + dts -= pf->ref_clock; + if(dts < 0 && pf->prologue > 0) + dts = AV_NOPTS_VALUE; + else + dts &= 0x1ffffffffULL; + } if(dts != AV_NOPTS_VALUE) - pf->pids[pidindex].source_dts = av_rescale_q(dts, mpeg_tc, AV_TIME_BASE_Q); + dts = av_rescale_q(dts, mpeg_tc, AV_TIME_BASE_Q); - pdelta = av_rescale_q(pdelta, mpeg_tc, AV_TIME_BASE_Q); - while(len > 0) { + if(pts == AV_NOPTS_VALUE) + pts = pf->pids[pidindex].next_pts; + if(pts != AV_NOPTS_VALUE) { + pts -= pf->ref_clock; + if(pts < 0 && pf->prologue > 0) + pts = AV_NOPTS_VALUE; + else + pts &= 0x1ffffffffULL; + } + if(pts != AV_NOPTS_VALUE) + pts = av_rescale_q(pts, mpeg_tc, AV_TIME_BASE_Q); + + + while(1) { rlen = av_parser_parse(st->parser, st->codec, &pbuf, &pbuflen, buf, len, pts, dts); if(pbuflen == 0) return 0; - if(pf->pids[pidindex].decoded == 0) { + switch(pvrr->pvrr_rec_status) { + default: + break; + + + case PVR_REC_WAIT_AUDIO_LOCK: + if(st->codec->codec_type != CODEC_TYPE_AUDIO || + pf->pids[pidindex].decoded) + break; + + abuf = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); + r = avcodec_decode_audio(st->codec, abuf, &data_size, pbuf, pbuflen); + free(abuf); + + if(r != 0 && data_size) { + syslog(LOG_DEBUG, "pvr: \"%s\" - " + "Stream #%d: Decoded a complete audio frame: " + "%d channels in %d Hz\n", + pvrr->pvrr_printname, lavf_index, + st->codec->channels, + st->codec->sample_rate); + + pf->pids[pidindex].decoded = 1; + } + + j = 0; + for(i = 0; i < PWO_FFMPEG_MAXPIDS; i++) { + if(pf->pids[i].codec_type == CODEC_TYPE_AUDIO && pf->pids[i].decoded) + j++; + } + + if(j == pf->audio_pids) + pvrr_set_rec_state(pvrr, PVR_REC_WAIT_VIDEO_LOCK); + break; + + case PVR_REC_WAIT_VIDEO_LOCK: + if(st->codec->codec_type != CODEC_TYPE_VIDEO || + pf->pids[pidindex].decoded) + break; + + r = avcodec_decode_video(st->codec, &pic, &data_size, pbuf, pbuflen); + if(r != 0 && data_size) { + syslog(LOG_DEBUG, "pvr: \"%s\" - " + "Stream #%d: Decoded a complete video frame: " + "%d x %d in %.2fHz\n", + pvrr->pvrr_printname, lavf_index, + st->codec->width, st->codec->height, + (float)st->codec->time_base.den / + (float)st->codec->time_base.num); + + pf->pids[pidindex].decoded = 1; + } + + j = 0; + for(i = 0; i < PWO_FFMPEG_MAXPIDS; i++) { + if(pf->pids[i].codec_type == CODEC_TYPE_VIDEO && pf->pids[i].decoded) + j++; + } + + if(j != pf->audio_pids) + break; + + pvrr_set_rec_state(pvrr, PVR_REC_RUNNING); + + if(!pf->header_written) { + pf->header_written = 1; + + if(av_write_header(pf->fctx)) + return 0; + + syslog(LOG_DEBUG, + "pvr: \"%s\" - Header written to file, stream dump:", + pvrr->pvrr_printname); + + for(i = 0; i < pf->fctx->nb_streams; i++) { + stx = pf->fctx->streams[i]; + g = ff_gcd(stx->time_base.num, stx->time_base.den); + + avcodec_string(txt, sizeof(txt), stx->codec, 1); + + syslog(LOG_DEBUG, "pvr: \"%s\" - Stream #%d: %s [%d/%d]", + pvrr->pvrr_printname, i, txt, + stx->time_base.num, stx->time_base.den); + + } + } + /* FALLTHRU */ + + case PVR_REC_RUNNING: + + if(th != NULL && th->tht_tt_commercial_advice == COMMERCIAL_YES) { + pvrr_set_rec_state(pvrr, PVR_REC_COMMERCIAL); + break; + } + + if(dts == AV_NOPTS_VALUE || pts == AV_NOPTS_VALUE) + break; + switch(st->codec->codec_type) { default: + duration = 0; break; case CODEC_TYPE_VIDEO: - r = avcodec_decode_video(st->codec, &pic, &data_size, buf, len); - if(r != 0 && data_size) { - syslog(LOG_DEBUG, "pvr: \"%s\" - " - "Stream #%d: Decoded a complete video frame: " - "%d x %d in %.2fHz\n", - pvrr->pvrr_printname, lavf_index, - st->codec->width, st->codec->height, - (float)st->codec->time_base.den / - (float)st->codec->time_base.num); - - pf->pids[pidindex].decoded = 1; - pf->decode_ctd--; - } + duration = 1000000 * + st->codec->time_base.num / st->codec->time_base.den; break; - + case CODEC_TYPE_AUDIO: - abuf = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); - - r = avcodec_decode_audio(st->codec, abuf, &data_size, buf, len); - - free(abuf); - - if(r != 0 && data_size) { - syslog(LOG_DEBUG, "pvr: \"%s\" - " - "Stream #%d: Decoded a complete audio frame: " - "%d channels in %d Hz\n", - pvrr->pvrr_printname, lavf_index, - st->codec->channels, - st->codec->sample_rate); - pf->pids[pidindex].decoded = 1; - pf->decode_ctd--; - } - break; - } - } - - if(pf->decode_ctd > 0) - goto next; - - if(pf->hdr_written == 0) { - if(av_write_header(pf->fctx)) - return 0; - pf->hdr_written = 1; - syslog(LOG_DEBUG, "pvr: \"%s\" - Header written to file, stream dump:", - pvrr->pvrr_printname); - - for(i = 1; i < PWO_FFMPEG_MAXPIDS; i++) - pf->pids[i].dts = pf->pids[i].source_dts - pf->pids[0].source_dts; - - pf->pids[0].dts = 0; - - for(i = 0; i < pf->fctx->nb_streams; i++) { - stx = pf->fctx->streams[i]; - g = ff_gcd(stx->time_base.num, stx->time_base.den); - - avcodec_string(txt, sizeof(txt), stx->codec, 1); - - syslog(LOG_DEBUG, "pvr: \"%s\" - Stream #%d: %s [%d/%d]", - pvrr->pvrr_printname, i, txt, - stx->time_base.num/g, stx->time_base.den/g); - - } - } - - if(pf->logged_status != pvrr->pvrr_rec_status) { - pf->logged_status = pvrr->pvrr_rec_status; - - switch(pf->logged_status) { - case PVR_REC_WAIT_SUBSCRIPTION: - tp = "wait for start"; - break; - case PVR_REC_WAIT_KEY_FRAME: - tp = "waiting for key frame"; - break; - case PVR_REC_RUNNING: - tp = "running"; - break; - case PVR_REC_COMMERCIAL: - tp = "commercial break"; - break; - default: - tp = NULL; - break; + duration = 1000000 * st->codec->frame_size / st->codec->sample_rate; } - if(tp != NULL) { - syslog(LOG_INFO, "pvr: \"%s\" - Recorder entering state \"%s\"", - pvrr->pvrr_printname, tp); + av_init_packet(&pkt); + pkt.stream_index = lavf_index; + + pkt.dts = av_rescale_q(dts, AV_TIME_BASE_Q, st->time_base); + pkt.pts = av_rescale_q(pts, AV_TIME_BASE_Q, st->time_base); + + if(pkt.dts <= pf->pids[pidindex].last_dts) { + if(pkt.pts == pkt.dts) + pkt.pts = pf->pids[pidindex].last_dts + 1; + pkt.dts = pf->pids[pidindex].last_dts + 1; } - } - - switch(pvrr->pvrr_status) { - case PVR_REC_WAIT_FOR_START: - return 0; + + pf->pids[pidindex].last_dts = pkt.dts; + + pkt.data = pbuf; + pkt.size = pbuflen; + pkt.duration = duration; + + if(st->codec->codec_type == CODEC_TYPE_AUDIO || + st->parser->pict_type == FF_I_TYPE) + pkt.flags |= PKT_FLAG_KEY; + r = av_interleaved_write_frame(pf->fctx, &pkt); + + dts += duration; + pts += duration; + pf->pids[pidindex].next_dts = dts; + pf->pids[pidindex].next_pts = pts; + break; + case PVR_REC_COMMERCIAL: if(th == NULL || th->tht_tt_commercial_advice != COMMERCIAL_YES) { - pvrr->pvrr_status = PVR_REC_WAIT_KEY_FRAME; - } else { - return 0; - } + + for(i = 0; i < PWO_FFMPEG_MAXPIDS; i++) + pf->pids[i].decoded = 0; - /* FALLTHRU */ - - case PVR_REC_WAIT_KEY_FRAME: - /* this check is not enough .. we need to scan for GOP start */ - if(st->codec->codec_type == CODEC_TYPE_VIDEO && - st->parser->pict_type == FF_I_TYPE) { - pvrr->pvrr_rec_status = PVR_REC_RUNNING; - } else { - return 0; + pvrr_set_rec_state(pvrr, PVR_REC_WAIT_AUDIO_LOCK); } break; - - case PVR_REC_RUNNING: - if(th != NULL && th->tht_tt_commercial_advice == COMMERCIAL_YES) { - pvrr->pvrr_status = PVR_REC_COMMERCIAL; - return 0; - } - - default: - break; } - - av_init_packet(&pkt); - pkt.stream_index = lavf_index; - - if(pf->pids[pidindex].dts >= 0) { - - pkt.dts = av_rescale_q(pf->pids[pidindex].dts, - AV_TIME_BASE_Q, st->time_base); - - pkt.pts = av_rescale_q(pf->pids[pidindex].dts + pdelta, - AV_TIME_BASE_Q, st->time_base); - pkt.data = pbuf; - pkt.size = pbuflen; - - if(st->codec->codec_type == CODEC_TYPE_VIDEO && - st->parser->pict_type == FF_I_TYPE) - pkt.flags |= PKT_FLAG_KEY; - - r = av_interleaved_write_frame(pf->fctx, &pkt); - } - - switch(st->codec->codec_type) { - case CODEC_TYPE_VIDEO: - pf->pids[pidindex].dts += 1000000 * - st->codec->time_base.num / st->codec->time_base.den; - break; - - case CODEC_TYPE_AUDIO: - fs = st->codec->frame_size; - pf->pids[pidindex].dts += 1000000 * fs / st->codec->sample_rate; - break; - - default: - break; - } - - next: buf += rlen; len -= rlen; } return 0; } + + static int pwo_end(pvr_rec_t *pvrr) { diff --git a/tvhead.h b/tvhead.h index a2b8e9f0..1472834e 100644 --- a/tvhead.h +++ b/tvhead.h @@ -493,7 +493,8 @@ typedef enum { PVR_REC_STOP, PVR_REC_WAIT_SUBSCRIPTION, PVR_REC_WAIT_FOR_START, - PVR_REC_WAIT_KEY_FRAME, + PVR_REC_WAIT_AUDIO_LOCK, + PVR_REC_WAIT_VIDEO_LOCK, PVR_REC_RUNNING, PVR_REC_COMMERCIAL,