📄 ivtv-streams.c
字号:
break; case IVTV_ENC_STREAM_TYPE_VBI: captype = 1; subtype = 4; itv->vbi.frame = 0; itv->vbi.inserted_frame = 0; memset(itv->vbi.sliced_mpeg_size, 0, sizeof(itv->vbi.sliced_mpeg_size)); break; default: return -EINVAL; } s->subtype = subtype; s->buffers_stolen = 0; /* Clear Streamoff flags in case left from last capture */ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); if (atomic_read(&itv->capturing) == 0) { int digitizer; /* Always use frame based mode. Experiments have demonstrated that byte stream based mode results in dropped frames and corruption. Not often, but occasionally. Many thanks go to Leonard Orb who spent a lot of effort and time trying to trace the cause of the drop outs. */ /* 1 frame per DMA */ /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */ ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1); /* Stuff from Windows, we don't know what it is */ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0); /* According to the docs, this should be correct. However, this is untested. I don't dare enable this without having tested it. Only very few old cards actually have this hardware combination. ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0); */ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415); ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0); ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1); ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); /* assign placeholder */ ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X)) digitizer = 0xF1; else if (itv->card->hw_all & IVTV_HW_SAA7114) digitizer = 0xEF; else /* cx25840 */ digitizer = 0x140; ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer); /* Setup VBI */ if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) { ivtv_vbi_setup(itv); } /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */ ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400); itv->pgm_info_offset = data[0]; itv->pgm_info_num = data[1]; itv->pgm_info_write_idx = 0; itv->pgm_info_read_idx = 0; IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n", itv->pgm_info_offset, itv->pgm_info_num); /* Setup API for Stream */ cx2341x_update(itv, ivtv_api_func, NULL, p); /* mute if capturing radio */ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, 1 | (p->video_mute_yuv << 8)); } /* Vsync Setup */ if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { /* event notification (on) */ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1); ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); } if (atomic_read(&itv->capturing) == 0) { /* Clear all Pending Interrupts */ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); clear_bit(IVTV_F_I_EOS, &itv->i_flags); /* Initialize Digitizer for Capture */ itv->video_dec_func(itv, VIDIOC_STREAMOFF, 0); ivtv_msleep_timeout(300, 1); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); itv->video_dec_func(itv, VIDIOC_STREAMON, 0); } /* begin_capture */ if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype)) { IVTV_DEBUG_WARN( "Error starting capture!\n"); return -EINVAL; } /* Start Passthrough */ if (enable_passthrough) { ivtv_passthrough_mode(itv, 1); } if (s->type == IVTV_ENC_STREAM_TYPE_VBI) ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); else ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); /* you're live! sit back and await interrupts :) */ atomic_inc(&itv->capturing); return 0;}static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s){ u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv *itv = s->itv; struct cx2341x_mpeg_params *p = &itv->params; int datatype; if (s->v4l2dev == NULL) return -EINVAL; IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); /* set audio mode to left/stereo for dual/stereo mode. */ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); /* set number of internal decoder buffers */ ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0); /* prebuffering */ ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1); /* extract from user packets */ ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1); itv->vbi.dec_start = data[0]; IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n", itv->vbi.dec_start, data[1]); /* set decoder source settings */ /* Data type: 0 = mpeg from host, 1 = yuv from encoder, 2 = yuv_from_host */ switch (s->type) { case IVTV_DEC_STREAM_TYPE_YUV: datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2; IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype); break; case IVTV_DEC_STREAM_TYPE_MPG: default: datatype = 0; break; } if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype, p->width, p->height, p->audio_properties)) { IVTV_DEBUG_WARN("Couldn't initialize decoder source\n"); } return 0;}int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset){ struct ivtv *itv = s->itv; if (s->v4l2dev == NULL) return -EINVAL; if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) return 0; /* already started */ IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); /* Clear Streamoff */ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { /* Initialize Decoder */ /* Reprogram Decoder YUV Buffers for YUV */ write_reg(yuv_offset[0] >> 4, 0x82c); write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); write_reg(yuv_offset[0] >> 4, 0x834); write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24); write_reg_sync(0x00108080, 0x2898); /* Enable YUV decoder output */ write_reg_sync(0x01, IVTV_REG_VDM); } ivtv_setup_v4l2_decode_stream(s); /* set dma size to 65536 bytes */ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); /* Zero out decoder counters */ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]); writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]); writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]); writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]); writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]); writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]); writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]); writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]); /* turn on notification of dual/stereo mode change */ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); /* start playback */ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0); /* Clear the following Interrupt mask bits for decoding */ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE); IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask); /* you're live! sit back and await interrupts :) */ atomic_inc(&itv->decoding); return 0;}void ivtv_stop_all_captures(struct ivtv *itv){ int i; for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) { struct ivtv_stream *s = &itv->streams[i]; if (s->v4l2dev == NULL) continue; if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ivtv_stop_v4l2_encode_stream(s, 0); } }}int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end){ struct ivtv *itv = s->itv; DECLARE_WAITQUEUE(wait, current); int cap_type; int stopmode; if (s->v4l2dev == NULL) return -EINVAL; /* This function assumes that you are allowed to stop the capture and that we are actually capturing */ IVTV_DEBUG_INFO("Stop Capture\n"); if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) return 0; if (atomic_read(&itv->capturing) == 0) return 0; switch (s->type) { case IVTV_ENC_STREAM_TYPE_YUV: cap_type = 1; break; case IVTV_ENC_STREAM_TYPE_PCM: cap_type = 1; break; case IVTV_ENC_STREAM_TYPE_VBI: cap_type = 1; break; case IVTV_ENC_STREAM_TYPE_MPG: default: cap_type = 0; break; } /* Stop Capture Mode */ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { stopmode = 0; } else { stopmode = 1; } /* end_capture */ /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype); if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { /* only run these if we're shutting down the last cap */ unsigned long duration; unsigned long then = jiffies; add_wait_queue(&itv->eos_waitq, &wait); set_current_state(TASK_INTERRUPTIBLE); /* wait 2s for EOS interrupt */ while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && jiffies < then + msecs_to_jiffies (2000)) { schedule_timeout(msecs_to_jiffies(10)); } /* To convert jiffies to ms, we must multiply by 1000 * and divide by HZ. To avoid runtime division, we * convert this to multiplication by 1000/HZ. * Since integer division truncates, we get the best * accuracy if we do a rounding calculation of the constant. * Think of the case where HZ is 1024. */ duration = ((1000 + HZ / 2) / HZ) * (jiffies - then); if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) { IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); } else { IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); } set_current_state(TASK_RUNNING); remove_wait_queue(&itv->eos_waitq, &wait); set_bit(IVTV_F_S_STREAMOFF, &s->s_flags); } /* Handle any pending interrupts */ ivtv_msleep_timeout(100, 1); } atomic_dec(&itv->capturing); /* Clear capture and no-read bits */ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); if (s->type == IVTV_ENC_STREAM_TYPE_VBI) ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); if (atomic_read(&itv->capturing) > 0) { return 0; } /* Set the following Interrupt mask bits for capture */ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); del_timer(&itv->dma_timer); /* event notification (off) */ if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { /* type: 0 = refresh */ /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); } wake_up(&s->waitq); return 0;}int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts){ struct ivtv *itv = s->itv; if (s->v4l2dev == NULL) return -EINVAL; if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG) return -EINVAL; if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags)) return 0; IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags); /* Stop Decoder */ if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) { u32 tmp = 0; /* Wait until the decoder is no longer running */ if (pts) { ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32)); } while (1) { u32 data[CX2341X_MBOX_MAX_DATA]; ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0); if (s->q_full.buffers + s->q_dma.buffers == 0) { if (tmp == data[3]) break; tmp = data[3]; } if (ivtv_msleep_timeout(100, 1)) break; } } ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0); /* turn off notification of dual/stereo mode change */ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE); del_timer(&itv->dma_timer); clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ivtv_flush_queues(s); /* decrement decoding */ atomic_dec(&itv->decoding); set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); wake_up(&itv->event_waitq); /* wake up wait queues */ wake_up(&s->waitq); return 0;}int ivtv_passthrough_mode(struct ivtv *itv, int enable){ struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV]; struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL) return -EINVAL; IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n"); /* Prevent others from starting/stopping streams while we initiate/terminate passthrough mode */ if (enable) { if (itv->output_mode == OUT_PASSTHROUGH) { return 0; } if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH) return -EBUSY; /* Fully initialize stream, and then unflag init */ set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); /* Setup YUV Decoder */ ivtv_setup_v4l2_decode_stream(dec_stream); /* Start Decoder */ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1); atomic_inc(&itv->decoding); /* Setup capture if not already done */ if (atomic_read(&itv->capturing) == 0) { cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); } /* Start Passthrough Mode */ ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11); atomic_inc(&itv->capturing); return 0; } if (itv->output_mode != OUT_PASSTHROUGH) return 0; /* Stop Passthrough Mode */ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11); ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0); atomic_dec(&itv->capturing); atomic_dec(&itv->decoding); clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); itv->output_mode = OUT_NONE; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -