From 6dacea6e811ebff1e88fc4a9f717b3e7cbf99378 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 21 Aug 2021 22:18:24 -0600 Subject: plugins/ffmpeg_decoder: migrate to FFmpeg 4.x API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Zixing Liu plugins/ffmpeg_decoder: Add detection whether AVFrame structure has pkt_size and channels members. Signed-off-by: Slávek Banko --- CMakeLists.txt | 1 + ConfigureChecks.cmake | 17 +++ akode/plugins/ffmpeg_decoder/CMakeLists.txt | 1 - akode/plugins/ffmpeg_decoder/ffmpeg_decoder.cpp | 138 +++++++++++++----------- config.h.cmake | 4 + 5 files changed, 99 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4050e06..4abe3ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ include( CheckFunctionExists ) include( CheckSymbolExists ) include( CheckIncludeFile ) include( CheckLibraryExists ) +include( CheckStructHasMember ) ##### include our cmake modules diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index ea07330..dd50162 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -169,6 +169,23 @@ if( WITH_FFMPEG_DECODER ) tde_message_fatal( "libavcodec >= 50 are required, but not found on your system" ) endif( NOT AVCODEC_FOUND ) + pkg_search_module( AVUTIL libavutil>=50 ) + if( NOT AVUTIL_FOUND ) + tde_message_fatal( "libavutil >= 50 are required, but not found on your system" ) + endif( NOT AVUTIL_FOUND ) + + message( STATUS "Looking for ffmpeg frame.h" ) + find_file( AVFRAME_H NAMES frame.h avcodec.h + PATHS ${AVUTIL_INCLUDE_DIRS} ${AVCODEC_INCLUDE_DIRS} + PATH_SUFFIXES libavutil libavcodec + ) + if( NOT AVFRAME_H ) + tde_message_fatal( "FFMPEG header frame.h cannot be found!" ) + endif() + message( STATUS "Looking for ffmpeg frame.h - found ${AVFRAME_H}" ) + check_struct_has_member( AVFrame pkt_size ${AVFRAME_H} FFMPEG_AVFRAME_HAVE_PKT_SIZE ) + check_struct_has_member( AVFrame channels ${AVFRAME_H} FFMPEG_AVFRAME_HAVE_CHANNELS ) + set( HAVE_FFMPEG 1 ) endif( WITH_FFMPEG_DECODER ) diff --git a/akode/plugins/ffmpeg_decoder/CMakeLists.txt b/akode/plugins/ffmpeg_decoder/CMakeLists.txt index f1d4c73..edffafe 100644 --- a/akode/plugins/ffmpeg_decoder/CMakeLists.txt +++ b/akode/plugins/ffmpeg_decoder/CMakeLists.txt @@ -30,4 +30,3 @@ tde_add_library( LINK akode-shared ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} DESTINATION ${LIB_INSTALL_DIR} ) - diff --git a/akode/plugins/ffmpeg_decoder/ffmpeg_decoder.cpp b/akode/plugins/ffmpeg_decoder/ffmpeg_decoder.cpp index 67156a1..4d0506c 100644 --- a/akode/plugins/ffmpeg_decoder/ffmpeg_decoder.cpp +++ b/akode/plugins/ffmpeg_decoder/ffmpeg_decoder.cpp @@ -26,13 +26,21 @@ #include "decoder.h" #include -#include -#include -#include +extern "C" { +#include +#include +#include +} #include "ffmpeg_decoder.h" #include +#if LIBAVCODEC_VERSION_MAJOR < 58 +#define CODECPAR codec +#else +#define CODECPAR codecpar +#endif + // FFMPEG callbacks extern "C" { static int akode_read(void* opaque, unsigned char *buf, int size) @@ -45,7 +53,7 @@ extern "C" { aKode::File *file = (aKode::File*)opaque; return file->write((char*)buf, size); } - static offset_t akode_seek(void* opaque, offset_t pos, int whence) + static off_t akode_seek(void* opaque, off_t pos, int whence) { aKode::File *file = (aKode::File*)opaque; return file->seek(pos, whence); @@ -76,7 +84,7 @@ struct FFMPEGDecoder::private_data AVFormatContext* ic; AVCodec* codec; AVInputFormat *fmt; - ByteIOContext stream; + AVIOContext* stream; int audioStream; int videoStream; @@ -94,14 +102,13 @@ struct FFMPEGDecoder::private_data bool initialized; int retries; - unsigned char file_buffer[FILE_BUFFER_SIZE]; - char buffer[AVCODEC_MAX_AUDIO_FRAME_SIZE]; + unsigned char *file_buffer; + unsigned char **buffer; int buffer_size; }; FFMPEGDecoder::FFMPEGDecoder(File *src) { d = new private_data; - av_register_all(); d->src = src; } @@ -121,10 +128,12 @@ static bool setAudioConfiguration(AudioConfiguration *config, AVCodecContext *co config->channel_config = MonoStereo; // avcodec.h says sample_fmt is not used. I guess it means it is always S16 switch(codec_context->sample_fmt) { - case SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_U8P: config->sample_width = 8; // beware unsigned! break; - case SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S16P: config->sample_width = 16; break; /* disabled because I don't know byte ordering @@ -132,10 +141,12 @@ static bool setAudioConfiguration(AudioConfiguration *config, AVCodecContext *co config->sample_width = 24; break; */ - case SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32P: config->sample_width = 32; break; - case SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLTP: config->sample_width = -32; break; default: @@ -152,58 +163,35 @@ bool FFMPEGDecoder::openFile() { // The following duplicates what av_open_input_file would normally do // url_fdopen - init_put_byte(&d->stream, d->file_buffer, FILE_BUFFER_SIZE, 0, d->src, akode_read, akode_write, akode_seek); - d->stream.is_streamed = !d->src->seekable(); - d->stream.max_packet_size = FILE_BUFFER_SIZE; - - { - // 2048 is PROBE_BUF_SIZE from libavformat/utils.c - AVProbeData pd; - uint8_t buf[2048]; - pd.filename = d->src->filename; - pd.buf = buf; - pd.buf_size = 0; - pd.buf_size = get_buffer(&d->stream, buf, 2048); - d->fmt = av_probe_input_format(&pd, 1); - // Seek back to 0 - // copied from url_fseek - long offset1 = 0 - (d->stream.pos - (d->stream.buf_end - d->stream.buffer)); - if (offset1 >= 0 && offset1 <= (d->stream.buf_end - d->stream.buffer)) { - /* can do the seek inside the buffer */ - d->stream.buf_ptr = d->stream.buffer + offset1; - } else { - if (!d->src->seek(0)) { - d->src->close(); - return false; - } else { - d->stream.pos = 0; - d->stream.buf_ptr = d->file_buffer; - d->stream.buf_end = d->file_buffer; - } - } + d->file_buffer = (unsigned char*)av_malloc(FILE_BUFFER_SIZE); + d->stream = avio_alloc_context(d->file_buffer, FILE_BUFFER_SIZE, 0, d->src, akode_read, akode_write, akode_seek); + if (!d->stream) { + return false; } - if (!d->fmt) { - std::cerr << "akode: FFMPEG: Format not found\n"; - closeFile(); + d->stream->seekable = d->src->seekable(); + d->stream->max_packet_size = FILE_BUFFER_SIZE; + d->ic = avformat_alloc_context(); + if (!d->ic) { return false; } + d->ic->pb = d->stream; - if (av_open_input_stream(&d->ic, &d->stream, d->src->filename, d->fmt, 0) != 0) + if (avformat_open_input(&d->ic, d->src->filename, NULL, NULL) != 0) { closeFile(); return false; } - av_find_stream_info( d->ic ); + avformat_find_stream_info( d->ic, NULL ); // Find the first a/v streams d->audioStream = -1; d->videoStream = -1; for (int i = 0; i < d->ic->nb_streams; i++) { - if (d->ic->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) + if (d->ic->streams[i]->CODECPAR->codec_type == AVMEDIA_TYPE_AUDIO) d->audioStream = i; else - if (d->ic->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) + if (d->ic->streams[i]->CODECPAR->codec_type == AVMEDIA_TYPE_VIDEO) d->videoStream = i; } if (d->audioStream == -1) @@ -214,6 +202,8 @@ bool FFMPEGDecoder::openFile() { return false; } + av_dump_format(d->ic, d->audioStream, d->src->filename, 0); + // Set config if (!setAudioConfiguration(&d->config, d->ic->streams[d->audioStream]->codec)) { @@ -221,13 +211,13 @@ bool FFMPEGDecoder::openFile() { return false; } - d->codec = avcodec_find_decoder(d->ic->streams[d->audioStream]->codec->codec_id); + d->codec = avcodec_find_decoder(d->ic->streams[d->audioStream]->CODECPAR->codec_id); if (!d->codec) { std::cerr << "akode: FFMPEG: Codec not found\n"; closeFile(); return false; } - avcodec_open( d->ic->streams[d->audioStream]->codec, d->codec ); + avcodec_open2( d->ic->streams[d->audioStream]->codec, d->codec, NULL ); double ffpos = (double)d->ic->streams[d->audioStream]->start_time / (double)AV_TIME_BASE; d->position = (long)(ffpos * d->config.sample_rate); @@ -236,8 +226,17 @@ bool FFMPEGDecoder::openFile() { } void FFMPEGDecoder::closeFile() { + if ( d->stream ) { + if (d->stream->buffer) + av_free(d->stream->buffer); +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 80, 100) + av_free(d->stream); +#else + avio_context_free(&d->stream); +#endif + } if( d->packetSize > 0 ) { - av_free_packet( &d->packet ); + av_packet_unref( &d->packet ); d->packetSize = 0; } @@ -248,7 +247,7 @@ void FFMPEGDecoder::closeFile() { if( d->ic ) { // make sure av_close_input_file doesn't actually close the file d->ic->iformat->flags = d->ic->iformat->flags | AVFMT_NOFILE; - av_close_input_file( d->ic ); + avformat_close_input( &d->ic ); d->ic = 0; } if (d->src) { @@ -260,7 +259,7 @@ bool FFMPEGDecoder::readPacket() { do { av_init_packet(&d->packet); if ( av_read_frame(d->ic, &d->packet) < 0 ) { - av_free_packet( &d->packet ); + av_packet_unref( &d->packet ); d->packetSize = 0; d->packetData = 0; return false; @@ -270,7 +269,7 @@ bool FFMPEGDecoder::readPacket() { d->packetData = d->packet.data; return true; } - av_free_packet(&d->packet); + av_packet_unref(&d->packet); } while (true); return false; @@ -286,11 +285,11 @@ static long demux(FFMPEGDecoder::private_data* d, AudioFrame* frame) { if (frame->sample_width == 8) offset = -128; // convert unsigned to signed // Demux into frame - T* buffer = (T*)d->buffer; + T** buffer = (T**)d->buffer; T** data = (T**)frame->data; for(int i=0; ipacket.stream_index == d->audioStream); retry: - int len = avcodec_decode_audio( d->ic->streams[d->audioStream]->codec, - (short*)d->buffer, &d->buffer_size, - d->packetData, d->packetSize ); + AVFrame *decodeFrame = av_frame_alloc(); + if (!decodeFrame) { + return false; + } + int decoded; + int len = avcodec_decode_audio4( d->ic->streams[d->audioStream]->codec, + decodeFrame, &decoded, + &d->packet ); +#if !defined(FFMPEG_AVFRAME_HAVE_PKT_SIZE) + d->packetSize = d->packet.size; +#else + d->packetSize = decodeFrame->pkt_size; +#endif + d->buffer = decodeFrame->data; +#if !defined(FFMPEG_AVFRAME_HAVE_CHANNELS) + d->buffer_size = decodeFrame->nb_samples * d->ic->streams[d->audioStream]->codec->channels * av_get_bytes_per_sample(d->ic->streams[d->audioStream]->codec->sample_fmt); +#else + d->buffer_size = decodeFrame->nb_samples * decodeFrame->channels * av_get_bytes_per_sample(d->ic->streams[d->audioStream]->codec->sample_fmt); +#endif if (len <= 0) { d->retries++; @@ -348,11 +363,12 @@ retry: default: assert(false); } + av_frame_free(&decodeFrame); if (length == 0) return readFrame(frame); - std::cout << "akode: FFMPEG: Frame length: " << length << "\n"; + // std::cout << "akode: FFMPEG: Frame length: " << length << "\n"; if( d->packetSize <= 0 ) - av_free_packet( &d->packet ); + av_packet_unref( &d->packet ); frame->pos = (d->position*1000)/d->config.sample_rate; d->position += length; diff --git a/config.h.cmake b/config.h.cmake index ea60b7d..29f226c 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -39,6 +39,10 @@ and RealAudio decoding) */ #cmakedefine HAVE_FFMPEG +/* Define if struct AVFrame have appropriate members */ +#cmakedefine FFMPEG_AVFRAME_HAVE_PKT_SIZE 1 +#cmakedefine FFMPEG_AVFRAME_HAVE_CHANNELS 1 + /* Define if you have libFLAC 1.1.3 or newer */ #cmakedefine HAVE_LIBFLAC113 -- cgit v1.2.1