本文對在使用ffmpeg進行音視頻編解碼時使用到的一些函數做一個簡單介紹,我當前使用的ffmpeg版本爲:0.8.5,因爲本人發現在不同的版本中,有些函數名稱會有點小改動,所以在此有必要說明下ffmpeg的版本號。
ffmpeg本人也是剛接觸,本文將採用累加的方法逐個介紹我使用到的函數,如有不妥之處,還望諒解!
頭文件引入方法:
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/mem.h"
#include "libavutil/fifo.h"
#include "libswscale/swscale.h"
};
/**
* Initialize libavcodec.
* If called more than once, does nothing.
*
* @warning This function must be called before any other libavcodec
* function.
*
* @warning This function is not thread-safe.
*/
void avcodec_init(void);
// 初始化libavcodec,一般最先調用該函數
// 引入頭文件: #include "libavcodec/avcodec.h"
// 實現在: \ffmpeg\libavcodec\utils.c
// 該函數必須在調用libavcodec裏的其它函數前調用,一般在程序啓動或模塊初始化時調用,如果你調用了多次也無所謂,因爲後面的調用不會做任何事情.從函數的實現裏你可以發現,代碼中對多次調用進行了控制.
// 該函數是非線程安全的
2 av_register_all()
/**
* Initialize libavformat and register all the muxers, demuxers and
* protocols. If you do not call this function, then you can select
* exactly which formats you want to support.
*
* @see av_register_input_format()
* @see av_register_output_format()
* @see av_register_protocol()
*/
void av_register_all(void);
// 初始化 libavformat和註冊所有的muxers、demuxers和protocols,
// 一般在調用avcodec_init後調用該方法
// 引入頭文件:#include "libavformat/avformat.h"
// 實現在:\ffmpeg\libavformat\allformats.c
// 其中會調用avcodec_register_all()註冊多種音視頻格式的編解碼器,並註冊各種文件的編解複用器
// 當然,你也可以不調用該函數,而通過選擇調用特定的方法來提供支持
3 avformat_alloc_context()
/**
* Allocate an AVFormatContext.
* avformat_free_context() can be used to free the context and everything
* allocated by the framework within it.
*/
AVFormatContext *avformat_alloc_context(void);
// 分配一個AVFormatContext結構
// 引入頭文件:#include "libavformat/avformat.h"
// 實現在:\ffmpeg\libavformat\options.c
// 其中負責申請一個AVFormatContext結構的內存,並進行簡單初始化
// avformat_free_context()可以用來釋放該結構裏的所有東西以及該結構本身
// 也是就說使用 avformat_alloc_context()分配的結構,需要使用avformat_free_context()來釋放
// 有些版本中函數名可能爲: av_alloc_format_context();
4 avformat_free_context()
/**
* Free an AVFormatContext and all its streams.
* @param s context to free
*/
void avformat_free_context(AVFormatContext *s);
// 釋放一個AVFormatContext結構
// 引入頭文件:#include "libavformat/avformat.h"
// 實現在:\ffmpeg\libavformat\utils.c
// 使用 avformat_alloc_context()分配的結構,採用該函數進行釋放,除釋放AVFormatContext結構本身內存之外,AVFormatContext中指針所指向的內存也會一併釋放
// 有些版本中函數名猜測可能爲: av_free_format_context();
5 AVFormatContext 結構
/**
* Format I/O context.
* New fields can be added to the end with minor version bumps.
* Removal, reordering and changes to existing fields require a major
* version bump.
* sizeof(AVFormatContext) must not be used outside libav*.
*/
typedef struct AVFormatContext {
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
AVIOContext *pb;
unsigned int nb_streams;
AVStream **streams;
char filename[1024]; /**< input or output filename */
....
} AVFormatContext;
// AVFormatContext在FFMpeg裏是一個非常重要的的結構,是其它輸入、輸出相關信息的一個容器
// 引入頭文件:#include "libavformat/avformat.h"
// 以上只列出了其中的部分成員
// 作爲輸入容器時 struct AVInputFormat *iformat; 不能爲空, 其中包含了輸入文件的音視頻流信息,程序從輸入容器從讀出音視頻包進行解碼處理
// 作爲輸出容器時 struct AVOutputFormat *oformat; 不能爲空, 程序把編碼好的音視頻包寫入到輸出容器中
// AVIOContext *pb: I/O上下文,通過對該變量賦值可以改變輸入源或輸出目的
// unsigned int nb_streams; 音視頻流數量
// AVStream **streams; 音視頻流
6 AVIOContext 結構
/**
* Bytestream IO Context.
* New fields can be added to the end with minor version bumps.
* Removal, reordering and changes to existing fields require a major
* version bump.
* sizeof(AVIOContext) must not be used outside libav*.
*
* @note None of the function pointers in AVIOContext should be called
* directly, they should only be set by the client application
* when implementing custom I/O. Normally these are set to the
* function pointers specified in avio_alloc_context()
*/
typedef struct {
unsigned char *buffer; /**< Start of the buffer. */
int buffer_size; /**< Maximum buffer size */
unsigned char *buf_ptr; /**< Current position in the buffer */
unsigned char *buf_end; /**< End of the data, may be less than
buffer+buffer_size if the read function returned
less data than requested, e.g. for streams where
no more data has been received yet. */
void *opaque; /**< A private pointer, passed to the read/write/seek/...
functions. */
int (*read_packet)(void *opaque, uint8_t *buf,int buf_size);
int (*write_packet)(void *opaque, uint8_t *buf,int buf_size);
int64_t (*seek)(void *opaque, int64_t offset,int whence);
int64_t pos; /**< position in the file of the current buffer */
int must_flush; /**< true if the next seek should flush */
int eof_reached; /**< true if eof reached */
int write_flag; /**< true if open for writing */
#if FF_API_OLD_AVIO
attribute_deprecated int is_streamed;
#endif
int max_packet_size;
unsigned long checksum;
unsigned char *checksum_ptr;
unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
int error; /**< contains the error code or 0 if no error happened */
/**
* Pause or resume playback for network streaming protocols - e.g. MMS.
*/
int (*read_pause)(void *opaque,int pause);
/**
* Seek to a given timestamp in stream with the specified stream_index.
* Needed for some network streaming protocols which don't support seeking
* to byte position.
*/
int64_t (*read_seek)(void *opaque,int stream_index,
int64_t timestamp, int flags);
/**
* A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
*/
int seekable;
} AVIOContext;
// 字節流 I/O 上下文
// 在結構的尾部增加變量可以減少版本衝突
// 移除、排序和修改已經存在的變量將會導致較大的版本衝突
// sizeof(AVIOContext)在libav*.外部不可使用
// AVIOContext裏的函數指針不能直接調用,通常使用avio_alloc_context()函數來設置其中的函數指針
// unsigned char *buffer: 緩存的起始指針
// int buffer_size: 緩存的最大值
// void *opaque: 在回調函數中使用的指針
// int (*read_packet)(void *opaque, uint8_t *buf,int buf_size): 讀文件回調方法
// int (*write_packet)(void *opaque, uint8_t *buf,int buf_size): 寫文件回調方法
// int64_t (*seek)(void *opaque, int64_t offset,int whence): seek文件回調方法
7 avio_alloc_context()
/**
* Allocate and initialize an AVIOContext for buffered I/O. It must be later
* freed with av_free().
*
* @param buffer Memory block for input/output operations via AVIOContext.
* The buffer must be allocated with av_malloc() and friends.
* @param buffer_size The buffer size is very important for performance.
* For protocols with fixed blocksize it should be set to this blocksize.
* For others a typical size is a cache page, e.g. 4kb.
* @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
* @param opaque An opaque pointer to user-specific data.
* @param read_packet A function for refilling the buffer, may be NULL.
* @param write_packet A function for writing the buffer contents, may be NULL.
* @param seek A function for seeking to specified byte position, may be NULL.
*
* @return Allocated AVIOContext or NULL on failure.
*/
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf,int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf,int buf_size),
int64_t (*seek)(void *opaque, int64_t offset,int whence));
// 爲I/0緩存申請並初始化一個AVIOContext結構,結束使用時必須使用av_free()進行釋放
// unsigned char *buffer: 輸入/輸出緩存內存塊,必須是使用av_malloc()分配的
// int buffer_size: 緩存大小是非常重要的
// int write_flag: 如果緩存爲可寫則設置爲1,否則設置爲0
// void *opaque: 指針,用於回調時使用
// int (*read_packet): 讀包函數指針
// int (*write_packet): 寫包函數指針
// int64_t (*seek): seek文件函數指針
8 av_open_input_file()
/**
* Open a media file as input. The codecs are not opened. Only the file
* header (if present) is read.
*
* @param ic_ptr The opened media file handle is put here.
* @param filename filename to open
* @param fmt If non-NULL, force the file format to use.
* @param buf_size optional buffer size (zero if default is OK)
* @param ap Additional parameters needed when opening the file
* (NULL if default).
* @return 0 if OK, AVERROR_xxx otherwise
*
* @deprecated use avformat_open_input instead.
*/
attribute_deprecated int av_open_input_file(AVFormatContext **ic_ptr,constchar *filename,
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap);
// 以輸入方式打開一個媒體文件,也即源文件,codecs並沒有打開,只讀取了文件的頭信息.
// 引入頭文件:#include "libavformat/avformat.h"
// AVFormatContext **ic_ptr 輸入文件容器
//constchar *filename 輸入文件名,全路徑,並且保證文件存在
// AVInputFormat *fmt 輸入文件格式,填NULL即可
//int buf_size,緩衝區大小,直接填0即可
// AVFormatParameters *ap, 格式參數,添NULL即可
// 成功返回0,其它失敗
// 不贊成使用 avformat_open_input 代替
9 av_close_input_file()
/**
* @deprecated use avformat_close_input()
* Close a media file (but not its codecs).
* @param s media file handle
*/
void av_close_input_file(AVFormatContext *s);
// 關閉使用avformat_close_input()打開的輸入文件容器,但並不關係它的codecs
// 引入頭文件:#include "libavformat/avformat.h"
// 使用av_open_input_file 打開的文件容器,可以使用該函數關閉
// 使用 av_close_input_file 關閉後,就不再需要使用avformat_free_context 進行釋放了
10 av_find_stream_info()
/**
* Read packets of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
* function also computes the real framerate in case of MPEG-2 repeat
* frame mode.
* The logical file position is not changed by this function;
* examined packets may be buffered for later processing.
*
* @param ic media file handle
* @return >=0 if OK, AVERROR_xxx on error
* @todo Let the user decide somehow what information is needed so that
* we do not waste time getting stuff the user does not need.
*/
int av_find_stream_info(AVFormatContext *ic);
// 通過讀取媒體文件的中的包來獲取媒體文件中的流信息,對於沒有頭信息的文件如(mpeg)是非常有用的,
// 該函數通常重算類似mpeg-2幀模式的真實幀率,該函數並未改變邏輯文件的position.
// 引入頭文件:#include "libavformat/avformat.h"
// 也就是把媒體文件中的音視頻流等信息讀出來,保存在容器中,以便解碼時使用
// 返回>=0時成功,否則失敗
/***********************************************************/
/**
* Find a registered decoder with a matching codec ID.
*
* @param id CodecID of the requested decoder
* @return A decoder if one was found, NULL otherwise.
*/
AVCodec *avcodec_find_decoder(enum CodecID id);
// 通過code ID查找一個已經註冊的音視頻解碼器
// 引入 #include "libavcodec/avcodec.h"
// 實現在: \ffmpeg\libavcodec\utils.c
// 查找解碼器之前,必須先調用av_register_all註冊所有支持的解碼器
// 查找成功返回解碼器指針,否則返回NULL
// 音視頻解碼器保存在一個鏈表中,查找過程中,函數從頭到尾遍歷鏈表,通過比較解碼器的ID來查找
2 avcodec_find_decoder_by_name()
/**
* Find a registered decoder with the specified name.
*
* @param name name of the requested decoder
* @return A decoder if one was found, NULL otherwise.
*/
AVCodec *avcodec_find_decoder_by_name(constchar *name);
// 通過一個指定的名稱查找一個已經註冊的音視頻解碼器
// 引入 #include "libavcodec/avcodec.h"
// 實現在: \ffmpeg\libavcodec\utils.c
// 查找解碼器之前,必須先調用av_register_all註冊所有支持的解碼器
// 查找成功返回解碼器指針,否則返回NULL
// 音視頻解碼器保存在一個鏈表中,查找過程中,函數從頭到尾遍歷鏈表,通過比較解碼器的name來查找
3 avcodec_find_encoder()
/**
* Find a registered encoder with a matching codec ID.
*
* @param id CodecID of the requested encoder
* @return An encoder if one was found, NULL otherwise.
*/
AVCodec *avcodec_find_encoder(enum CodecID id);
// 通過code ID查找一個已經註冊的音視頻編碼器
// 引入 #include "libavcodec/avcodec.h"
// 查找編碼器之前,必須先調用av_register_all註冊所有支持的編碼器
// 查找成功返回編碼器指針,否則返回NULL
// 音視頻編碼器保存在一個鏈表中,查找過程中,函數從頭到尾遍歷鏈表,通過比較編碼器的ID來查找
/**
* Find a registered encoder with the specified name.
*
* @param name name of the requested encoder
* @return An encoder if one was found, NULL otherwise.
*/
AVCodec *avcodec_find_encoder_by_name(constchar *name);
// 通過一個指定的名稱查找一個已經註冊的音視頻編碼器
// 引入 #include "libavcodec/avcodec.h"
// 查找編碼器之前,必須先調用av_register_all註冊所有支持的編碼器
// 查找成功返回編碼器指針,否則返回NULL
// 音視頻編碼器保存在一個鏈表中,查找過程中,函數從頭到尾遍歷鏈表,通過比較編碼器的名稱來查找
/**
* Initialize the AVCodecContext to use the given AVCodec. Prior to using this
* function the context has to be allocated.
*
* The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
* avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for
* retrieving a codec.
*
* @warning This function is not thread safe!
*
* @code
* avcodec_register_all();
* codec = avcodec_find_decoder(CODEC_ID_H264);
* if (!codec)
* exit(1);
*
* context = avcodec_alloc_context();
*
* if (avcodec_open(context, codec) < 0)
* exit(1);
* @endcode
*
* @param avctx The context which will be set up to use the given codec.
* @param codec The codec to use within the context.
* @return zero on success, a negative value on error
* @see avcodec_alloc_context, avcodec_find_decoder, avcodec_find_encoder, avcodec_close
*/
int avcodec_open(AVCodecContext *avctx, AVCodec *codec);
// 使用給定的AVCodec初始化AVCodecContext
// 引入#include "libavcodec/avcodec.h"
// 方法: avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), avcodec_find_decoder() and avcodec_find_encoder() 提供了快速獲取一個codec的途徑
// 該方法在編碼和解碼時都會用到
// 返回0時成功,打開作爲輸出時,參數設置不對的話,調用會失敗
/**
* Return the output format in the list of registered output formats
* which best matches the provided parameters, or return NULL if
* there is no match.
*
* @param short_name if non-NULL checks if short_name matches with the
* names of the registered formats
* @param filename if non-NULL checks if filename terminates with the
* extensions of the registered formats
* @param mime_type if non-NULL checks if mime_type matches with the
* MIME type of the registered formats
*/
AVOutputFormat *av_guess_format(constchar *short_name,
constchar *filename,
constchar *mime_type);
// 返回一個已經註冊的最合適的輸出格式
// 引入#include "libavformat/avformat.h"
// 可以通過 const char *short_name 獲取,如"mpeg"
// 也可以通過 const char *filename 獲取,如"E:\a.mp4"
7 av_new_stream()
/**
* Add a new stream to a media file.
*
* Can only be called in the read_header() function. If the flag
* AVFMTCTX_NOHEADER is in the format context, then new streams
* can be added in read_packet too.
*
* @param s media file handle
* @param id file-format-dependent stream ID
*/
AVStream *av_new_stream(AVFormatContext *s, int id);
// 爲媒體文件添加一個流,一般爲作爲輸出的媒體文件容器添加音視頻流
// 引入 #include "libavformat/avformat.h"
// 再打開源文件時用戶一般不需要直接調用該方法
#if FF_API_DUMP_FORMAT
/**
* @deprecated Deprecated in favor of av_dump_format().
*/
attribute_deprecated void dump_format(AVFormatContext *ic,
int index,
constchar *url,
int is_output);
#endif
#if FF_API_FORMAT_PARAMETERS
/**
* @deprecated pass the options to avformat_write_header directly.
*/
attribute_deprecated int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap);
#endif#if FF_API_FORMAT_PARAMETERS
/**
* Allocate the stream private data and write the stream header to an
* output media file.
* @note: this sets stream time-bases, if possible to stream->codec->time_base
* but for some formats it might also be some other time base
*
* @param s media file handle
* @return 0 if OK, AVERROR_xxx on error
*
* @deprecated use avformat_write_header.
*/
attribute_deprecated int av_write_header(AVFormatContext *s);
#endif
/**********************************************************/
typedef struct AVPacket {
/**
* Presentation timestamp in AVStream->time_base units; the time at which
* the decompressed packet will be presented to the user.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
* pts MUST be larger or equal to dts as presentation cannot happen before
* decompression, unless one wants to view hex dumps. Some formats misuse
* the terms dts and pts/cts to mean something different. Such timestamps
* must be converted to true pts/dts before they are stored in AVPacket.
*/
int64_t pts;
/**
* Decompression timestamp in AVStream->time_base units; the time at which
* the packet is decompressed.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
*/
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
int duration;
.
.
.
} AVPacket
/**
* Initialize optional fields of a packet with default values.
*
* @param pkt packet
*/
void av_init_packet(AVPacket *pkt);
/**
* Free a packet.
*
* @param pkt packet to free
*/
void av_free_packet(AVPacket *pkt);
/**
* Return the next frame of a stream.
* This function returns what is stored in the file, and does not validate
* that what is there are valid frames for the decoder. It will split what is
* stored in the file into frames and return one for each call. It will not
* omit invalid data between valid frames so as to give the decoder the maximum
* information possible for decoding.
*
* The returned packet is valid
* until the next av_read_frame() or until av_close_input_file() and
* must be freed with av_free_packet. For video, the packet contains
* exactly one frame. For audio, it contains an integer number of
* frames if each frame has a known fixed size (e.g. PCM or ADPCM
* data). If the audio frames have a variable size (e.g. MPEG audio),
* then it contains one frame.
*
* pkt->pts, pkt->dts and pkt->duration are always set to correct
* values in AVStream.time_base units (and guessed if the format cannot
* provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
* has B-frames, so it is better to rely on pkt->dts if you do not
* decompress the payload.
*
* @return 0 if OK, < 0 on error or end of file
*/
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
// 從輸入源文件容器中讀取一個AVPacket數據包
// 該函數讀出的包並不每次都是有效的,對於讀出的包我們都應該進行相應的解碼(視頻解碼/音頻解碼),
/**
* Decode the video frame of size avpkt->size from avpkt->data into picture.
* Some decoders may support multiple frames in a single AVPacket, such
* decoders would then just decode the first frame.
*
* @warning The input buffer must be FF_INPUT_BUFFER_PADDING_SIZE larger than
* the actual read bytes because some optimized bitstream readers read 32 or 64
* bits at once and could read over the end.
*
* @warning The end of the input buffer buf should be set to 0 to ensure that
* no overreading happens for damaged MPEG streams.
*
* @note You might have to align the input buffer avpkt->data.
* The alignment requirements depend on the CPU: on some CPUs it isn't
* necessary at all, on others it won't work at all if not aligned and on others
* it will work but it will have an impact on performance.
*
* In practice, avpkt->data should have 4 byte alignment at minimum.
*
* @note Some codecs have a delay between input and output, these need to be
* fed with avpkt->data=NULL, avpkt->size=0 at the end to return the remaining frames.
*
* @param avctx the codec context
* @param[out] picture The AVFrame in which the decoded video frame will be stored.
* Use avcodec_alloc_frame to get an AVFrame, the codec will
* allocate memory for the actual bitmap.
* with default get/release_buffer(), the decoder frees/reuses the bitmap as it sees fit.
* with overridden get/release_buffer() (needs CODEC_CAP_DR1) the user decides into what buffer the decoder
* decodes and the decoder tells the user once it does not need the data anymore,
* the user app can at this point free/reuse/keep the memory as it sees fit.
*
* @param[in] avpkt The input AVpacket containing the input buffer.
* You can create such packet with av_init_packet() and by then setting
* data and size, some decoders might in addition need other fields like
* flags&AV_PKT_FLAG_KEY. All decoders are designed to use the least
* fields possible.
* @param[in,out] got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero.
* @return On error a negative value is returned, otherwise the number of bytes
* used or zero if no frame could be decompressed.
*/
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
AVPacket *avpkt);
// vPacket.data += vLen;
/**
* Decode the audio frame of size avpkt->size from avpkt->data into samples.
* Some decoders may support multiple frames in a single AVPacket, such
* decoders would then just decode the first frame. In this case,
* avcodec_decode_audio3 has to be called again with an AVPacket that contains
* the remaining data in order to decode the second frame etc.
* If no frame
* could be outputted, frame_size_ptr is zero. Otherwise, it is the
* decompressed frame size in bytes.
*
* @warning You must set frame_size_ptr to the allocated size of the
* output buffer before calling avcodec_decode_audio3().
*
* @warning The input buffer must be FF_INPUT_BUFFER_PADDING_SIZE larger than
* the actual read bytes because some optimized bitstream readers read 32 or 64
* bits at once and could read over the end.
*
* @warning The end of the input buffer avpkt->data should be set to 0 to ensure that
* no overreading happens for damaged MPEG streams.
*
* @note You might have to align the input buffer avpkt->data and output buffer
* samples. The alignment requirements depend on the CPU: On some CPUs it isn't
* necessary at all, on others it won't work at all if not aligned and on others
* it will work but it will have an impact on performance.
*
* In practice, avpkt->data should have 4 byte alignment at minimum and
* samples should be 16 byte aligned unless the CPU doesn't need it
* (AltiVec and SSE do).
*
* @param avctx the codec context
* @param[out] samples the output buffer, sample type in avctx->sample_fmt
* @param[in,out] frame_size_ptr the output buffer size in bytes
* @param[in] avpkt The input AVPacket containing the input buffer.
* You can create such packet with av_init_packet() and by then setting
* data and size, some decoders might in addition need other fields.
* All decoders are designed to use the least fields possible though.
* @return On error a negative value is returned, otherwise the number of bytes
* used or zero if no frame data was decompressed (used) from the input AVPacket.
*/
int avcodec_decode_audio3(AVCodecContext *avctx, int16_t *samples,
int *frame_size_ptr,
AVPacket *avpkt);
// vPacket.data += vLen;