static void upipe_dump_line(struct upipe *upipe, unsigned int at, uint8_t *line, int size) { char hex[16 * 2 + 16 + 1]; char *tmp = hex; if (!size) return; for (unsigned i = 0; i < 16; i++) { const char *sep = i ? (i == 8 ? " " : " "): ""; if (i < size) tmp += snprintf(tmp, hex + sizeof (hex) - tmp, "%s%02x", sep, line[i]); else tmp += snprintf(tmp, hex + sizeof (hex) - tmp, "%s ", sep); assert(tmp < hex + sizeof (hex)); } char ascii[16 + 1]; tmp = ascii; for (unsigned i = 0; i < size; i++) { tmp += snprintf(tmp, ascii + sizeof (ascii) - tmp, "%c", isprint(line[i]) ? line[i] : '.'); assert(tmp < ascii + sizeof (ascii)); } upipe_notice_va(upipe, "hexdump: %08x %s |%s|", at, hex, ascii); }
static void upipe_dveo_asi_sink_stats(struct upipe *upipe) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); int fd = upipe_dveo_asi_sink->fd; static uint64_t sum; sum += 188; //upipe_notice_va(upipe, "Wrote %"PRIu64" bytes into the card", sum); int val; if (ioctl(fd, ASI_IOC_TXGETEVENTS, &val) < 0) upipe_err_va(upipe, "ioctl TXGETEVENTS failed (%m)"); else { if (val & ASI_EVENT_TX_BUFFER) upipe_notice(upipe, "driver transmit buffer queue underrun"); if (val & ASI_EVENT_TX_FIFO) upipe_notice(upipe, "onboard transmit FIFO underrun"); if (val & ASI_EVENT_TX_DATA) { upipe_notice(upipe, "transmit data status change"); if (ioctl(fd, ASI_IOC_TXGETTXD, &val) < 0) upipe_err_va(upipe, "ioctl TXGETTXDfailed (%m)"); else upipe_notice_va(upipe, "transmitting: %d", val); } } if (ioctl(fd, ASI_IOC_TXGETBUFLEVEL, &val) < 0) upipe_err_va(upipe, "ioctl TXGETBUFLEVEL failed (%m)"); else { static int old; #define MARGIN 2 if ((val - MARGIN) > old || (val + MARGIN) < old) { float secs = (float)val * 6 * 196 * 8 / 10500000; upipe_notice_va(upipe, "buf level %d -> %.2fs", val, secs); old = val; } } if (ioctl(fd, ASI_IOC_TXGETBYTECOUNT, &val) < 0) upipe_err_va(upipe, "ioctl TXGETBYTECOUNTfailed (%m)"); else { /*static uint64_t byte_sum; byte_sum += val; upipe_notice_va(upipe, "byte count %d -> %"PRIu64, val, byte_sum);*/ } }
static void upipe_dveo_asi_sink_close(struct upipe *upipe) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); if (unlikely(upipe_dveo_asi_sink->fd != -1)) { upipe_notice_va(upipe, "closing card %i", upipe_dveo_asi_sink->card_idx); ubase_clean_fd(&upipe_dveo_asi_sink->fd); } upipe_dveo_asi_sink_set_upump(upipe, NULL); }
/** @internal @This changes the rotate interval * * @param upipe description structure of the pipe * @param rotate new rotate interval * @return false in case of error */ static bool _upipe_multicat_probe_set_rotate(struct upipe *upipe, uint64_t rotate) { struct upipe_multicat_probe *upipe_multicat_probe = upipe_multicat_probe_from_upipe(upipe); if (unlikely(rotate < 1)) { upipe_warn_va(upipe, "invalid rotate interval (%"PRIu64" < 1)", rotate); return false; } upipe_multicat_probe->rotate = rotate; upipe_notice_va(upipe, "setting rotate: %"PRIu64, rotate); return true; }
/** @internal @This interpolates the PCRs for packets without a PCR. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static void upipe_ts_pcr_interpolator_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_ts_pcr_interpolator *upipe_ts_pcr_interpolator = upipe_ts_pcr_interpolator_from_upipe(upipe); bool discontinuity = ubase_check(uref_flow_get_discontinuity(uref)); if (discontinuity) { upipe_ts_pcr_interpolator->last_pcr = 0; upipe_ts_pcr_interpolator->packets = 0; upipe_ts_pcr_interpolator->pcr_packets = 0; upipe_ts_pcr_interpolator->pcr_delta = 0; upipe_ts_pcr_interpolator->discontinuity = true; upipe_notice_va(upipe, "Clearing state"); } upipe_ts_pcr_interpolator->packets++; uint64_t pcr_prog = 0; uref_clock_get_cr_prog(uref, &pcr_prog); if (pcr_prog) { uint64_t delta = pcr_prog - upipe_ts_pcr_interpolator->last_pcr; upipe_ts_pcr_interpolator->last_pcr = pcr_prog; upipe_verbose_va(upipe, "pcr_prog %"PRId64" offset %"PRId64" stored offset %"PRIu64" bitrate %"PRId64" bps", pcr_prog, delta, upipe_ts_pcr_interpolator->pcr_delta, INT64_C(27000000) * upipe_ts_pcr_interpolator->packets * 188 * 8 / delta); if (upipe_ts_pcr_interpolator->pcr_delta) upipe_ts_pcr_interpolator->pcr_packets = upipe_ts_pcr_interpolator->packets; upipe_ts_pcr_interpolator->pcr_delta = delta; upipe_ts_pcr_interpolator->packets = 0; } else if (upipe_ts_pcr_interpolator->pcr_packets) { uint64_t offset = upipe_ts_pcr_interpolator->pcr_delta * upipe_ts_pcr_interpolator->packets / upipe_ts_pcr_interpolator->pcr_packets; uint64_t prog = upipe_ts_pcr_interpolator->last_pcr + offset; uref_clock_set_date_prog(uref, prog, UREF_DATE_CR); upipe_throw_clock_ts(upipe, uref); } if (!upipe_ts_pcr_interpolator->pcr_packets) { uref_free(uref); return; } if (upipe_ts_pcr_interpolator->discontinuity) { uref_flow_set_discontinuity(uref); upipe_ts_pcr_interpolator->discontinuity = false; } upipe_ts_pcr_interpolator_output(upipe, uref, upump_p); }
static void keyhandler(struct upipe *upipe, unsigned long key) { switch (key) { case 27: case 'q': { upipe_notice_va(upipe, "exit key pressed (%d), exiting", key); exit(0); } default: upipe_dbg_va(upipe, "key pressed (%d)", key); break; } }
/** @internal @This catches events of the glx pipe. * * @param uprobe pointer to probe * @param upipe pointer to pipe throwing the event * @param event event thrown * @param args optional event-specific parameters * @return an error code */ static int upipe_glxplayer_catch_glx(struct uprobe *uprobe, struct upipe *upipe, int event, va_list args) { switch (event) { case UPROBE_GLX_SINK_KEYPRESS: { struct upipe_glxplayer *glxplayer = container_of(uprobe, struct upipe_glxplayer, uprobe_glx_s); unsigned int signature = va_arg(args, unsigned int); assert(signature == UPIPE_GLX_SINK_SIGNATURE); unsigned long key = va_arg(args, unsigned long); switch (key) { case 27: case 'q': { upipe_notice_va(upipe, "exit key pressed (%d), exiting", key); upipe_release(glxplayer->upipe_src_xfer); upipe_mgr_release(glxplayer->src_xfer); break; } case ' ': { if (glxplayer->trickp) { if ( (glxplayer->paused = !glxplayer->paused) ) { upipe_notice(upipe, "Playback paused"); struct urational rate = { .num = 0, .den = 0 }; upipe_trickp_set_rate(glxplayer->upipe_trickp, rate); } else { upipe_notice(upipe, "Playback resumed"); struct urational rate = { .num = 1, .den = 1 }; upipe_trickp_set_rate(glxplayer->upipe_trickp, rate); } } break; } default: upipe_dbg_va(upipe, "key pressed (%d)", key); break; } return UBASE_ERR_NONE; } case UPROBE_GLX_SINK_KEYRELEASE: return UBASE_ERR_NONE; default: break; } return uprobe_throw_next(uprobe, upipe, event, args); }
/** @internal @This creates a new audioqueue * @param upipe description structure of the pipe * @param flow description structure of the flow * @return an error code */ static int upipe_osx_audioqueue_sink_set_flow_def(struct upipe *upipe, struct uref *flow) { OSStatus status; uint64_t sample_rate = 0; /* hush gcc */ uint8_t channels = 0; uint8_t sample_size = 0; struct AudioStreamBasicDescription fmt; struct upipe_osx_audioqueue_sink *osx_audioqueue = upipe_osx_audioqueue_sink_from_upipe(upipe); if (unlikely(osx_audioqueue->queue)) { upipe_osx_audioqueue_sink_remove(upipe); } /* retrieve flow format information */ uref_sound_flow_get_rate(flow, &sample_rate); uref_sound_flow_get_sample_size(flow, &sample_size); uref_sound_flow_get_channels(flow, &channels); /* build format description */ memset(&fmt, 0, sizeof(struct AudioStreamBasicDescription)); fmt.mSampleRate = sample_rate; fmt.mFormatID = kAudioFormatLinearPCM; fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; fmt.mFramesPerPacket = 1; fmt.mChannelsPerFrame = channels; fmt.mBytesPerPacket = fmt.mBytesPerFrame = sample_size * channels; fmt.mBitsPerChannel = sample_size * 8; /* create queue */ status = AudioQueueNewOutput(&fmt, upipe_osx_audioqueue_sink_cb, upipe, NULL, kCFRunLoopCommonModes, 0, &osx_audioqueue->queue); if (unlikely(status == kAudioFormatUnsupportedDataFormatError)) { upipe_warn(upipe, "unsupported data format"); return UBASE_ERR_EXTERNAL; } /* change volume */ AudioQueueSetParameter(osx_audioqueue->queue, kAudioQueueParam_Volume, osx_audioqueue->volume); /* start queue ! */ AudioQueueStart(osx_audioqueue->queue, NULL); upipe_notice_va(upipe, "audioqueue started (%uHz, %hhuch, %db)", sample_rate, channels, sample_size*8); return UBASE_ERR_NONE; }
/** @This frees a upipe. * * @param upipe description structure of the pipe */ static void upipe_dveo_asi_sink_free(struct upipe *upipe) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); if (likely(upipe_dveo_asi_sink->fd != -1)) { upipe_notice_va(upipe, "closing device %d", upipe_dveo_asi_sink->card_idx); close(upipe_dveo_asi_sink->fd); } upipe_throw_dead(upipe); upipe_dveo_asi_sink_clean_upump(upipe); upipe_dveo_asi_sink_clean_upump_mgr(upipe); upipe_dveo_asi_sink_clean_input(upipe); upipe_dveo_asi_sink_clean_urefcount(upipe); upipe_dveo_asi_sink_free_void(upipe); }
static void upipe_dump_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_dump *upipe_dump = upipe_dump_from_upipe(upipe); uref_dump(uref, upipe->uprobe); size_t total_size; ubase_assert(uref_block_size(uref, &total_size)); upipe_notice_va(upipe, "dumping ubuf %p of size %zu", uref->ubuf, total_size); if (upipe_dump->max_len != (size_t)-1 && total_size > upipe_dump->max_len) total_size = upipe_dump->max_len; unsigned int count = 0; uint8_t line[16]; int offset = 0; while (total_size) { const uint8_t *buf; int size = total_size; ubase_assert(uref_block_read(uref, offset, &size, &buf)); assert(size != 0); total_size -= size; for (unsigned i = 0; i < size; i++, count++) { line[count % 16] = buf[i]; if (!((count + 1) % 16) || (!total_size && (i + 1 == size))) upipe_dump_line(upipe, count - (count % 16), line, (count % 16) + 1); } ubase_assert(uref_block_unmap(uref, offset)); offset += size; } #define UPIPE_DUMP_SEP \ "--------------------------------------------" \ "-------------------------------------------" upipe_notice(upipe, UPIPE_DUMP_SEP); upipe_dump_output(upipe, uref, upump_p); }
/** @This frees a upipe. * * @param upipe description structure of the pipe */ static void upipe_fsink_free(struct upipe *upipe) { struct upipe_fsink *upipe_fsink = upipe_fsink_from_upipe(upipe); if (likely(upipe_fsink->fd != -1)) { if (likely(upipe_fsink->path != NULL)) upipe_notice_va(upipe, "closing file %s", upipe_fsink->path); close(upipe_fsink->fd); } upipe_throw_dead(upipe); free(upipe_fsink->path); upipe_fsink_clean_uclock(upipe); upipe_fsink_clean_upump(upipe); upipe_fsink_clean_upump_mgr(upipe); upipe_fsink_clean_input(upipe); upipe_fsink_clean_urefcount(upipe); upipe_fsink_free_void(upipe); }
/** @internal @This provides a ubuf_mgr request. * * @param upipe description structure of the pipe * @param flow_format amended flow format * @return an error code */ static int upipe_audiobar_check_ubuf_mgr(struct upipe *upipe, struct uref *flow_format) { struct upipe_audiobar *upipe_audiobar = upipe_audiobar_from_upipe(upipe); if (flow_format == NULL) return UBASE_ERR_NONE; upipe_audiobar_store_flow_def(upipe, flow_format); UBASE_RETURN(uref_pic_flow_get_hsize(flow_format, &upipe_audiobar->hsize)) UBASE_RETURN(uref_pic_flow_get_vsize(flow_format, &upipe_audiobar->vsize)) upipe_audiobar->chan_width = upipe_audiobar->hsize / upipe_audiobar->channels; upipe_audiobar->chan_width -= upipe_audiobar->chan_width % 2; if (unlikely(upipe_audiobar->chan_width < 16)) { upipe_warn_va(upipe, "channel width is too small to have a separation (%"PRIu64")", upipe_audiobar->chan_width); } upipe_audiobar->sep_width = upipe_audiobar->chan_width / 4; upipe_audiobar->sep_width -= upipe_audiobar->sep_width % 4; upipe_audiobar->pad_width = upipe_audiobar->hsize - upipe_audiobar->chan_width * upipe_audiobar->channels; upipe_notice_va(upipe, "setting up chan %"PRIu64" sep %"PRIu64" pad %"PRIu64, upipe_audiobar->chan_width, upipe_audiobar->sep_width, upipe_audiobar->pad_width); bool was_buffered = !upipe_audiobar_check_input(upipe); upipe_audiobar_output_input(upipe); upipe_audiobar_unblock_input(upipe); if (was_buffered && upipe_audiobar_check_input(upipe)) { /* All packets have been output, release again the pipe that has been * used in @ref upipe_audiobar_input. */ upipe_release(upipe); } return UBASE_ERR_NONE; }
/** @internal @This configures a new codec context * * @param upipe description structure of the pipe * @param codec avcodec description structure * @param extradata pointer to extradata buffer * @param extradata_size extradata size * @return false if the buffer couldn't be accepted */ static bool upipe_avcdec_open_codec(struct upipe *upipe, AVCodec *codec, uint8_t *extradata, int extradata_size) { AVCodecContext *context = NULL; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); assert(upipe); /* close previously opened context */ if (unlikely(upipe_avcdec->context)) { /* first send empty packet to flush retained frames */ upipe_dbg(upipe, "flushing frames in decoder"); while (upipe_avcdec_process_buf(upipe, NULL, 0, NULL)); /* now close codec and free extradata if any */ upipe_notice_va(upipe, "avcodec context (%s) closed (%d)", upipe_avcdec->context->codec->name, upipe_avcdec->counter); avcodec_close(upipe_avcdec->context); if (upipe_avcdec->context->extradata_size > 0) { free(upipe_avcdec->context->extradata); } av_free(upipe_avcdec->context); upipe_avcdec->context = NULL; upipe_avcdec_store_flow_def(upipe, NULL); } /* just closing, that's all */ if (!codec) { upipe_release(upipe); return false; } /* allocate and configure codec context */ context = avcodec_alloc_context3(codec); if (unlikely(!context)) { upipe_throw_aerror(upipe); upipe_release(upipe); return false; } context->opaque = upipe; context->extradata = extradata; context->extradata_size = extradata_size; switch (codec->type) { case AVMEDIA_TYPE_VIDEO: { if (upipe_avcdec->lowres > codec->max_lowres) { upipe_warn_va(upipe, "Unsupported lowres (%d > %hhu), setting to %hhu", upipe_avcdec->lowres, codec->max_lowres, codec->max_lowres); upipe_avcdec->lowres = codec->max_lowres; } context->get_buffer = upipe_avcdec_get_buffer; context->release_buffer = upipe_avcdec_release_buffer; context->flags |= CODEC_FLAG_EMU_EDGE; context->lowres = upipe_avcdec->lowres; context->skip_loop_filter = AVDISCARD_ALL; break; } case AVMEDIA_TYPE_AUDIO: { context->get_buffer = upipe_avcdec_get_buffer_audio; break; } default: { av_free(context); upipe_err_va(upipe, "Unsupported media type (%d)", codec->type); upipe_release(upipe); return false; break; } } /* open new context */ if (unlikely(avcodec_open2(context, codec, NULL) < 0)) { upipe_warn(upipe, "could not open codec"); av_free(context); upipe_release(upipe); return false; } upipe_avcdec->context = context; upipe_avcdec->counter = 0; upipe_notice_va(upipe, "codec %s (%s) %d opened", codec->name, codec->long_name, codec->id); upipe_release(upipe); return true; }
/** @internal @This asks to open the given file. * * @param upipe description structure of the pipe * @param path relative or absolute path of the file * @param mode mode of opening the file * @return an error code */ static int _upipe_fsink_set_path(struct upipe *upipe, const char *path, enum upipe_fsink_mode mode) { struct upipe_fsink *upipe_fsink = upipe_fsink_from_upipe(upipe); if (unlikely(upipe_fsink->fd != -1)) { if (likely(upipe_fsink->path != NULL)) upipe_notice_va(upipe, "closing file %s", upipe_fsink->path); close(upipe_fsink->fd); } free(upipe_fsink->path); upipe_fsink->path = NULL; upipe_fsink_set_upump(upipe, NULL); if (!upipe_fsink_check_input(upipe)) /* Release the pipe used in @ref upipe_fsink_input. */ upipe_release(upipe); if (unlikely(path == NULL)) return UBASE_ERR_NONE; upipe_fsink_check_upump_mgr(upipe); const char *mode_desc = NULL; /* hush gcc */ int flags; switch (mode) { case UPIPE_FSINK_NONE: flags = 0; break; case UPIPE_FSINK_APPEND: mode_desc = "append"; flags = O_CREAT; break; case UPIPE_FSINK_OVERWRITE: mode_desc = "overwrite"; flags = O_CREAT | O_TRUNC; break; case UPIPE_FSINK_CREATE: mode_desc = "create"; flags = O_CREAT | O_EXCL; break; default: upipe_err_va(upipe, "invalid mode %d", mode); return UBASE_ERR_INVALID; } upipe_fsink->fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC | flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (unlikely(upipe_fsink->fd == -1)) { upipe_err_va(upipe, "can't open file %s (%s)", path, mode_desc); return UBASE_ERR_EXTERNAL; } switch (mode) { /* O_APPEND seeks on each write, so use this instead */ case UPIPE_FSINK_APPEND: if (unlikely(lseek(upipe_fsink->fd, 0, SEEK_END) == -1)) { upipe_err_va(upipe, "can't append to file %s (%s)", path, mode_desc); close(upipe_fsink->fd); upipe_fsink->fd = -1; return UBASE_ERR_EXTERNAL; } break; default: break; } upipe_fsink->path = strdup(path); if (unlikely(upipe_fsink->path == NULL)) { close(upipe_fsink->fd); upipe_fsink->fd = -1; upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } if (!upipe_fsink_check_input(upipe)) /* Use again the pipe that we previously released. */ upipe_use(upipe); upipe_notice_va(upipe, "opening file %s in %s mode", upipe_fsink->path, mode_desc); return UBASE_ERR_NONE; }
/** @internal @This asks to open the given device. * * @param upipe description structure of the pipe * @param path relative or absolute path of the file * @return an error code */ static int upipe_dveo_asi_sink_open(struct upipe *upipe) { #define BYPASS_MODE 1 struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); char path[20], sys[50], buf[20]; memset(buf, 0, sizeof(buf)); static const char dev_fmt[] = "/dev/asitx%u"; static const char sys_fmt[] = "/sys/class/asi/asitx%u/%s"; static const char dvbm_sys_fmt[] = "/sys/class/dvbm/%u/%s"; snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "timestamps"); snprintf(buf, sizeof(buf), "%u\n", 2); if (util_write(sys, buf, sizeof(buf)) < 0) { upipe_err_va(upipe, "Couldn't set timestamp mode (%m)"); return UBASE_ERR_EXTERNAL; } snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "bufsize"); snprintf(buf, sizeof(buf), "%u\n", 6*(188+8)); /* minimum is 1024 */ if (util_write(sys, buf, sizeof(buf)) < 0) { upipe_err_va(upipe, "Couldn't set buffer size (%m)"); return UBASE_ERR_EXTERNAL; } snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "buffers"); snprintf(buf, sizeof(buf), "%u\n", 500); if (util_write(sys, buf, sizeof(buf)) < 0) { upipe_err_va(upipe, "Couldn't set # of buffers (%m)"); return UBASE_ERR_EXTERNAL; } snprintf(path, sizeof(path), dev_fmt, upipe_dveo_asi_sink->card_idx); int fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC); if (unlikely(fd < 0)) { upipe_err_va(upipe, "can't open file %s (%m)", path); return UBASE_ERR_EXTERNAL; } snprintf(sys, sizeof(sys), dvbm_sys_fmt, upipe_dveo_asi_sink->card_idx, "bypass_mode"); snprintf(buf, sizeof(buf), "%u", BYPASS_MODE); util_write(sys, buf, sizeof(buf)); /* Not all cards have this so don't fail */ unsigned int cap; if (ioctl(fd, ASI_IOC_TXGETCAP, &cap) < 0) { upipe_err_va(upipe, "can't get tx caps (%m)"); goto error; } unsigned long int bufsize; snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "bufsize"); if (util_strtoul(sys, &bufsize) < 0) { upipe_err(upipe, "Couldn't read buffer size"); goto error; } unsigned long int mode; snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "mode"); if (util_strtoul(sys, &mode) < 0) { upipe_err(upipe, "Couldn't read buffer size"); goto error; } switch (mode) { case ASI_CTL_TX_MODE_MAKE204: upipe_dbg(upipe, "Appending 0x00 bytes to make 204 bytes packets"); case ASI_CTL_TX_MODE_188: case ASI_CTL_TX_MODE_204: break; default: upipe_err_va(upipe, "Unknown TX mode %lu", mode); goto error; } unsigned long int clock_source = 0; if (cap & ASI_CAP_TX_SETCLKSRC) { snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "clock_source"); if (util_strtoul(sys, &clock_source) < 0) { upipe_err(upipe, "Couldn't read clock source"); goto error; } } switch (clock_source) { case ASI_CTL_TX_CLKSRC_ONBOARD: upipe_dbg(upipe, "Using onboard oscillator"); break; case ASI_CTL_TX_CLKSRC_EXT: upipe_dbg(upipe, "Using external reference clock"); break; case ASI_CTL_TX_CLKSRC_RX: upipe_dbg(upipe, "Using recovered receive clock"); break; default: upipe_dbg(upipe, "Unknown clock source"); break; } if (!(cap & ASI_CAP_TX_TIMESTAMPS)) { upipe_err(upipe, "Device does not support timestamps"); goto error; } upipe_dveo_asi_sink->fd = fd; upipe_notice_va(upipe, "opening file %s", path); return UBASE_ERR_NONE; error: close(fd); return UBASE_ERR_EXTERNAL; }