int main(int argc, char * argv[]) { /* parse arguments */ char *exe = argv[0]; char *filename; struct GrooveFile *file; int i; char *arg; char *key; char *value; struct GrooveTag *tag; if (argc < 2) return usage(exe); printf("Using libgroove v%s\n", groove_version()); filename = argv[1]; groove_init(); atexit(groove_finish); groove_set_logging(GROOVE_LOG_INFO); file = groove_file_open(filename); if (!file) { fprintf(stderr, "error opening file\n"); return 1; } for (i = 2; i < argc; i += 1) { arg = argv[i]; if (strcmp("--update", arg) == 0) { if (i + 2 >= argc) { groove_file_close(file); fprintf(stderr, "--update requires 2 arguments"); return usage(exe); } key = argv[++i]; value = argv[++i]; groove_file_metadata_set(file, key, value, 0); } else if (strcmp("--delete", arg) == 0) { if (i + 1 >= argc) { groove_file_close(file); fprintf(stderr, "--delete requires 1 argument"); return usage(exe); } key = argv[++i]; groove_file_metadata_set(file, key, NULL, 0); } else { groove_file_close(file); return usage(exe); } } tag = NULL; printf("duration=%f\n", groove_file_duration(file)); while ((tag = groove_file_metadata_get(file, "", tag, 0))) printf("%s=%s\n", groove_tag_key(tag), groove_tag_value(tag)); if (file->dirty && groove_file_save(file) < 0) fprintf(stderr, "error saving file\n"); groove_file_close(file); return 0; }
int groove_file_open(struct GrooveFile *file, const char *filename, const char *filename_hint) { struct GrooveFilePrivate *f = (struct GrooveFilePrivate *) file; f->stdfile = fopen(filename, "rb"); if (!f->stdfile) { int err = errno; assert(err != EINVAL); groove_file_close(file); if (err == ENOMEM) { return GrooveErrorNoMem; } else if (err == ENOENT) { return GrooveErrorFileNotFound; } else if (err == EPERM) { return GrooveErrorPermissions; } else { return GrooveErrorFileSystem; } } f->prealloc_custom_io.userdata = f; f->prealloc_custom_io.read_packet = file_read_packet; f->prealloc_custom_io.write_packet = file_write_packet; f->prealloc_custom_io.seek = file_seek; return groove_file_open_custom(file, &f->prealloc_custom_io, filename_hint); }
void groove_file_destroy(struct GrooveFile *file) { struct GrooveFilePrivate *f = (struct GrooveFilePrivate *)file; if (!file) return; groove_file_close(file); deallocate(f); }
int main(int argc, char * argv[]) { // parse arguments const char *exe = argv[0]; if (argc < 2) return usage(exe); groove_init(); atexit(groove_finish); groove_set_logging(GROOVE_LOG_INFO); struct GroovePlaylist *playlist = groove_playlist_create(); if (!playlist) { fprintf(stderr, "Error creating playlist.\n"); return 1; } struct GroovePlayer *player = groove_player_create(); for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; if (arg[0] == '-' && arg[1] == '-') { arg += 2; if (strcmp(arg, "dummy") == 0) { player->device_index = GROOVE_PLAYER_DUMMY_DEVICE; } else if (i + 1 >= argc) { return usage(exe); } else if (strcmp(arg, "volume") == 0) { double volume = atof(argv[++i]); groove_playlist_set_gain(playlist, volume); } else { return usage(exe); } } else { struct GrooveFile * file = groove_file_open(arg); if (!file) { fprintf(stderr, "Not queuing %s\n", arg); continue; } groove_playlist_insert(playlist, file, 1.0, 1.0, NULL); } } groove_player_attach(player, playlist); union GroovePlayerEvent event; struct GroovePlaylistItem *item; while (groove_player_event_get(player, &event, 1) >= 0) { switch (event.type) { case GROOVE_EVENT_BUFFERUNDERRUN: fprintf(stderr, "buffer underrun\n"); break; case GROOVE_EVENT_NOWPLAYING: groove_player_position(player, &item, NULL); if (!item) { printf("done\n"); item = playlist->head; while (item) { struct GrooveFile *file = item->file; struct GroovePlaylistItem *next = item->next; groove_playlist_remove(playlist, item); groove_file_close(file); item = next; } groove_player_detach(player); groove_player_destroy(player); groove_playlist_destroy(playlist); return 0; } struct GrooveTag *artist_tag = groove_file_metadata_get(item->file, "artist", NULL, 0); struct GrooveTag *title_tag = groove_file_metadata_get(item->file, "title", NULL, 0); if (artist_tag && title_tag) { printf("Now playing: %s - %s\n", groove_tag_value(artist_tag), groove_tag_value(title_tag)); } else { printf("Now playing: %s\n", item->file->filename); } break; } } return 1; }
int groove_file_open_custom(struct GrooveFile *file, struct GrooveCustomIo *custom_io, const char *filename_hint) { struct GrooveFilePrivate *f = (struct GrooveFilePrivate *) file; f->custom_io = custom_io; if (pthread_mutex_init(&f->seek_mutex, NULL)) { groove_file_close(file); return GrooveErrorSystemResources; } f->ic = avformat_alloc_context(); if (!f->ic) { groove_file_close(file); return GrooveErrorNoMem; } file->filename = f->ic->filename; f->ic->interrupt_callback.callback = decode_interrupt_cb; f->ic->interrupt_callback.opaque = f; const int buffer_size = 8 * 1024; f->avio_buf = allocate_nonzero<unsigned char>(buffer_size); if (!f->avio_buf) { groove_file_close(file); return GrooveErrorNoMem; } f->avio = avio_alloc_context(f->avio_buf, buffer_size, 0, f, avio_read_packet, avio_write_packet, avio_seek); if (!f->avio) { groove_file_close(file); return GrooveErrorNoMem; } f->avio->seekable = AVIO_SEEKABLE_NORMAL; f->avio->direct = AVIO_FLAG_DIRECT; f->ic->pb = f->avio; int err = avformat_open_input(&f->ic, filename_hint, NULL, NULL); if (err < 0) { assert(err != AVERROR(EINVAL)); groove_file_close(file); if (err == AVERROR(ENOMEM)) { return GrooveErrorNoMem; } else if (err == AVERROR(ENOENT)) { return GrooveErrorFileNotFound; } else if (err == AVERROR(EPERM)) { return GrooveErrorPermissions; } else { return GrooveErrorUnknownFormat; } } err = avformat_find_stream_info(f->ic, NULL); if (err < 0) { groove_file_close(file); return GrooveErrorStreamNotFound; } // set all streams to discard. in a few lines here we will find the audio // stream and cancel discarding it if (f->ic->nb_streams > INT_MAX) { groove_file_close(file); return GrooveErrorTooManyStreams; } int stream_count = (int)f->ic->nb_streams; for (int i = 0; i < stream_count; i++) f->ic->streams[i]->discard = AVDISCARD_ALL; f->audio_stream_index = av_find_best_stream(f->ic, AVMEDIA_TYPE_AUDIO, -1, -1, &f->decoder, 0); if (f->audio_stream_index < 0) { groove_file_close(file); return GrooveErrorStreamNotFound; } if (!f->decoder) { groove_file_close(file); return GrooveErrorDecoderNotFound; } f->audio_st = f->ic->streams[f->audio_stream_index]; f->audio_st->discard = AVDISCARD_DEFAULT; AVCodecContext *avctx = f->audio_st->codec; if (avcodec_open2(avctx, f->decoder, NULL) < 0) { groove_file_close(file); return GrooveErrorDecoding; } if (!avctx->channel_layout) avctx->channel_layout = av_get_default_channel_layout(avctx->channels); if (!avctx->channel_layout) { groove_file_close(file); return GrooveErrorInvalidChannelLayout; } // copy the audio stream metadata to the context metadata av_dict_copy(&f->ic->metadata, f->audio_st->metadata, 0); return 0; }