/* This function tries to claim the stream for a specific file descriptor. Ifnooneelseisusingthisstreamthenthestreamisclaimedand associatedVBIandIDXstreamsarealsoautomaticallyclaimed. Possibleerrorreturns:-EBUSYifsomeoneelsehasclaimed
the stream or 0 on success. */ int cx18_claim_stream(struct cx18_open_id *id, int type)
{ struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[type]; struct cx18_stream *s_assoc;
/* Nothing should ever try to directly claim the IDX stream */ if (type == CX18_ENC_STREAM_TYPE_IDX) {
CX18_WARN("MPEG Index stream cannot be claimed directly, but something tried.\n"); return -EINVAL;
}
if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { /* someone already claimed this stream */ if (s->id == id->open_id) { /* yes, this file descriptor did. So that's OK. */ return0;
} if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) { /* VBI is handled already internally, now also assign thefiledescriptortothisstreamforexternal
reading of the stream. */
s->id = id->open_id;
CX18_DEBUG_INFO("Start Read VBI\n"); return0;
} /* someone else is using this stream already */
CX18_DEBUG_INFO("Stream %d is busy\n", type); return -EBUSY;
}
s->id = id->open_id;
/* mark that it is used internally */
set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags); return0;
}
EXPORT_SYMBOL(cx18_claim_stream);
/* This function releases a previously claimed stream. It will take into
account associated VBI streams. */ void cx18_release_stream(struct cx18_stream *s)
{ struct cx18 *cx = s->cx; struct cx18_stream *s_assoc;
if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) { /* this stream is still in use internally */ return;
} if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name); return;
}
if (new_stereo_mode == cx->dualwatch_stereo_mode) return;
CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
cx->dualwatch_stereo_mode, new_stereo_mode); if (v4l2_ctrl_s_ctrl(cx->cxhdl.audio_mode, new_stereo_mode))
CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
}
*err = 0; while (1) { if (s->type == CX18_ENC_STREAM_TYPE_MPG) { /* Process pending program updates and VBI data */ if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
cx->dualwatch_jiffies = jiffies;
cx18_dualwatch(cx);
} if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { while ((mdl = cx18_dequeue(s_vbi,
&s_vbi->q_full))) { /* byteswap and process VBI data */
cx18_process_vbi_data(cx, mdl,
s_vbi->type);
cx18_stream_put_mdl_fw(s_vbi, mdl);
}
}
mdl = &cx->vbi.sliced_mpeg_mdl; if (mdl->readpos != mdl->bytesused) return mdl;
}
/* do we have new data? */
mdl = cx18_dequeue(s, &s->q_full); if (mdl) { if (!test_and_clear_bit(CX18_F_M_NEED_SWAP,
&mdl->m_flags)) return mdl; if (s->type == CX18_ENC_STREAM_TYPE_MPG) /* byteswap MPG data */
cx18_mdl_swap(mdl); else { /* byteswap and process VBI data */
cx18_process_vbi_data(cx, mdl, s->type);
} return mdl;
}
/* return if end of stream */ if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
CX18_DEBUG_INFO("EOS %s\n", s->name); return NULL;
}
/* return if file was opened with O_NONBLOCK */ if (non_block) {
*err = -EAGAIN; return NULL;
}
/* wait for more data to arrive */
prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added
to the waitqueue */ if (!atomic_read(&s->q_full.depth))
schedule();
finish_wait(&s->waitq, &wait); if (signal_pending(current)) { /* return if a signal was received */
CX18_DEBUG_INFO("User stopped %s\n", s->name);
*err = -EINTR; return NULL;
}
}
}
while (start + len > p) { /* Scan for a 0 to find a potential MPEG-2 start code */
q = memchr(p, 0, start + len - p); if (q == NULL) break;
p = q + 1; /* *Keeplookingifnota *MPEG-2Packheaderstartcode:0x000x000x010xba *orMPEG-2videoPESstartcode:0x000x000x010xe0
*/ if ((char *)q + 15 >= buf->buf + buf->bytesused ||
q[1] != 0 || q[2] != 1 || q[3] != ch) continue;
/* If expecting the primary video PES */ if (!cx->search_pack_header) { /* Continue if it couldn't be a PES packet */ if ((q[6] & 0xc0) != 0x80) continue; /* Check if a PTS or PTS & DTS follow */ if (((q[7] & 0xc0) == 0x80 && /* PTS only */
(q[9] & 0xf0) == 0x20) || /* PTS only */
((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */
(q[9] & 0xf0) == 0x30)) { /* DTS follows */ /* Assume we found the video PES hdr */
ch = 0xba; /* next want a Program Pack*/
cx->search_pack_header = 1;
p = q + 9; /* Skip this video PES hdr */
} continue;
}
/* We may have found a Program Pack start code */
/* Get the count of stuffing bytes & verify them */
stuffing = q[13] & 7; /* all stuffing bytes must be 0xff */ for (i = 0; i < stuffing; i++) if (q[14 + i] != 0xff) break; if (i == stuffing && /* right number of stuffing bytes*/
(q[4] & 0xc4) == 0x44 && /* marker check */
(q[12] & 3) == 3 && /* marker check */
q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */
q[15 + stuffing] == 0 &&
q[16 + stuffing] == 1) { /* We declare we actually found a Program Pack*/
cx->search_pack_header = 0; /* expect vid PES */
len = (char *)q - start;
cx18_setup_sliced_vbi_mdl(cx);
*stop = true; break;
}
}
} if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",
len, s->name); return -EFAULT;
}
buf->readpos += len; if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
buf != &cx->vbi.sliced_mpeg_buf)
cx->mpg_data_received += len; return len;
}
if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) { /* shouldn't happen */
CX18_DEBUG_WARN("Stream %s not initialized before read\n",
s->name); return -EIO;
}
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the framesshouldarriveone-by-one,somakesureweneveroutputmore
than one VBI frame at a time */ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx))
single_frame = 1;
for (;;) { struct cx18_mdl *mdl; int rc;
mdl = cx18_get_mdl(s, non_block, &rc); /* if there is no data available... */ if (mdl == NULL) { /* if we got data, then return that regardless */ if (tot_written) break; /* EOS condition */ if (rc == 0) {
clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
cx18_release_stream(s);
} /* set errno */ return rc;
}
if (s->type == CX18_ENC_STREAM_TYPE_RAD) { /* you cannot read from these stream types. */ return -EPERM;
}
/* Try to claim this stream. */ if (cx18_claim_stream(id, s->type)) return -EBUSY;
/* If capture is already in progress, then we also have to
do nothing extra. */ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
set_bit(CX18_F_S_APPL_IO, &s->s_flags); return0;
}
/* Tell the card to start capturing */ if (!cx18_start_v4l2_encode_stream(s)) { /* We're done */
set_bit(CX18_F_S_APPL_IO, &s->s_flags); /* Resume a possibly paused encoder */ if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle); return0;
}
start_failed:
CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
/* *TheassociatedVBIandIDXstreamsforinternalusearereleased *automaticallywhentheMPGstreamisreleased.Weonlyneedtostop *theassociatedstream.
*/ if (s->type == CX18_ENC_STREAM_TYPE_MPG) { /* Stop the IDX stream which is always for internal use */ if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
cx18_stop_v4l2_encode_stream(s_idx, 0);
clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
} /* Stop the VBI stream, if only running for internal use */ if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
cx18_stop_v4l2_encode_stream(s_vbi, 0);
clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
}
}
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
cx18_release_stream(s); /* Also releases associated streams */ return -EIO;
}
/* Start a capture if there is none */ if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags) &&
(req_events & (EPOLLIN | EPOLLRDNORM))) { int rc;
mutex_lock(&cx->serialize_lock);
rc = cx18_start_capture(id);
mutex_unlock(&cx->serialize_lock); if (rc) {
CX18_DEBUG_INFO("Could not start capture for %s (%d)\n",
s->name, rc); return EPOLLERR;
}
CX18_DEBUG_FILE("Encoder poll started capture\n");
}
/* add stream's waitq to the poll list */
CX18_DEBUG_HI_FILE("Encoder poll\n"); if (v4l2_event_pending(&id->fh))
res |= EPOLLPRI; else
poll_wait(filp, &s->waitq, wait);
if (atomic_read(&s->q_full.depth)) return res | EPOLLIN | EPOLLRDNORM; if (eof) return res | EPOLLHUP; return res;
}
mutex_lock(&cx->serialize_lock); /* Stop radio */ if (id->type == CX18_ENC_STREAM_TYPE_RAD &&
v4l2_fh_is_singular_file(filp)) { /* Closing radio device, return to TV mode */
cx18_mute(cx); /* Mark that the radio is no longer in use */
clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* Switch tuner to TV */
cx18_call_all(cx, video, s_std, cx->std); /* Select correct audio input (i.e. TV tuner or Line in) */
cx18_audio_set_io(cx); if (atomic_read(&cx->ana_capturing) > 0) { /* Undo video mute */
cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
(v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute) |
(v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8)));
} /* Done! Unmute and continue. */
cx18_unmute(cx);
}
if (item->type == CX18_ENC_STREAM_TYPE_RAD &&
v4l2_fh_is_singular_file(filp)) { if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { if (atomic_read(&cx->ana_capturing) > 0) { /* switching to radio while capture is
in progress is not polite */
v4l2_fh_del(&item->fh);
v4l2_fh_exit(&item->fh);
kfree(item); return -EBUSY;
}
}
/* Mark that the radio is being used. */
set_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* We have the radio */
cx18_mute(cx); /* Switch tuner to radio */
cx18_call_all(cx, tuner, s_radio); /* Select the correct audio input (i.e. radio tuner) */
cx18_audio_set_io(cx); /* Done! Unmute and continue. */
cx18_unmute(cx);
} return0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.