//roarvorbis.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2013 * * This file is part of roarclients a part of RoarAudio, * a cross-platform sound system for both, home and professional use. * See README for details. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * RoarAudio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include #include #include #include #ifdef ROAR_HAVE_LIBVORBISFILE #include #include #endif #ifdef _WIN32 #include #include #endif #define BUFSIZE 1024 void usage (void) { printf("roarvorbis [OPTIONS]... FILE\n"); printf("\nOptions:\n\n"); printf(" --server SERVER - Set server hostname\n" " --help - Show this help\n" " --vclt-out FILE - Writes VCLT file\n" ); } #ifdef ROAR_HAVE_LIBVORBISFILE int _g_cf_vorbis_vfvio_return_err (void) { return -1; } size_t cf_vorbis_vfvio_read (void *ptr, size_t size, size_t nmemb, void *datasource) { ssize_t r; r = roar_vio_read(datasource, ptr, size*nmemb); ROAR_DBG("cf_vorbis_vfvio_read(ptr=%p, size=%lu, nmemb=%lu, datasource=%p): r=%i", ptr, (unsigned long int)size, (unsigned long int)nmemb, datasource, r); if ( r == -1 ) return 0; if ( r > 0 ) errno = 0; r /= size; ROAR_DBG("cf_vorbis_vfvio_read(ptr=%p, size=%lu, nmemb=%lu, datasource=%p) = %i", ptr, (unsigned long int)size, (unsigned long int)nmemb, datasource, r); return r; } int cf_vorbis_vfvio_seek (void *datasource, ogg_int64_t offset, int whence) { return roar_vio_lseek(datasource, offset, whence); } long cf_vorbis_vfvio_tell (void *datasource) { return roar_vio_lseek(datasource, 0, SEEK_CUR); } ov_callbacks _g_cf_vorbis_vfvio = { .read_func = cf_vorbis_vfvio_read, .seek_func = cf_vorbis_vfvio_seek, .close_func = (int (*)(void * )) _g_cf_vorbis_vfvio_return_err, .tell_func = cf_vorbis_vfvio_tell }; int update_stream (struct roar_connection * con, roar_vs_t ** vss, OggVorbis_File * vf, const char * file, struct roar_audio_info * info, struct roar_vio_calls * vclt) { struct roar_stream * s; vorbis_info *vi = ov_info(vf, -1); int bits = 16; int codec = ROAR_CODEC_PCM_S_LE; char **ptr = ov_comment(vf, -1)->user_comments; char key[ROAR_META_MAX_NAMELEN], value[LIBROAR_BUFFER_MSGDATA] = {0}; int j, h = 0; struct roar_meta meta; static int need_new_stream = 1; int need_close = 0; int meta_ok; fprintf(stderr, "\n"); if ( vclt != NULL ) { roar_vio_printf(vclt, "AUDIOINFO=rate:%liHz, channels:%li\n", (long int)vi->rate, (long int)vi->channels); } if ( !need_new_stream ) { if ( info->rate != (uint16_t)vi->rate || info->channels != (uint16_t)vi->channels ) { need_close = 1; need_new_stream = 1; } } if ( need_new_stream ) { if ( need_close ) { roar_vs_sync(*vss, ROAR_VS_WAIT, NULL); roar_vs_close(*vss, ROAR_VS_FALSE, NULL); } fprintf(stderr, "Audio: %i channel, %liHz\n\n", vi->channels, vi->rate); info->rate = vi->rate; info->channels = vi->channels; info->bits = bits; info->codec = codec; *vss = roar_vs_new_from_con(con, NULL); if ( *vss == NULL ) { roar_disconnect(con); return -1; } if ( roar_vs_stream(*vss, info, ROAR_DIR_PLAY, NULL) == -1 ) { roar_vs_close(*vss, ROAR_VS_TRUE, NULL); roar_disconnect(con); return -1; } need_new_stream = 0; } s = roar_vs_stream_obj(*vss, NULL); meta.value = value; meta.key[0] = 0; meta.type = ROAR_META_TYPE_NONE; roar_stream_meta_set(con, s, ROAR_META_MODE_CLEAR, &meta); if ( strncmp(file, "http:", 5) == 0 ) meta.type = ROAR_META_TYPE_FILEURL; else meta.type = ROAR_META_TYPE_FILENAME; strncpy(value, file, LIBROAR_BUFFER_MSGDATA-1); value[LIBROAR_BUFFER_MSGDATA-1] = 0; roar_stream_meta_set(con, s, ROAR_META_MODE_SET, &meta); while(*ptr){ meta_ok = 1; for (j = 0; (*ptr)[j] != 0 && (*ptr)[j] != '='; j++) { if ( j == ROAR_META_MAX_NAMELEN ) { ROAR_ERR("update_stream(*): invalid meta data: meta data key too long"); meta_ok = 0; j = 0; break; } key[j] = (*ptr)[j]; } key[j] = 0; if ( meta_ok ) { for (j++, h = 0; (*ptr)[j] != 0 && (*ptr)[j] != '='; j++) { if ( h == LIBROAR_BUFFER_MSGDATA ) { ROAR_ERR("update_stream(*): invalid meta data: meta data value for key '%s' too long", key); meta_ok = 0; h = 0; break; } value[h++] = (*ptr)[j]; } value[h] = 0; } if ( meta_ok ) { fprintf(stderr, "Meta %-16s: %s\n", key, value); if ( vclt != NULL ) { roar_vio_printf(vclt, "%s=%s\n", key, value); } meta.type = roar_meta_inttype(key); if ( meta.type != -1 ) roar_stream_meta_set(con, s, ROAR_META_MODE_SET, &meta); } ptr++; } fprintf(stderr, "\n"); *value = 0; meta.key[0] = 0; meta.type = ROAR_META_TYPE_NONE; roar_stream_meta_set(con, s, ROAR_META_MODE_FINALIZE, &meta); if ( vclt != NULL ) { roar_vio_printf(vclt, "==\n"); } return 0; } const char * time2str(double t, char * buf, size_t len) { int h, m; if ( t < 0 ) { // strncpy(buf, "unknown", len); // return buf; *buf++ = '-'; t *= -1; } h = t / 3600; t -= h * 3600; m = t / 60; t -= m * 60; snprintf(buf, len, "%.2i:%.2i:%.2i", h, m, (int)t); return buf; } void print_time (OggVorbis_File * vf, roar_vs_t * vss, double time_total, struct roar_audio_info * info) { ssize_t pos; double time_cur; long bitrate_cur = ov_bitrate_instant(vf); float bitrate = bitrate_cur / 1000.0; char time_buf[3][10]; pos = roar_vs_position(vss, ROAR_VS_BACKEND_DEFAULT, NULL); if ( pos == -1 ) { time_cur = ov_time_tell(vf); } else { time_cur = (double)pos/(double)(info->channels*info->rate); } time2str(time_cur, time_buf[0], sizeof(time_buf[0])); if ( time_total > 0 ) { time2str(time_total-time_cur, time_buf[1], sizeof(time_buf[1])); time2str(time_total, time_buf[2], sizeof(time_buf[2])); //Time: 00:02.53 [03:26.20] of 03:28.73 (122.7 kbps) Output Buffer 43.8% fprintf(stderr, "\rTime: %s [%s] of %s (%.1f kbps) ", time_buf[0], time_buf[1], time_buf[2], bitrate); } else { fprintf(stderr, "\rTime: %s (%.1f kbps) ", time_buf[0], bitrate); } fflush(stderr); } #endif int main (int argc, char * argv[]) { #ifndef ROAR_HAVE_LIBVORBISFILE (void)argc, (void)argv; fprintf(stderr, "Error: no Vorbis support!\n"); return 1; #else struct roar_vio_calls vclt, in; struct roar_vio_defaults def; const char * file = NULL; const char * vcltfile = NULL; const char * k; int i; struct roar_connection con; roar_vs_t * vss = NULL; OggVorbis_File vf; int eof=0; int current_section = -1; int last_section = -1; struct roar_audio_info info; char pcmout[4096]; double time_total; ssize_t bits_per_sec = -1; ssize_t bits_written = -1; for (i = 1; i < argc; i++) { k = argv[i]; if ( strcmp(k, "--server") == 0 ) { roar_libroar_set_server(argv[++i]); } else if ( strcmp(k, "--vclt-out") == 0 ) { vcltfile = argv[++i]; } else if ( strcmp(k, "--help") == 0 ) { usage(); return 0; } else if ( file == NULL ) { file = k; } else { fprintf(stderr, "Error: unknown argument: %s\n", k); usage(); return 1; } } if ( file == NULL ) { ROAR_ERR("No filename given."); return 1; } if ( roar_vio_dstr_init_defaults(&def, ROAR_VIO_DEF_TYPE_NONE, O_RDONLY, 0644) == -1 ) return 1; if ( roar_vio_open_dstr(&in, file, &def, 1) == -1 ) { fprintf(stderr, "Error: can not open file: %s: %s\n", file, strerror(errno)); return 1; } if ( roar_simple_connect(&con, NULL, "roarvorbis") == -1 ) { ROAR_DBG("roar_simple_play(*): roar_simple_connect() faild!"); roar_vio_close(&in); return 1; } if ( ov_open_callbacks((void*)&in, &vf, NULL, 0, _g_cf_vorbis_vfvio) < 0 ) { // if( ov_open(in, &vf, NULL, 0) < 0 ) { fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n"); roar_disconnect(&con); roar_vio_close(&in); return 1; } time_total = ov_time_total(&vf, -1); if ( vcltfile != NULL ) { if ( roar_vio_dstr_init_defaults(&def, ROAR_VIO_DEF_TYPE_NONE, O_WRONLY|O_CREAT|O_APPEND, 0644) == -1 ) return 1; if ( roar_vio_open_dstr(&vclt, vcltfile, &def, 1) == -1 ) { fprintf(stderr, "Error: can not open file: %s: %s\n", vcltfile, strerror(errno)); roar_disconnect(&con); roar_vio_close(&in); return 1; } } // if ( update_stream(&con, &s, &out, &vf, file) == -1 ) // return 1; while (!eof) { long ret = ov_read(&vf, pcmout, sizeof(pcmout), 0, 2, 1, ¤t_section); if ( last_section != current_section ) if ( update_stream(&con, &vss, &vf, file, &info, vcltfile == NULL ? NULL : &vclt) == -1 ) { roar_vio_close(&in); if ( vcltfile != NULL ) roar_vio_close(&vclt); return 1; } bits_per_sec = roar_info2bitspersec(&info); last_section = current_section; if (ret == 0) { /* EOF */ eof = 1; } else if (ret < 0) { /* error in the stream. Not a problem, just reporting it in case we (the app) cares. In this case, we don't. */ } else { if ( roar_vs_write(vss, pcmout, ret, NULL) != (ssize_t)ret ) { fprintf(stderr, "\nError: Can not write to server.\n"); eof = 1; continue; } bits_written += ret * 8; if ( bits_written > bits_per_sec ) { bits_written = 0; print_time(&vf, vss, time_total, &info); } } } fprintf(stderr, "\n"); // end the lion of print_time(). ov_clear(&vf); // fclose(in); roar_vs_close(vss, ROAR_VS_FALSE, NULL); roar_disconnect(&con); if ( vcltfile != NULL ) roar_vio_close(&vclt); roar_vio_close(&in); return 0; #endif } //ll