static int decode_audio(sh_audio_t *sh_audio,unsigned char *buf,int minlen,int maxlen) { unsigned char *start=NULL; int y,len=-1; while(len<minlen){ AVPacket pkt; int len2=maxlen; double pts; int x=ds_get_packet_pts(sh_audio->ds,&start, &pts); if(x<=0) { start = NULL; x = 0; ds_parse(sh_audio->ds, &start, &x, MP_NOPTS_VALUE, 0); if (x <= 0) break; // error } else { int in_size = x; int consumed = ds_parse(sh_audio->ds, &start, &x, pts, 0); sh_audio->ds->buffer_pos -= in_size - consumed; } av_init_packet(&pkt); pkt.data = start; pkt.size = x; if (pts != MP_NOPTS_VALUE) { sh_audio->pts = pts; sh_audio->pts_bytes = 0; } y=avcodec_decode_audio3(sh_audio->context,(int16_t*)buf,&len2,&pkt); //printf("return:%d samples_out:%d bitstream_in:%d sample_sum:%d\n", y, len2, x, len); fflush(stdout); // LATM may need many packets to find mux info if (y == AVERROR(EAGAIN)) continue; if(y<0){ mp_msg(MSGT_DECAUDIO,MSGL_V,"lavc_audio: error\n");break; } if(!sh_audio->parser && y<x) sh_audio->ds->buffer_pos+=y-x; // put back data (HACK!) if(len2>0){ if (((AVCodecContext *)sh_audio->context)->channels >= 5) { int samplesize = av_get_bits_per_sample_format(((AVCodecContext *) sh_audio->context)->sample_fmt) / 8; reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_LAVC_DEFAULT, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, ((AVCodecContext *)sh_audio->context)->channels, len2 / samplesize, samplesize); } //len=len2;break; if(len<0) len=len2; else len+=len2; buf+=len2; maxlen -= len2; sh_audio->pts_bytes += len2; } mp_dbg(MSGT_DECAUDIO,MSGL_DBG2,"Decoded %d -> %d \n",y,len2); if (setup_format(sh_audio, sh_audio->context)) break; } return len; }
int scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) { AVFormatContext *ctx; const struct metadata_map *extra_md_map; enum CodecID codec_id; enum CodecID video_codec_id; enum CodecID audio_codec_id; AVStream *video_stream; AVStream *audio_stream; int mdcount; int i; int ret; ctx = NULL; #if LIBAVFORMAT_VERSION_MAJOR >= 53 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 3) ret = avformat_open_input(&ctx, file, NULL, NULL); #else ret = av_open_input_file(&ctx, file, NULL, 0, NULL); #endif if (ret != 0) { DPRINTF(E_WARN, L_SCAN, "Cannot open media file '%s': %s\n", file, strerror(AVUNERROR(ret))); return -1; } ret = av_find_stream_info(ctx); if (ret < 0) { DPRINTF(E_WARN, L_SCAN, "Cannot get stream info: %s\n", strerror(AVUNERROR(ret))); av_close_input_file(ctx); return -1; } #if 0 /* Dump input format as determined by ffmpeg */ # if LIBAVFORMAT_VERSION_MAJOR >= 52 || (LIBAVFORMAT_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 101) av_dump_format(ctx, 0, file, 0); # else dump_format(ctx, 0, file, FALSE); # endif #endif DPRINTF(E_DBG, L_SCAN, "File has %d streams\n", ctx->nb_streams); /* Extract codec IDs, check for video */ video_codec_id = CODEC_ID_NONE; video_stream = NULL; audio_codec_id = CODEC_ID_NONE; audio_stream = NULL; for (i = 0; i < ctx->nb_streams; i++) { switch (ctx->streams[i]->codec->codec_type) { #if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) case AVMEDIA_TYPE_VIDEO: #else case CODEC_TYPE_VIDEO: #endif if (!video_stream) { DPRINTF(E_DBG, L_SCAN, "File has video (stream %d)\n", i); mfi->has_video = 1; video_stream = ctx->streams[i]; video_codec_id = video_stream->codec->codec_id; } break; #if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) case AVMEDIA_TYPE_AUDIO: #else case CODEC_TYPE_AUDIO: #endif if (!audio_stream) { audio_stream = ctx->streams[i]; audio_codec_id = audio_stream->codec->codec_id; } break; default: break; } } if (audio_codec_id == CODEC_ID_NONE) { DPRINTF(E_DBG, L_SCAN, "File has no audio streams, discarding\n"); av_close_input_file(ctx); return -1; } /* Common media information */ if (ctx->duration > 0) mfi->song_length = ctx->duration / (AV_TIME_BASE / 1000); /* ms */ if (ctx->bit_rate > 0) mfi->bitrate = ctx->bit_rate / 1000; else if (ctx->duration > AV_TIME_BASE) /* guesstimate */ mfi->bitrate = ((mfi->file_size * 8) / (ctx->duration / AV_TIME_BASE)) / 1000; DPRINTF(E_DBG, L_SCAN, "Duration %d ms, bitrate %d kbps\n", mfi->song_length, mfi->bitrate); /* Get some more information on the audio stream */ if (audio_stream) { if (audio_stream->codec->sample_rate != 0) mfi->samplerate = audio_stream->codec->sample_rate; /* Try sample format first */ #if LIBAVUTIL_VERSION_MAJOR >= 51 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4) mfi->bits_per_sample = 8 * av_get_bytes_per_sample(audio_stream->codec->sample_fmt); #elif LIBAVCODEC_VERSION_MAJOR >= 53 mfi->bits_per_sample = av_get_bits_per_sample_fmt(audio_stream->codec->sample_fmt); #else mfi->bits_per_sample = av_get_bits_per_sample_format(audio_stream->codec->sample_fmt); #endif if (mfi->bits_per_sample == 0) { /* Try codec */ mfi->bits_per_sample = av_get_bits_per_sample(audio_codec_id); } DPRINTF(E_DBG, L_SCAN, "samplerate %d, bps %d\n", mfi->samplerate, mfi->bits_per_sample); } /* Check codec */ extra_md_map = NULL; codec_id = (mfi->has_video) ? video_codec_id : audio_codec_id; switch (codec_id) { case CODEC_ID_AAC: DPRINTF(E_DBG, L_SCAN, "AAC\n"); mfi->type = strdup("m4a"); mfi->codectype = strdup("mp4a"); mfi->description = strdup("AAC audio file"); break; case CODEC_ID_ALAC: DPRINTF(E_DBG, L_SCAN, "ALAC\n"); mfi->type = strdup("m4a"); mfi->codectype = strdup("alac"); mfi->description = strdup("AAC audio file"); break; case CODEC_ID_FLAC: DPRINTF(E_DBG, L_SCAN, "FLAC\n"); mfi->type = strdup("flac"); mfi->codectype = strdup("flac"); mfi->description = strdup("FLAC audio file"); extra_md_map = md_map_vorbis; break; case CODEC_ID_MUSEPACK7: case CODEC_ID_MUSEPACK8: DPRINTF(E_DBG, L_SCAN, "Musepack\n"); mfi->type = strdup("mpc"); mfi->codectype = strdup("mpc"); mfi->description = strdup("Musepack audio file"); break; case CODEC_ID_MPEG4: /* Video */ case CODEC_ID_H264: DPRINTF(E_DBG, L_SCAN, "MPEG4 video\n"); mfi->type = strdup("m4v"); mfi->codectype = strdup("mp4v"); mfi->description = strdup("MPEG-4 video file"); extra_md_map = md_map_tv; break; case CODEC_ID_MP3: DPRINTF(E_DBG, L_SCAN, "MP3\n"); mfi->type = strdup("mp3"); mfi->codectype = strdup("mpeg"); mfi->description = strdup("MPEG audio file"); extra_md_map = md_map_id3; break; case CODEC_ID_VORBIS: DPRINTF(E_DBG, L_SCAN, "VORBIS\n"); mfi->type = strdup("ogg"); mfi->codectype = strdup("ogg"); mfi->description = strdup("Ogg Vorbis audio file"); extra_md_map = md_map_vorbis; break; case CODEC_ID_WMAVOICE: DPRINTF(E_DBG, L_SCAN, "WMA Voice\n"); mfi->type = strdup("wma"); mfi->codectype = strdup("wmav"); mfi->description = strdup("WMA audio file"); break; case CODEC_ID_WMAPRO: DPRINTF(E_DBG, L_SCAN, "WMA Pro\n"); mfi->type = strdup("wmap"); mfi->codectype = strdup("wma"); mfi->description = strdup("WMA audio file"); break; case CODEC_ID_WMALOSSLESS: DPRINTF(E_DBG, L_SCAN, "WMA Lossless\n"); mfi->type = strdup("wma"); mfi->codectype = strdup("wmal"); mfi->description = strdup("WMA audio file"); break; case CODEC_ID_WMAV1: case CODEC_ID_WMAV2: DPRINTF(E_DBG, L_SCAN, "WMA V1/V2\n"); mfi->type = strdup("wma"); mfi->codectype = strdup("wma"); mfi->description = strdup("WMA audio file"); break; case CODEC_ID_PCM_S16LE ... CODEC_ID_PCM_F64LE: if (strcmp(ctx->iformat->name, "aiff") == 0) { DPRINTF(E_DBG, L_SCAN, "AIFF\n"); mfi->type = strdup("aif"); mfi->codectype = strdup("aif"); mfi->description = strdup("AIFF audio file"); break; } else if (strcmp(ctx->iformat->name, "wav") == 0) { DPRINTF(E_DBG, L_SCAN, "WAV\n"); mfi->type = strdup("wav"); mfi->codectype = strdup("wav"); mfi->description = strdup("WAV audio file"); break; } /* WARNING: will fallthrough to default case, don't move */ /* FALLTHROUGH */ default: DPRINTF(E_DBG, L_SCAN, "Unknown codec 0x%x (video: %s), format %s (%s)\n", codec_id, (mfi->has_video) ? "yes" : "no", ctx->iformat->name, ctx->iformat->long_name); mfi->type = strdup("unkn"); mfi->codectype = strdup("unkn"); if (mfi->has_video) { mfi->description = strdup("Unknown video file format"); extra_md_map = md_map_tv; } else mfi->description = strdup("Unknown audio file format"); break; } mdcount = 0; if ((!ctx->metadata) && (!audio_stream->metadata) && (video_stream && !video_stream->metadata)) { DPRINTF(E_WARN, L_SCAN, "ffmpeg reports no metadata\n"); goto skip_extract; } if (extra_md_map) { ret = extract_metadata(mfi, ctx, audio_stream, video_stream, extra_md_map); mdcount += ret; DPRINTF(E_DBG, L_SCAN, "Picked up %d tags with extra md_map\n", ret); } #if LIBAVFORMAT_VERSION_MAJOR < 53 av_metadata_conv(ctx, NULL, ctx->iformat->metadata_conv); #endif ret = extract_metadata(mfi, ctx, audio_stream, video_stream, md_map_generic); mdcount += ret; DPRINTF(E_DBG, L_SCAN, "Picked up %d tags with generic md_map, %d tags total\n", ret, mdcount); /* fix up TV metadata */ if (mfi->media_kind == 10) { /* I have no idea why this is, but iTunes reports a media kind of 64 for stik==10 (?!) */ mfi->media_kind = 64; } /* Unspecified video files are "Movies", media_kind 2 */ else if (mfi->has_video == 1) { mfi->media_kind = 2; } skip_extract: if (mdcount == 0) { /* ffmpeg doesn't support FLAC nor Musepack metadata, * and is buggy for some WMA variants, so fall back to the * legacy format-specific parsers until it gets fixed */ if ((codec_id == CODEC_ID_WMAPRO) || (codec_id == CODEC_ID_WMAVOICE) || (codec_id == CODEC_ID_WMALOSSLESS)) { DPRINTF(E_WARN, L_SCAN, "Falling back to legacy WMA scanner\n"); av_close_input_file(ctx); return (scan_get_wmainfo(file, mfi) ? 0 : -1); } #ifdef FLAC else if (codec_id == CODEC_ID_FLAC) { DPRINTF(E_WARN, L_SCAN, "Falling back to legacy FLAC scanner\n"); av_close_input_file(ctx); return (scan_get_flacinfo(file, mfi) ? 0 : -1); } #endif /* FLAC */ #ifdef MUSEPACK else if ((codec_id == CODEC_ID_MUSEPACK7) || (codec_id == CODEC_ID_MUSEPACK8)) { DPRINTF(E_WARN, L_SCAN, "Falling back to legacy Musepack scanner\n"); av_close_input_file(ctx); return (scan_get_mpcinfo(file, mfi) ? 0 : -1); } #endif /* MUSEPACK */ else DPRINTF(E_WARN, L_SCAN, "Could not extract any metadata\n"); } /* Just in case there's no title set ... */ if (mfi->title == NULL) mfi->title = strdup(mfi->fname); /* All done */ av_close_input_file(ctx); return 0; }
int scan_metadata_ffmpeg(char *file, struct media_file_info *mfi) { AVFormatContext *ctx; AVDictionary *options; const struct metadata_map *extra_md_map; struct http_icy_metadata *icy_metadata; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) enum AVCodecID codec_id; enum AVCodecID video_codec_id; enum AVCodecID audio_codec_id; #else enum CodecID codec_id; enum CodecID video_codec_id; enum CodecID audio_codec_id; #endif AVStream *video_stream; AVStream *audio_stream; char *path; int mdcount; int i; int ret; ctx = NULL; options = NULL; path = strdup(file); #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) # ifndef HAVE_FFMPEG // Without this, libav is slow to probe some internet streams if (mfi->data_kind == DATA_KIND_HTTP) { ctx = avformat_alloc_context(); ctx->probesize = 64000; } # endif if (mfi->data_kind == DATA_KIND_HTTP) { free(path); ret = http_stream_setup(&path, file); if (ret < 0) return -1; av_dict_set(&options, "icy", "1", 0); mfi->artwork = ARTWORK_HTTP; } ret = avformat_open_input(&ctx, path, NULL, &options); if (options) av_dict_free(&options); #else ret = av_open_input_file(&ctx, path, NULL, 0, NULL); #endif if (ret != 0) { DPRINTF(E_WARN, L_SCAN, "Cannot open media file '%s': %s\n", path, strerror(AVUNERROR(ret))); free(path); return -1; } free(path); #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) ret = avformat_find_stream_info(ctx, NULL); #else ret = av_find_stream_info(ctx); #endif if (ret < 0) { DPRINTF(E_WARN, L_SCAN, "Cannot get stream info: %s\n", strerror(AVUNERROR(ret))); #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21) avformat_close_input(&ctx); #else av_close_input_file(ctx); #endif return -1; } #if 0 /* Dump input format as determined by ffmpeg */ av_dump_format(ctx, 0, file, 0); #endif DPRINTF(E_DBG, L_SCAN, "File has %d streams\n", ctx->nb_streams); /* Extract codec IDs, check for video */ #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) video_codec_id = AV_CODEC_ID_NONE; video_stream = NULL; audio_codec_id = AV_CODEC_ID_NONE; audio_stream = NULL; #else video_codec_id = CODEC_ID_NONE; video_stream = NULL; audio_codec_id = CODEC_ID_NONE; audio_stream = NULL; #endif for (i = 0; i < ctx->nb_streams; i++) { switch (ctx->streams[i]->codec->codec_type) { #if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) case AVMEDIA_TYPE_VIDEO: #else case CODEC_TYPE_VIDEO: #endif #if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 6) if (ctx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) { DPRINTF(E_DBG, L_SCAN, "Found embedded artwork (stream %d)\n", i); mfi->artwork = ARTWORK_EMBEDDED; break; } #endif // We treat these as audio no matter what if (mfi->compilation || (mfi->media_kind & (MEDIA_KIND_PODCAST | MEDIA_KIND_AUDIOBOOK))) break; if (!video_stream) { DPRINTF(E_DBG, L_SCAN, "File has video (stream %d)\n", i); mfi->has_video = 1; video_stream = ctx->streams[i]; video_codec_id = video_stream->codec->codec_id; } break; #if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) case AVMEDIA_TYPE_AUDIO: #else case CODEC_TYPE_AUDIO: #endif if (!audio_stream) { audio_stream = ctx->streams[i]; audio_codec_id = audio_stream->codec->codec_id; } break; default: break; } } #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) if (audio_codec_id == AV_CODEC_ID_NONE) #else if (audio_codec_id == CODEC_ID_NONE) #endif { DPRINTF(E_DBG, L_SCAN, "File has no audio streams, discarding\n"); #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21) avformat_close_input(&ctx); #else av_close_input_file(ctx); #endif return -1; } /* Common media information */ if (ctx->duration > 0) mfi->song_length = ctx->duration / (AV_TIME_BASE / 1000); /* ms */ if (ctx->bit_rate > 0) mfi->bitrate = ctx->bit_rate / 1000; else if (ctx->duration > AV_TIME_BASE) /* guesstimate */ mfi->bitrate = ((mfi->file_size * 8) / (ctx->duration / AV_TIME_BASE)) / 1000; DPRINTF(E_DBG, L_SCAN, "Duration %d ms, bitrate %d kbps\n", mfi->song_length, mfi->bitrate); /* Try to extract ICY metadata if http stream */ if (mfi->data_kind == DATA_KIND_HTTP) { icy_metadata = http_icy_metadata_get(ctx, 0); if (icy_metadata && icy_metadata->name) { DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, name is '%s'\n", icy_metadata->name); if (mfi->title) free(mfi->title); if (mfi->artist) free(mfi->artist); if (mfi->album_artist) free(mfi->album_artist); mfi->title = strdup(icy_metadata->name); mfi->artist = strdup(icy_metadata->name); mfi->album_artist = strdup(icy_metadata->name); } if (icy_metadata && icy_metadata->description) { DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, description is '%s'\n", icy_metadata->description); if (mfi->album) free(mfi->album); mfi->album = strdup(icy_metadata->description); } if (icy_metadata && icy_metadata->genre) { DPRINTF(E_DBG, L_SCAN, "Found ICY metadata, genre is '%s'\n", icy_metadata->genre); if (mfi->genre) free(mfi->genre); mfi->genre = strdup(icy_metadata->genre); } if (icy_metadata) http_icy_metadata_free(icy_metadata, 0); } /* Get some more information on the audio stream */ if (audio_stream) { if (audio_stream->codec->sample_rate != 0) mfi->samplerate = audio_stream->codec->sample_rate; /* Try sample format first */ #if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4) mfi->bits_per_sample = 8 * av_get_bytes_per_sample(audio_stream->codec->sample_fmt); #elif LIBAVCODEC_VERSION_MAJOR >= 53 mfi->bits_per_sample = av_get_bits_per_sample_fmt(audio_stream->codec->sample_fmt); #else mfi->bits_per_sample = av_get_bits_per_sample_format(audio_stream->codec->sample_fmt); #endif if (mfi->bits_per_sample == 0) { /* Try codec */ mfi->bits_per_sample = av_get_bits_per_sample(audio_codec_id); } DPRINTF(E_DBG, L_SCAN, "samplerate %d, bps %d\n", mfi->samplerate, mfi->bits_per_sample); } /* Check codec */ extra_md_map = NULL; codec_id = (mfi->has_video) ? video_codec_id : audio_codec_id; switch (codec_id) { #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_AAC: #else case CODEC_ID_AAC: #endif DPRINTF(E_DBG, L_SCAN, "AAC\n"); mfi->type = strdup("m4a"); mfi->codectype = strdup("mp4a"); mfi->description = strdup("AAC audio file"); break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_ALAC: #else case CODEC_ID_ALAC: #endif DPRINTF(E_DBG, L_SCAN, "ALAC\n"); mfi->type = strdup("m4a"); mfi->codectype = strdup("alac"); mfi->description = strdup("Apple Lossless audio file"); break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_FLAC: #else case CODEC_ID_FLAC: #endif DPRINTF(E_DBG, L_SCAN, "FLAC\n"); mfi->type = strdup("flac"); mfi->codectype = strdup("flac"); mfi->description = strdup("FLAC audio file"); extra_md_map = md_map_vorbis; break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_MUSEPACK7: case AV_CODEC_ID_MUSEPACK8: #else case CODEC_ID_MUSEPACK7: case CODEC_ID_MUSEPACK8: #endif DPRINTF(E_DBG, L_SCAN, "Musepack\n"); mfi->type = strdup("mpc"); mfi->codectype = strdup("mpc"); mfi->description = strdup("Musepack audio file"); break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_MPEG4: /* Video */ case AV_CODEC_ID_H264: #else case CODEC_ID_MPEG4: /* Video */ case CODEC_ID_H264: #endif DPRINTF(E_DBG, L_SCAN, "MPEG4 video\n"); mfi->type = strdup("m4v"); mfi->codectype = strdup("mp4v"); mfi->description = strdup("MPEG-4 video file"); extra_md_map = md_map_tv; break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_MP3: #else case CODEC_ID_MP3: #endif DPRINTF(E_DBG, L_SCAN, "MP3\n"); mfi->type = strdup("mp3"); mfi->codectype = strdup("mpeg"); mfi->description = strdup("MPEG audio file"); extra_md_map = md_map_id3; break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_VORBIS: #else case CODEC_ID_VORBIS: #endif DPRINTF(E_DBG, L_SCAN, "VORBIS\n"); mfi->type = strdup("ogg"); mfi->codectype = strdup("ogg"); mfi->description = strdup("Ogg Vorbis audio file"); extra_md_map = md_map_vorbis; break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_WMAV1: case AV_CODEC_ID_WMAV2: case AV_CODEC_ID_WMAVOICE: #else case CODEC_ID_WMAV1: case CODEC_ID_WMAV2: case CODEC_ID_WMAVOICE: #endif DPRINTF(E_DBG, L_SCAN, "WMA Voice\n"); mfi->type = strdup("wma"); mfi->codectype = strdup("wmav"); mfi->description = strdup("WMA audio file"); break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_WMAPRO: #else case CODEC_ID_WMAPRO: #endif DPRINTF(E_DBG, L_SCAN, "WMA Pro\n"); mfi->type = strdup("wmap"); mfi->codectype = strdup("wma"); mfi->description = strdup("WMA audio file"); break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_WMALOSSLESS: #else case CODEC_ID_WMALOSSLESS: #endif DPRINTF(E_DBG, L_SCAN, "WMA Lossless\n"); mfi->type = strdup("wma"); mfi->codectype = strdup("wmal"); mfi->description = strdup("WMA audio file"); break; #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) case AV_CODEC_ID_PCM_S16LE ... AV_CODEC_ID_PCM_F64LE: #else case CODEC_ID_PCM_S16LE ... CODEC_ID_PCM_F64LE: #endif if (strcmp(ctx->iformat->name, "aiff") == 0) { DPRINTF(E_DBG, L_SCAN, "AIFF\n"); mfi->type = strdup("aif"); mfi->codectype = strdup("aif"); mfi->description = strdup("AIFF audio file"); break; } else if (strcmp(ctx->iformat->name, "wav") == 0) { DPRINTF(E_DBG, L_SCAN, "WAV\n"); mfi->type = strdup("wav"); mfi->codectype = strdup("wav"); mfi->description = strdup("WAV audio file"); break; } /* WARNING: will fallthrough to default case, don't move */ /* FALLTHROUGH */ default: DPRINTF(E_DBG, L_SCAN, "Unknown codec 0x%x (video: %s), format %s (%s)\n", codec_id, (mfi->has_video) ? "yes" : "no", ctx->iformat->name, ctx->iformat->long_name); mfi->type = strdup("unkn"); mfi->codectype = strdup("unkn"); if (mfi->has_video) { mfi->description = strdup("Unknown video file format"); extra_md_map = md_map_tv; } else mfi->description = strdup("Unknown audio file format"); break; } mdcount = 0; if ((!ctx->metadata) && (!audio_stream->metadata) && (video_stream && !video_stream->metadata)) { DPRINTF(E_WARN, L_SCAN, "ffmpeg reports no metadata\n"); goto skip_extract; } if (extra_md_map) { ret = extract_metadata(mfi, ctx, audio_stream, video_stream, extra_md_map); mdcount += ret; DPRINTF(E_DBG, L_SCAN, "Picked up %d tags with extra md_map\n", ret); } ret = extract_metadata(mfi, ctx, audio_stream, video_stream, md_map_generic); mdcount += ret; DPRINTF(E_DBG, L_SCAN, "Picked up %d tags with generic md_map, %d tags total\n", ret, mdcount); /* fix up TV metadata */ if (mfi->media_kind == 10) { /* I have no idea why this is, but iTunes reports a media kind of 64 for stik==10 (?!) */ mfi->media_kind = MEDIA_KIND_TVSHOW; } /* Unspecified video files are "Movies", media_kind 2 */ else if (mfi->has_video == 1) { mfi->media_kind = MEDIA_KIND_MOVIE; } skip_extract: #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21) avformat_close_input(&ctx); #else av_close_input_file(ctx); #endif if (mdcount == 0) DPRINTF(E_WARN, L_SCAN, "ffmpeg/libav could not extract any metadata\n"); /* Just in case there's no title set ... */ if (mfi->title == NULL) mfi->title = strdup(mfi->fname); /* All done */ return 0; }
int decode_audio_file(ChromaprintContext *chromaprint_ctx, int16_t *buffer1, int16_t *buffer2, const char *file_name, int max_length, int *duration) { int i, ok = 0, remaining, length, consumed, buffer_size, codec_ctx_opened = 0; AVFormatContext *format_ctx = NULL; AVCodecContext *codec_ctx = NULL; AVCodec *codec = NULL; AVStream *stream = NULL; AVPacket packet, packet_temp; #ifdef HAVE_AV_AUDIO_CONVERT AVAudioConvert *convert_ctx = NULL; #endif int16_t *buffer; if (!strcmp(file_name, "-")) { file_name = "pipe:0"; } #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0) if (av_open_input_file(&format_ctx, file_name, NULL, 0, NULL) != 0) { #else if (avformat_open_input(&format_ctx, file_name, NULL, NULL) != 0) { #endif fprintf(stderr, "ERROR: couldn't open the file\n"); goto done; } if (av_find_stream_info(format_ctx) < 0) { fprintf(stderr, "ERROR: couldn't find stream information in the file\n"); goto done; } for (i = 0; i < format_ctx->nb_streams; i++) { codec_ctx = format_ctx->streams[i]->codec; if (codec_ctx && codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { stream = format_ctx->streams[i]; break; } } if (!stream) { fprintf(stderr, "ERROR: couldn't find any audio stream in the file\n"); goto done; } codec = avcodec_find_decoder(codec_ctx->codec_id); if (!codec) { fprintf(stderr, "ERROR: unknown codec\n"); goto done; } if (avcodec_open(codec_ctx, codec) < 0) { fprintf(stderr, "ERROR: couldn't open the codec\n"); goto done; } codec_ctx_opened = 1; if (codec_ctx->channels <= 0) { fprintf(stderr, "ERROR: no channels found in the audio stream\n"); goto done; } if (codec_ctx->sample_fmt != AV_SAMPLE_FMT_S16) { #ifdef HAVE_AV_AUDIO_CONVERT convert_ctx = av_audio_convert_alloc(AV_SAMPLE_FMT_S16, codec_ctx->channels, codec_ctx->sample_fmt, codec_ctx->channels, NULL, 0); if (!convert_ctx) { fprintf(stderr, "ERROR: couldn't create sample format converter\n"); goto done; } #else fprintf(stderr, "ERROR: unsupported sample format\n"); goto done; #endif } *duration = stream->time_base.num * stream->duration / stream->time_base.den; av_init_packet(&packet); av_init_packet(&packet_temp); remaining = max_length * codec_ctx->channels * codec_ctx->sample_rate; chromaprint_start(chromaprint_ctx, codec_ctx->sample_rate, codec_ctx->channels); while (1) { if (av_read_frame(format_ctx, &packet) < 0) { break; } packet_temp.data = packet.data; packet_temp.size = packet.size; while (packet_temp.size > 0) { buffer_size = BUFFER_SIZE; #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 23, 0) consumed = avcodec_decode_audio2(codec_ctx, buffer1, &buffer_size, packet_temp.data, packet_temp.size); #else consumed = avcodec_decode_audio3(codec_ctx, buffer1, &buffer_size, &packet_temp); #endif if (consumed < 0) { break; } packet_temp.data += consumed; packet_temp.size -= consumed; if (buffer_size <= 0) { if (buffer_size < 0) { fprintf(stderr, "WARNING: size returned from avcodec_decode_audioX is too small\n"); } continue; } if (buffer_size > BUFFER_SIZE) { fprintf(stderr, "WARNING: size returned from avcodec_decode_audioX is too large\n"); continue; } #ifdef HAVE_AV_AUDIO_CONVERT if (convert_ctx) { const void *ibuf[6] = { buffer1 }; void *obuf[6] = { buffer2 }; #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 8, 0) int istride[6] = { av_get_bits_per_sample_format(codec_ctx->sample_fmt) / 8 }; #else int istride[6] = { av_get_bytes_per_sample(codec_ctx->sample_fmt) }; #endif int ostride[6] = { 2 }; int len = buffer_size / istride[0]; if (av_audio_convert(convert_ctx, obuf, ostride, ibuf, istride, len) < 0) { break; } buffer = buffer2; buffer_size = len * ostride[0]; } else { buffer = buffer1; } #else buffer = buffer1; #endif length = MIN(remaining, buffer_size / 2); if (!chromaprint_feed(chromaprint_ctx, buffer, length)) { fprintf(stderr, "ERROR: fingerprint calculation failed\n"); goto done; } if (max_length) { remaining -= length; if (remaining <= 0) { goto finish; } } } if (packet.data) { av_free_packet(&packet); } } finish: if (!chromaprint_finish(chromaprint_ctx)) { fprintf(stderr, "ERROR: fingerprint calculation failed\n"); goto done; } ok = 1; done: if (codec_ctx_opened) { avcodec_close(codec_ctx); } if (format_ctx) { av_close_input_file(format_ctx); } #ifdef HAVE_AV_AUDIO_CONVERT if (convert_ctx) { av_audio_convert_free(convert_ctx); } #endif return ok; } int fpcalc_main(int argc, char **argv) { int i, j, max_length = 120, num_file_names = 0, raw = 0, raw_fingerprint_size, duration; int16_t *buffer1, *buffer2; int32_t *raw_fingerprint; char *file_name, *fingerprint, **file_names; ChromaprintContext *chromaprint_ctx; int algo = CHROMAPRINT_ALGORITHM_DEFAULT; file_names = malloc(argc * sizeof(char *)); for (i = 1; i < argc; i++) { char *arg = argv[i]; if (!strcmp(arg, "-length") && i + 1 < argc) { max_length = atoi(argv[++i]); } else if (!strcmp(arg, "-version") || !strcmp(arg, "-v")) { printf("fpcalc version %s\n", chromaprint_get_version()); return 0; } else if (!strcmp(arg, "-raw")) { raw = 1; } else if (!strcmp(arg, "-algo") && i + 1 < argc) { const char *v = argv[++i]; if (!strcmp(v, "test1")) { algo = CHROMAPRINT_ALGORITHM_TEST1; } else if (!strcmp(v, "test2")) { algo = CHROMAPRINT_ALGORITHM_TEST2; } else if (!strcmp(v, "test3")) { algo = CHROMAPRINT_ALGORITHM_TEST3; } else if (!strcmp(v, "test4")) { algo = CHROMAPRINT_ALGORITHM_TEST4; } else { fprintf(stderr, "WARNING: unknown algorithm, using the default\n"); } } else if (!strcmp(arg, "-set") && i + 1 < argc) { i += 1; } else { file_names[num_file_names++] = argv[i]; } } if (!num_file_names) { printf("usage: %s [OPTIONS] FILE...\n\n", argv[0]); printf("Options:\n"); printf(" -version print version information\n"); printf(" -length SECS length of the audio data used for fingerprint calculation (default 120)\n"); printf(" -raw output the raw uncompressed fingerprint\n"); printf(" -algo NAME version of the fingerprint algorithm\n"); return 2; } av_register_all(); av_log_set_level(AV_LOG_ERROR); buffer1 = av_malloc(BUFFER_SIZE + 16); buffer2 = av_malloc(BUFFER_SIZE + 16); chromaprint_ctx = chromaprint_new(algo); for (i = 1; i < argc; i++) { char *arg = argv[i]; if (!strcmp(arg, "-set") && i + 1 < argc) { char *name = argv[++i]; char *value = strchr(name, '='); if (value) { *value++ = '\0'; chromaprint_set_option(chromaprint_ctx, name, atoi(value)); } } } for (i = 0; i < num_file_names; i++) { file_name = file_names[i]; if (!decode_audio_file(chromaprint_ctx, buffer1, buffer2, file_name, max_length, &duration)) { fprintf(stderr, "ERROR: unable to calculate fingerprint for file %s, skipping\n", file_name); continue; } if (i > 0) { printf("\n"); } printf("FILE=%s\n", file_name); printf("DURATION=%d\n", duration); if (raw) { if (!chromaprint_get_raw_fingerprint(chromaprint_ctx, (void **)&raw_fingerprint, &raw_fingerprint_size)) { fprintf(stderr, "ERROR: unable to calculate fingerprint for file %s, skipping\n", file_name); continue; } printf("FINGERPRINT="); for (j = 0; j < raw_fingerprint_size; j++) { printf("%d%s", raw_fingerprint[j], j + 1 < raw_fingerprint_size ? "," : ""); } printf("\n"); chromaprint_dealloc(raw_fingerprint); } else { if (!chromaprint_get_fingerprint(chromaprint_ctx, &fingerprint)) { fprintf(stderr, "ERROR: unable to calculate fingerprint for file %s, skipping\n", file_name); continue; } printf("FINGERPRINT=%s\n", fingerprint); chromaprint_dealloc(fingerprint); } } chromaprint_free(chromaprint_ctx); av_free(buffer1); av_free(buffer2); free(file_names); return 0; }
static GstFlowReturn gst_ffmpegenc_chain_audio (GstPad * pad, GstBuffer * inbuf) { GstFFMpegEnc *ffmpegenc; GstFFMpegEncClass *oclass; AVCodecContext *ctx; GstClockTime timestamp, duration; guint size, frame_size; gint osize; GstFlowReturn ret; gint out_size; gboolean discont; guint8 *in_data; ffmpegenc = (GstFFMpegEnc *) (GST_OBJECT_PARENT (pad)); oclass = (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc); ctx = ffmpegenc->context; size = GST_BUFFER_SIZE (inbuf); timestamp = GST_BUFFER_TIMESTAMP (inbuf); duration = GST_BUFFER_DURATION (inbuf); discont = GST_BUFFER_IS_DISCONT (inbuf); GST_DEBUG_OBJECT (ffmpegenc, "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", size %d", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), size); frame_size = ctx->frame_size; osize = av_get_bits_per_sample_format (ctx->sample_fmt) / 8; if (frame_size > 1) { /* we have a frame_size, feed the encoder multiples of this frame size */ guint avail, frame_bytes; if (discont) { GST_LOG_OBJECT (ffmpegenc, "DISCONT, clear adapter"); gst_adapter_clear (ffmpegenc->adapter); ffmpegenc->discont = TRUE; } if (gst_adapter_available (ffmpegenc->adapter) == 0) { /* lock on to new timestamp */ GST_LOG_OBJECT (ffmpegenc, "taking buffer timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); ffmpegenc->adapter_ts = timestamp; ffmpegenc->adapter_consumed = 0; } else { GstClockTime upstream_time; guint64 bytes; /* use timestamp at head of the adapter */ GST_LOG_OBJECT (ffmpegenc, "taking adapter timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (ffmpegenc->adapter_ts)); timestamp = ffmpegenc->adapter_ts; timestamp += gst_util_uint64_scale (ffmpegenc->adapter_consumed, GST_SECOND, ctx->sample_rate); /* check with upstream timestamps, if too much deviation, * forego some timestamp perfection in favour of upstream syncing * (particularly in case these do not happen to come in multiple * of frame size) */ upstream_time = gst_adapter_prev_timestamp (ffmpegenc->adapter, &bytes); if (GST_CLOCK_TIME_IS_VALID (upstream_time)) { GstClockTimeDiff diff; upstream_time += gst_util_uint64_scale (bytes, GST_SECOND, ctx->sample_rate); diff = upstream_time - timestamp; /* relaxed difference, rather than half a sample or so ... */ if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) { GST_DEBUG_OBJECT (ffmpegenc, "adapter timestamp drifting, " "taking upstream timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (upstream_time)); timestamp = upstream_time; ffmpegenc->adapter_ts = upstream_time - gst_util_uint64_scale (bytes, GST_SECOND, ctx->sample_rate); ffmpegenc->adapter_consumed = bytes; ffmpegenc->discont = TRUE; } } } GST_LOG_OBJECT (ffmpegenc, "pushing buffer in adapter"); gst_adapter_push (ffmpegenc->adapter, inbuf); /* first see how many bytes we need to feed to the decoder. */ frame_bytes = frame_size * osize * ctx->channels; avail = gst_adapter_available (ffmpegenc->adapter); GST_LOG_OBJECT (ffmpegenc, "frame_bytes %u, avail %u", frame_bytes, avail); /* while there is more than a frame size in the adapter, consume it */ while (avail >= frame_bytes) { GST_LOG_OBJECT (ffmpegenc, "taking %u bytes from the adapter", frame_bytes); /* take an audio buffer out of the adapter */ in_data = (guint8 *) gst_adapter_peek (ffmpegenc->adapter, frame_bytes); ffmpegenc->adapter_consumed += frame_size; /* calculate timestamp and duration relative to start of adapter and to * the amount of samples we consumed */ duration = gst_util_uint64_scale (ffmpegenc->adapter_consumed, GST_SECOND, ctx->sample_rate); duration -= (timestamp - ffmpegenc->adapter_ts); /* 4 times the input size plus the minimal buffer size * should be big enough... */ out_size = frame_bytes * 4 + FF_MIN_BUFFER_SIZE; ret = gst_ffmpegenc_encode_audio (ffmpegenc, in_data, out_size, timestamp, duration, ffmpegenc->discont); gst_adapter_flush (ffmpegenc->adapter, frame_bytes); if (ret != GST_FLOW_OK) goto push_failed; /* advance the adapter timestamp with the duration */ timestamp += duration; ffmpegenc->discont = FALSE; avail = gst_adapter_available (ffmpegenc->adapter); } GST_LOG_OBJECT (ffmpegenc, "%u bytes left in the adapter", avail); } else { /* we have no frame_size, feed the encoder all the data and expect a fixed * output size */ int coded_bps = av_get_bits_per_sample (oclass->in_plugin->id) / 8; GST_LOG_OBJECT (ffmpegenc, "coded bps %d, osize %d", coded_bps, osize); out_size = size / osize; if (coded_bps) out_size *= coded_bps; /* We need to provide at least ffmpegs minimal buffer size */ out_size += FF_MIN_BUFFER_SIZE; in_data = (guint8 *) GST_BUFFER_DATA (inbuf); ret = gst_ffmpegenc_encode_audio (ffmpegenc, in_data, out_size, timestamp, duration, discont); gst_buffer_unref (inbuf); if (ret != GST_FLOW_OK) goto push_failed; } return GST_FLOW_OK; /* ERRORS */ push_failed: { GST_DEBUG_OBJECT (ffmpegenc, "Failed to push buffer %d (%s)", ret, gst_flow_get_name (ret)); return ret; } }