1.文章介紹
本文為<<FFMPEG學(xué)習(xí)教程系列>>第二篇跷跪,上一篇文章分析了FFMEPG的編譯方法熟呛,對(duì)FFMEPG的configure和makefile做了一個(gè)全局觀的分析九府,本文在此基礎(chǔ)上開始分析FFMEPG的API實(shí)現(xiàn)音視頻編解碼的相關(guān)流程璧诵。
2.干貨
1.源碼對(duì)應(yīng)版本:
ffmpeg version:3.2.1
main函數(shù)分析
/**ffmpeg.c*/
int main(int argc, char **argv)
{
int i, ret;
int64_t ti;
init_dynload();
register_exit(ffmpeg_cleanup);
setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
av_log_set_flags(AV_LOG_SKIP_REPEATED);
parse_loglevel(argc, argv, options);
if(argc>1 && !strcmp(argv[1], "-d")){
run_as_daemon=1;
av_log_set_callback(log_callback_null);
argc--;
argv++;
}
avcodec_register_all();
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
avfilter_register_all();
av_register_all();
avformat_network_init();
show_banner(argc, argv, options);
/* parse options and open all input/output files */
ret = ffmpeg_parse_options(argc, argv);
if (ret < 0)
exit_program(1);
if (nb_output_files <= 0 && nb_input_files == 0) {
show_usage();
av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
exit_program(1);
}
/* file converter / grab */
if (nb_output_files <= 0) {
av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
exit_program(1);
}
// if (nb_input_files == 0) {
// av_log(NULL, AV_LOG_FATAL, "At least one input file must be specified\n");
// exit_program(1);
// }
for (i = 0; i < nb_output_files; i++) {
if (strcmp(output_files[i]->ctx->oformat->name, "rtp"))
want_sdp = 0;
}
current_time = ti = getutime();
if (transcode() < 0)
exit_program(1);
ti = getutime() - ti;
if (do_benchmark) {
av_log(NULL, AV_LOG_INFO, "bench: utime=%0.3fs\n", ti / 1000000.0);
}
av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n",
decode_error_stat[0], decode_error_stat[1]);
if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
exit_program(69);
exit_program(received_nb_signals ? 255 : main_return_code);
return main_return_code;
}
2.avcodec_register_all()
(a) REGISTER_HWACCEL(X, x)
REGISTER_HWACCEL(H263_CUVID, h263_cuvid);
(b) REGISTER_ENCODER(X, x)
REGISTER_ENCODER(A64MULTI, a64multi);
(c) REGISTER_DECODER(X, x)
REGISTER_DECODER(AASC, aasc);
(d) REGISTER_ENCDEC(X, x)
REGISTER_ENCDEC (AMV, amv);
(e) REGISTER_PARSER(X, x)
REGISTER_PARSER(AAC, aac);
/**libavcodec/allcodecs.c*/
#define REGISTER_HWACCEL(X, x) \
{ \
extern AVHWAccel ff_##x##_hwaccel; \
if (CONFIG_##X##_HWACCEL) \
av_register_hwaccel(&ff_##x##_hwaccel); \
}
#define REGISTER_ENCODER(X, x) \
{ \
extern AVCodec ff_##x##_encoder; \
if (CONFIG_##X##_ENCODER) \
avcodec_register(&ff_##x##_encoder); \
}
#define REGISTER_DECODER(X, x) \
{ \
extern AVCodec ff_##x##_decoder; \
if (CONFIG_##X##_DECODER) \
avcodec_register(&ff_##x##_decoder); \
}
#define REGISTER_ENCDEC(X, x) REGISTER_ENCODER(X, x); REGISTER_DECODER(X, x)
#define REGISTER_PARSER(X, x) \
{ \
extern AVCodecParser ff_##x##_parser; \
if (CONFIG_##X##_PARSER) \
av_register_codec_parser(&ff_##x##_parser); \
}
可以看到實(shí)際上是av_register_hwaccel莱预,avcodec_register州疾,av_register_codec_parser的調(diào)用
/**libavcodec/utils.c*/
void av_register_hwaccel(AVHWAccel *hwaccel)
{
AVHWAccel **p = last_hwaccel;
hwaccel->next = NULL;
while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, hwaccel))
p = &(*p)->next;
last_hwaccel = &hwaccel->next;
}
av_cold void avcodec_register(AVCodec *codec)
{
AVCodec **p;
avcodec_init();
p = last_avcodec;
codec->next = NULL;
while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, codec))
p = &(*p)->next;
last_avcodec = &codec->next;
if (codec->init_static_data)
codec->init_static_data(codec);
}
static av_cold void avcodec_init(void)
{
static int initialized = 0;
if (initialized != 0)
return;
initialized = 1;
if (CONFIG_ME_CMP)
ff_me_cmp_init_static();
}
/**libavcodec/parser.c*/
void av_register_codec_parser(AVCodecParser *parser)
{
do {
parser->next = av_first_parser;
} while (parser->next != avpriv_atomic_ptr_cas((void * volatile *)&av_first_parser, parser->next, parser));
}
代碼不難懂严蓖,也就是對(duì)應(yīng)加入到AVHWAccel薄嫡,AVCodec,AVCodecParser鏈表的尾部
3.avfilter_register_all():
(a) REGISTER_FILTER(X, x, y)
REGISTER_FILTER(ABENCH, abench, af);
(b) REGISTER_FILTER_UNCONDITIONAL(x)
REGISTER_FILTER_UNCONDITIONAL(asrc_abuffer);
/**libavfilter/allfilters.c*/
#define REGISTER_FILTER(X, x, y) \
{ \
extern AVFilter ff_##y##_##x; \
if (CONFIG_##X##_FILTER) \
avfilter_register(&ff_##y##_##x); \
}
#define REGISTER_FILTER_UNCONDITIONAL(x) \
{ \
extern AVFilter ff_##x; \
avfilter_register(&ff_##x); \
}
實(shí)際上是對(duì)avfilter_register的調(diào)用:
/**libavfilter/avfilter.c*/
int avfilter_register(AVFilter *filter)
{
AVFilter **f = last_filter;
/* the filter must select generic or internal exclusively */
av_assert0((filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE) != AVFILTER_FLAG_SUPPORT_TIMELINE);
filter->next = NULL;
while(*f || avpriv_atomic_ptr_cas((void * volatile *)f, NULL, filter))
f = &(*f)->next;
last_filter = &filter->next;
return 0;
}
就是把filter加到AVFilter鏈表尾部颗胡。
4.av_register_all()
(a) REGISTER_MUXER(X, x)
REGISTER_MUXER (ADTS, adts);
(b) REGISTER_DEMUXER(X, x)
REGISTER_DEMUXER (AAC, aac);
(b)REGISTER_MUXDEMUX(X, x)
REGISTER_MUXDEMUX(AC3, ac3);
/**libavformat/allformats.c*/
#define REGISTER_MUXER(X, x) \
{ \
extern AVOutputFormat ff_##x##_muxer; \
if (CONFIG_##X##_MUXER) \
av_register_output_format(&ff_##x##_muxer); \
}
#define REGISTER_DEMUXER(X, x) \
{ \
extern AVInputFormat ff_##x##_demuxer; \
if (CONFIG_##X##_DEMUXER) \
av_register_input_format(&ff_##x##_demuxer); \
}
#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)
實(shí)際上是對(duì)av_register_input_format酣栈,av_register_output_format的調(diào)用.
/**libavformat/format.c*/
void av_register_input_format(AVInputFormat *format)
{
AVInputFormat **p = last_iformat;
// Note, format could be added after the first 2 checks but that implies that *p is no longer NULL
while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
p = &(*p)->next;
if (!format->next)
last_iformat = &format->next;
}
void av_register_output_format(AVOutputFormat *format)
{
AVOutputFormat **p = last_oformat;
// Note, format could be added after the first 2 checks but that implies that *p is no longer NULL
while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
p = &(*p)->next;
if (!format->next)
last_oformat = &format->next;
}
也是把對(duì)應(yīng)的format加到AVInputFormat琉闪,AVOutputFormat鏈表的尾部厅克。
了解FFMPEG API最好的方式就是參考API DEMO了:
源碼位置位于doc/examples/,比如文件decoding_encoding.c:
/**
* @file
* libavcodec API use example.
*
* @example decoding_encoding.c
* Note that libavcodec only handles codecs (MPEG, MPEG-4, etc...),
* not file formats (AVI, VOB, MP4, MOV, MKV, MXF, FLV, MPEG-TS, MPEG-PS, etc...).
* See library 'libavformat' for the format handling
*/
很貼心的API吧凤藏,可以根據(jù)根據(jù)這個(gè)API DEMO來(lái)逐步分析FFMPEG各個(gè)模塊望迎,不然這么大的FFMPEG代碼要看完也是頭疼贬蛙。
那么來(lái)看看libavcodec都能做些什么:
/**doc/decoding_encoding.c*/
int main(int argc, char **argv)
{
const char *output_type;
/* register all the codecs */
avcodec_register_all();
if (argc < 2) {
printf("usage: %s output_type\n"
"API example program to decode/encode a media stream with libavcodec.\n"
"This program generates a synthetic stream and encodes it to a file\n"
"named test.h264, test.mp2 or test.mpg depending on output_type.\n"
"The encoded stream is then decoded and written to a raw data output.\n"
"output_type must be chosen between 'h264', 'mp2', 'mpg'.\n",
argv[0]);
return 1;
}
output_type = argv[1];
if (!strcmp(output_type, "h264")) {
video_encode_example("test.h264", AV_CODEC_ID_H264);
} else if (!strcmp(output_type, "mp2")) {
audio_encode_example("test.mp2");
audio_decode_example("test.pcm", "test.mp2");
} else if (!strcmp(output_type, "mpg")) {
video_encode_example("test.mpg", AV_CODEC_ID_MPEG1VIDEO);
video_decode_example("test%02d.pgm", "test.mpg");
} else {
fprintf(stderr, "Invalid output type '%s', choose between 'h264', 'mp2', or 'mpg'\n",
output_type);
return 1;
}
return 0;
}
avcodec_register_all()這個(gè)函數(shù)上文已經(jīng)分析了吻贿,這里來(lái)看是如何解碼的:
static void video_decode_example(const char *outfilename, const char *filename)
{
AVCodec *codec;
AVCodecContext *c= NULL;
int frame_count;
FILE *f;
AVFrame *frame;
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
AVPacket avpkt;
av_init_packet(&avpkt);
/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
printf("Decode video file %s to %s\n", filename, outfilename);
/* find the MPEG-1 video decoder */
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
if (codec->capabilities & AV_CODEC_CAP_TRUNCATED)
c->flags |= AV_CODEC_FLAG_TRUNCATED; // we do not send complete frames
/* For some codecs, such as msmpeg4 and mpeg4, width and height
MUST be initialized there because this information is not
available in the bitstream. */
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame_count = 0;
for (;;) {
avpkt.size = fread(inbuf, 1, INBUF_SIZE, f);
if (avpkt.size == 0)
break;
/* NOTE1: some codecs are stream based (mpegvideo, mpegaudio)
and this is the only method to use them because you cannot
know the compressed data size before analysing it.
BUT some other codecs (msmpeg4, mpeg4) are inherently frame
based, so you must call them with all the data for one
frame exactly. You must also initialize 'width' and
'height' before initializing them. */
/* NOTE2: some codecs allow the raw parameters (frame size,
sample rate) to be changed at any frame. We handle this, so
you should also take care of it */
/* here, we use a stream based decoder (mpeg1video), so we
feed decoder and see if it could decode a frame */
avpkt.data = inbuf;
while (avpkt.size > 0)
if (decode_write_frame(outfilename, c, frame, &frame_count, &avpkt, 0) < 0)
exit(1);
}
/* Some codecs, such as MPEG, transmit the I- and P-frame with a
latency of one frame. You must do the following to have a
chance to get the last frame of the video. */
avpkt.data = NULL;
avpkt.size = 0;
decode_write_frame(outfilename, c, frame, &frame_count, &avpkt, 1);
fclose(f);
avcodec_close(c);
av_free(c);
av_frame_free(&frame);
printf("\n");
}
在這里涉及到AVCodec措译,AVCodecContext别凤,AVFrame,AVPacket 這幾個(gè)結(jié)構(gòu)體领虹,在FFMPEG編解碼編程中规哪,這幾個(gè)結(jié)構(gòu)體使用的頻率相對(duì)較高:
/**libavcodec/avcodec.h*/
/**
* main external API structure.
* New fields can be added to the end with minor version bumps.
* Removal, reordering and changes to existing fields require a major
* version bump.
* Please use AVOptions (av_opt* / av_set/get*()) to access these fields from user
* applications.
* The name string for AVOptions options matches the associated command line
* parameter name and can be found in libavcodec/options_table.h
* The AVOption/command line parameter names differ in some cases from the C
* structure field names for historic reasons or brevity.
* sizeof(AVCodecContext) must not be used outside libav*.
*/
typedef struct AVCodecContext {
/**
* information on struct for av_log
* - set by avcodec_alloc_context3
*/
const AVClass *av_class;
int log_level_offset;
enum AVMediaType codec_type; /* see AVMEDIA_TYPE_xxx */
const struct AVCodec *codec;
#if FF_API_CODEC_NAME
/**
* @deprecated this field is not used for anything in libavcodec
*/
attribute_deprecated
char codec_name[32];
#endif
enum AVCodecID codec_id; /* see AV_CODEC_ID_xxx */
//...
//省略后面的代碼,關(guān)于AVCodecContext結(jié)構(gòu)體的參數(shù)就有快2000行,請(qǐng)讀者自行查看源碼
}
/**
* AVCodec.
*/
typedef struct AVCodec {
/**
* Name of the codec implementation.
* The name is globally unique among encoders and among decoders (but an
* encoder and a decoder can share the same name).
* This is the primary way to find a codec from the user perspective.
*/
const char *name;
/**
* Descriptive name for the codec, meant to be more human readable than name.
* You should use the NULL_IF_CONFIG_SMALL() macro to define it.
*/
const char *long_name;
enum AVMediaType type;
enum AVCodecID id;
/**
* Codec capabilities.
* see AV_CODEC_CAP_*
*/
int capabilities;
const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0
uint8_t max_lowres; ///< maximum value for lowres supported by the decoder, no direct access, use av_codec_get_max_lowres()
const AVClass *priv_class; ///< AVClass for the private context
const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
/*****************************************************************
* No fields below this line are part of the public API. They
* may not be used outside of libavcodec and can be changed and
* removed at will.
* New public fields should be added right above.
*****************************************************************
*/
int priv_data_size;
struct AVCodec *next;
/**
* @name Frame-level threading support functions
* @{
*/
/**
* If defined, called on thread contexts when they are created.
* If the codec allocates writable tables in init(), re-allocate them here.
* priv_data will be set to a copy of the original.
*/
int (*init_thread_copy)(AVCodecContext *);
/**
* Copy necessary context variables from a previous thread context to the current one.
* If not defined, the next thread will start automatically; otherwise, the codec
* must call ff_thread_finish_setup().
*
* dst and src will (rarely) point to the same context, in which case memcpy should be skipped.
*/
int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src);
/** @} */
/**
* Private codec-specific defaults.
*/
const AVCodecDefault *defaults;
/**
* Initialize codec static data, called from avcodec_register().
*/
void (*init_static_data)(struct AVCodec *codec);
int (*init)(AVCodecContext *);
int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size,
const struct AVSubtitle *sub);
/**
* Encode data to an AVPacket.
*
* @param avctx codec context
* @param avpkt output AVPacket (may contain a user-provided buffer)
* @param[in] frame AVFrame containing the raw data to be encoded
* @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a
* non-empty packet was returned in avpkt.
* @return 0 on success, negative error code on failure
*/
int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame,
int *got_packet_ptr);
int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
int (*close)(AVCodecContext *);
/**
* Decode/encode API with decoupled packet/frame dataflow. The API is the
* same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except
* that:
* - never called if the codec is closed or the wrong type,
* - AVPacket parameter change side data is applied right before calling
* AVCodec->send_packet,
* - if AV_CODEC_CAP_DELAY is not set, drain packets or frames are never sent,
* - only one drain packet is ever passed down (until the next flush()),
* - a drain AVPacket is always NULL (no need to check for avpkt->size).
*/
int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);
int (*send_packet)(AVCodecContext *avctx, const AVPacket *avpkt);
int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);
/**
* Flush buffers.
* Will be called when seeking
*/
void (*flush)(AVCodecContext *);
/**
* Internal codec capabilities.
* See FF_CODEC_CAP_* in internal.h
*/
int caps_internal;
} AVCodec;
typedef struct AVPacket {
/**
* A reference to the reference-counted buffer where the packet data is
* stored.
* May be NULL, then the packet data is not reference-counted.
*/
AVBufferRef *buf;
/**
* 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;
/**
* A combination of AV_PKT_FLAG values
*/
int flags;
/**
* Additional packet data that can be provided by the container.
* Packet can contain several types of side information.
*/
AVPacketSideData *side_data;
int side_data_elems;
/**
* Duration of this packet in AVStream->time_base units, 0 if unknown.
* Equals next_pts - this_pts in presentation order.
*/
int64_t duration;
int64_t pos; ///< byte position in stream, -1 if unknown
#if FF_API_CONVERGENCE_DURATION
/**
* @deprecated Same as the duration field, but as int64_t. This was required
* for Matroska subtitles, whose duration values could overflow when the
* duration field was still an int.
*/
attribute_deprecated
int64_t convergence_duration;
#endif
} AVPacket;
當(dāng)然我不能把結(jié)構(gòu)體的每個(gè)參數(shù)都去標(biāo)注一遍(好多好累)掠械,有些參數(shù)和函數(shù)本身就有英文標(biāo)注由缆,實(shí)際寫代碼時(shí)大部分都是在使用時(shí)去查。
/**libavcodec/avpacket.c*/
//初始化一個(gè)AVPacket對(duì)象
void av_init_packet(AVPacket *pkt)
{
pkt->pts = AV_NOPTS_VALUE;
pkt->dts = AV_NOPTS_VALUE;
pkt->pos = -1;
pkt->duration = 0;
#if FF_API_CONVERGENCE_DURATION
FF_DISABLE_DEPRECATION_WARNINGS
pkt->convergence_duration = 0;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
pkt->flags = 0;
pkt->stream_index = 0;
pkt->buf = NULL;
pkt->side_data = NULL;
pkt->side_data_elems = 0;
}
/**libavcodec/utils.c*/
AVCodec *avcodec_find_decoder(enum AVCodecID id)
{
return find_encdec(id, 0);
}
static AVCodec *find_encdec(enum AVCodecID id, int encoder)
{
AVCodec *p, *experimental = NULL;
p = first_avcodec;
id= remap_deprecated_codec_id(id);
while (p) {
if ((encoder ? av_codec_is_encoder(p) : av_codec_is_decoder(p)) &&
p->id == id) {
if (p->capabilities & AV_CODEC_CAP_EXPERIMENTAL && !experimental) {
experimental = p;
} else
return p;
}
p = p->next;
}
return experimental;
}
/* encoder management */
static AVCodec *first_avcodec = NULL;
static AVCodec **last_avcodec = &first_avcodec;
從上面代碼可以看到猾蒂,通過(guò)在last_avcodec鏈表中輪詢匹配的解碼器均唉,那么last_avcodec鏈表是如何添加的?是不是有印象了肚菠,看文章前面avcodec_register_all那一節(jié)舔箭,在avcodec_register階段是不是每次就在鏈表尾部添加編解碼器,這樣就串聯(lián)起來(lái)了。
比如H264的編解碼器注冊(cè)了很多:
/**libavcodec/allcodecs.c*/
/* external libraries, that shouldn't be used by default if one of the
* above is available */
REGISTER_ENCDEC (LIBOPENH264, libopenh264);
REGISTER_DECODER(H263_CUVID, h263_cuvid);
REGISTER_DECODER(H264_CUVID, h264_cuvid);
REGISTER_ENCODER(H264_NVENC, h264_nvenc);
REGISTER_ENCODER(H264_OMX, h264_omx);
REGISTER_ENCODER(H264_QSV, h264_qsv);
REGISTER_ENCODER(H264_VAAPI, h264_vaapi);
REGISTER_ENCODER(H264_VIDEOTOOLBOX, h264_videotoolbox);
libopenh264的解碼器注冊(cè)是這樣的:
/**libavcodec/libopenh264dec.c*/
AVCodec ff_libopenh264_decoder = {
.name = "libopenh264",
.long_name = NULL_IF_CONFIG_SMALL("OpenH264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(SVCContext),
.init = svc_decode_init,
.decode = svc_decode_frame,
.close = svc_decode_close,
// The decoder doesn't currently support B-frames, and the decoder's API
// doesn't support reordering/delay, but the BSF could incur delay.
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_THREADSAFE |
FF_CODEC_CAP_INIT_CLEANUP,
};
按照匹配規(guī)則层扶,先注冊(cè)的優(yōu)先匹配箫章,即如果libopenh264和h264_cuvid都使能了,會(huì)優(yōu)先使用libopenh264來(lái)實(shí)現(xiàn)解碼镜会。
/**libavcodec/options.c*/
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)
{
AVCodecContext *avctx= av_malloc(sizeof(AVCodecContext));
if (!avctx)
return NULL;
if (init_context_defaults(avctx, codec) < 0) {
av_free(avctx);
return NULL;
}
return avctx;
}
static int init_context_defaults(AVCodecContext *s, const AVCodec *codec)
{
int flags=0;
memset(s, 0, sizeof(AVCodecContext));
s->av_class = &av_codec_context_class;
s->codec_type = codec ? codec->type : AVMEDIA_TYPE_UNKNOWN;
if (codec) {
s->codec = codec;
s->codec_id = codec->id;
}
if(s->codec_type == AVMEDIA_TYPE_AUDIO)
flags= AV_OPT_FLAG_AUDIO_PARAM;
else if(s->codec_type == AVMEDIA_TYPE_VIDEO)
flags= AV_OPT_FLAG_VIDEO_PARAM;
else if(s->codec_type == AVMEDIA_TYPE_SUBTITLE)
flags= AV_OPT_FLAG_SUBTITLE_PARAM;
av_opt_set_defaults2(s, flags, flags);
s->time_base = (AVRational){0,1};
s->framerate = (AVRational){ 0, 1 };
s->pkt_timebase = (AVRational){ 0, 1 };
s->get_buffer2 = avcodec_default_get_buffer2;
s->get_format = avcodec_default_get_format;
s->execute = avcodec_default_execute;
s->execute2 = avcodec_default_execute2;
s->sample_aspect_ratio = (AVRational){0,1};
s->pix_fmt = AV_PIX_FMT_NONE;
s->sample_fmt = AV_SAMPLE_FMT_NONE;
s->reordered_opaque = AV_NOPTS_VALUE;
if(codec && codec->priv_data_size){
if(!s->priv_data){
s->priv_data= av_mallocz(codec->priv_data_size);
if (!s->priv_data) {
return AVERROR(ENOMEM);
}
}
if(codec->priv_class){
*(const AVClass**)s->priv_data = codec->priv_class;
av_opt_set_defaults(s->priv_data);
}
}
if (codec && codec->defaults) {
int ret;
const AVCodecDefault *d = codec->defaults;
while (d->key) {
ret = av_opt_set(s, d->key, d->value, 0);
av_assert0(ret >= 0);
d++;
}
}
return 0;
}
根據(jù)以上代碼的分析檬寂,找到匹配ID為AV_CODEC_ID_MPEG1VIDEO解碼器并初始化解碼器參數(shù)后,接下來(lái)就是去打開這個(gè)解碼器戳表,avcodec_open2就是去打開解碼器:
int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
int ret = 0;
AVDictionary *tmp = NULL;
const AVPixFmtDescriptor *pixdesc;
if (avcodec_is_open(avctx))
return 0;
if ((!codec && !avctx->codec)) {
av_log(avctx, AV_LOG_ERROR, "No codec provided to avcodec_open2()\n");
return AVERROR(EINVAL);
}
if ((codec && avctx->codec && codec != avctx->codec)) {
av_log(avctx, AV_LOG_ERROR, "This AVCodecContext was allocated for %s, "
"but %s passed to avcodec_open2()\n", avctx->codec->name, codec->name);
return AVERROR(EINVAL);
}
if (!codec)
codec = avctx->codec;
if (avctx->extradata_size < 0 || avctx->extradata_size >= FF_MAX_EXTRADATA_SIZE)
return AVERROR(EINVAL);
if (options)
av_dict_copy(&tmp, *options, 0);
ret = ff_lock_avcodec(avctx, codec);
if (ret < 0)
return ret;
avctx->internal = av_mallocz(sizeof(AVCodecInternal));
if (!avctx->internal) {
ret = AVERROR(ENOMEM);
goto end;
}
avctx->internal->pool = av_mallocz(sizeof(*avctx->internal->pool));
if (!avctx->internal->pool) {
ret = AVERROR(ENOMEM);
goto free_and_end;
}
avctx->internal->to_free = av_frame_alloc();
if (!avctx->internal->to_free) {
ret = AVERROR(ENOMEM);
goto free_and_end;
}
avctx->internal->buffer_frame = av_frame_alloc();
if (!avctx->internal->buffer_frame) {
ret = AVERROR(ENOMEM);
goto free_and_end;
}
avctx->internal->buffer_pkt = av_packet_alloc();
if (!avctx->internal->buffer_pkt) {
ret = AVERROR(ENOMEM);
goto free_and_end;
}
if (codec->priv_data_size > 0) {
if (!avctx->priv_data) {
avctx->priv_data = av_mallocz(codec->priv_data_size);
if (!avctx->priv_data) {
ret = AVERROR(ENOMEM);
goto end;
}
if (codec->priv_class) {
*(const AVClass **)avctx->priv_data = codec->priv_class;
av_opt_set_defaults(avctx->priv_data);
}
}
if (codec->priv_class && (ret = av_opt_set_dict(avctx->priv_data, &tmp)) < 0)
goto free_and_end;
} else {
avctx->priv_data = NULL;
}
if ((ret = av_opt_set_dict(avctx, &tmp)) < 0)
goto free_and_end;
if (avctx->codec_whitelist && av_match_list(codec->name, avctx->codec_whitelist, ',') <= 0) {
av_log(avctx, AV_LOG_ERROR, "Codec (%s) not on whitelist \'%s\'\n", codec->name, avctx->codec_whitelist);
ret = AVERROR(EINVAL);
goto free_and_end;
}
// only call ff_set_dimensions() for non H.264/VP6F/DXV codecs so as not to overwrite previously setup dimensions
if (!(avctx->coded_width && avctx->coded_height && avctx->width && avctx->height &&
(avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_VP6F || avctx->codec_id == AV_CODEC_ID_DXV))) {
if (avctx->coded_width && avctx->coded_height)
ret = ff_set_dimensions(avctx, avctx->coded_width, avctx->coded_height);
else if (avctx->width && avctx->height)
ret = ff_set_dimensions(avctx, avctx->width, avctx->height);
if (ret < 0)
goto free_and_end;
}
if ((avctx->coded_width || avctx->coded_height || avctx->width || avctx->height)
&& ( av_image_check_size(avctx->coded_width, avctx->coded_height, 0, avctx) < 0
|| av_image_check_size(avctx->width, avctx->height, 0, avctx) < 0)) {
av_log(avctx, AV_LOG_WARNING, "Ignoring invalid width/height values\n");
ff_set_dimensions(avctx, 0, 0);
}
if (avctx->width > 0 && avctx->height > 0) {
if (av_image_check_sar(avctx->width, avctx->height,
avctx->sample_aspect_ratio) < 0) {
av_log(avctx, AV_LOG_WARNING, "ignoring invalid SAR: %u/%u\n",
avctx->sample_aspect_ratio.num,
avctx->sample_aspect_ratio.den);
avctx->sample_aspect_ratio = (AVRational){ 0, 1 };
}
}
/* if the decoder init function was already called previously,
* free the already allocated subtitle_header before overwriting it */
if (av_codec_is_decoder(codec))
av_freep(&avctx->subtitle_header);
if (avctx->channels > FF_SANE_NB_CHANNELS) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
avctx->codec = codec;
if ((avctx->codec_type == AVMEDIA_TYPE_UNKNOWN || avctx->codec_type == codec->type) &&
avctx->codec_id == AV_CODEC_ID_NONE) {
avctx->codec_type = codec->type;
avctx->codec_id = codec->id;
}
if (avctx->codec_id != codec->id || (avctx->codec_type != codec->type
&& avctx->codec_type != AVMEDIA_TYPE_ATTACHMENT)) {
av_log(avctx, AV_LOG_ERROR, "Codec type or id mismatches\n");
ret = AVERROR(EINVAL);
goto free_and_end;
}
avctx->frame_number = 0;
avctx->codec_descriptor = avcodec_descriptor_get(avctx->codec_id);
if ((avctx->codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) &&
avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
const char *codec_string = av_codec_is_encoder(codec) ? "encoder" : "decoder";
AVCodec *codec2;
av_log(avctx, AV_LOG_ERROR,
"The %s '%s' is experimental but experimental codecs are not enabled, "
"add '-strict %d' if you want to use it.\n",
codec_string, codec->name, FF_COMPLIANCE_EXPERIMENTAL);
codec2 = av_codec_is_encoder(codec) ? avcodec_find_encoder(codec->id) : avcodec_find_decoder(codec->id);
if (!(codec2->capabilities & AV_CODEC_CAP_EXPERIMENTAL))
av_log(avctx, AV_LOG_ERROR, "Alternatively use the non experimental %s '%s'.\n",
codec_string, codec2->name);
ret = AVERROR_EXPERIMENTAL;
goto free_and_end;
}
if (avctx->codec_type == AVMEDIA_TYPE_AUDIO &&
(!avctx->time_base.num || !avctx->time_base.den)) {
avctx->time_base.num = 1;
avctx->time_base.den = avctx->sample_rate;
}
if (!HAVE_THREADS)
av_log(avctx, AV_LOG_WARNING, "Warning: not compiled with thread support, using thread emulation\n");
if (CONFIG_FRAME_THREAD_ENCODER && av_codec_is_encoder(avctx->codec)) {
ff_unlock_avcodec(codec); //we will instantiate a few encoders thus kick the counter to prevent false detection of a problem
ret = ff_frame_thread_encoder_init(avctx, options ? *options : NULL);
ff_lock_avcodec(avctx, codec);
if (ret < 0)
goto free_and_end;
}
if (HAVE_THREADS
&& !(avctx->internal->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))) {
ret = ff_thread_init(avctx);
if (ret < 0) {
goto free_and_end;
}
}
if (!HAVE_THREADS && !(codec->capabilities & AV_CODEC_CAP_AUTO_THREADS))
avctx->thread_count = 1;
if (avctx->codec->max_lowres < avctx->lowres || avctx->lowres < 0) {
av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
avctx->codec->max_lowres);
avctx->lowres = avctx->codec->max_lowres;
}
#if FF_API_VISMV
if (avctx->debug_mv)
av_log(avctx, AV_LOG_WARNING, "The 'vismv' option is deprecated, "
"see the codecview filter instead.\n");
#endif
if (av_codec_is_encoder(avctx->codec)) {
int i;
#if FF_API_CODED_FRAME
FF_DISABLE_DEPRECATION_WARNINGS
avctx->coded_frame = av_frame_alloc();
if (!avctx->coded_frame) {
ret = AVERROR(ENOMEM);
goto free_and_end;
}
FF_ENABLE_DEPRECATION_WARNINGS
#endif
if (avctx->time_base.num <= 0 || avctx->time_base.den <= 0) {
av_log(avctx, AV_LOG_ERROR, "The encoder timebase is not set.\n");
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (avctx->codec->sample_fmts) {
for (i = 0; avctx->codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
if (avctx->sample_fmt == avctx->codec->sample_fmts[i])
break;
if (avctx->channels == 1 &&
av_get_planar_sample_fmt(avctx->sample_fmt) ==
av_get_planar_sample_fmt(avctx->codec->sample_fmts[i])) {
avctx->sample_fmt = avctx->codec->sample_fmts[i];
break;
}
}
if (avctx->codec->sample_fmts[i] == AV_SAMPLE_FMT_NONE) {
char buf[128];
snprintf(buf, sizeof(buf), "%d", avctx->sample_fmt);
av_log(avctx, AV_LOG_ERROR, "Specified sample format %s is invalid or not supported\n",
(char *)av_x_if_null(av_get_sample_fmt_name(avctx->sample_fmt), buf));
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
if (avctx->codec->pix_fmts) {
for (i = 0; avctx->codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++)
if (avctx->pix_fmt == avctx->codec->pix_fmts[i])
break;
if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_NONE
&& !((avctx->codec_id == AV_CODEC_ID_MJPEG || avctx->codec_id == AV_CODEC_ID_LJPEG)
&& avctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL)) {
char buf[128];
snprintf(buf, sizeof(buf), "%d", avctx->pix_fmt);
av_log(avctx, AV_LOG_ERROR, "Specified pixel format %s is invalid or not supported\n",
(char *)av_x_if_null(av_get_pix_fmt_name(avctx->pix_fmt), buf));
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ420P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ411P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ422P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ440P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P)
avctx->color_range = AVCOL_RANGE_JPEG;
}
if (avctx->codec->supported_samplerates) {
for (i = 0; avctx->codec->supported_samplerates[i] != 0; i++)
if (avctx->sample_rate == avctx->codec->supported_samplerates[i])
break;
if (avctx->codec->supported_samplerates[i] == 0) {
av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n",
avctx->sample_rate);
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
if (avctx->sample_rate < 0) {
av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n",
avctx->sample_rate);
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (avctx->codec->channel_layouts) {
if (!avctx->channel_layout) {
av_log(avctx, AV_LOG_WARNING, "Channel layout not specified\n");
} else {
for (i = 0; avctx->codec->channel_layouts[i] != 0; i++)
if (avctx->channel_layout == avctx->codec->channel_layouts[i])
break;
if (avctx->codec->channel_layouts[i] == 0) {
char buf[512];
av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout);
av_log(avctx, AV_LOG_ERROR, "Specified channel layout '%s' is not supported\n", buf);
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
}
if (avctx->channel_layout && avctx->channels) {
int channels = av_get_channel_layout_nb_channels(avctx->channel_layout);
if (channels != avctx->channels) {
char buf[512];
av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout);
av_log(avctx, AV_LOG_ERROR,
"Channel layout '%s' with %d channels does not match number of specified channels %d\n",
buf, channels, avctx->channels);
ret = AVERROR(EINVAL);
goto free_and_end;
}
} else if (avctx->channel_layout) {
avctx->channels = av_get_channel_layout_nb_channels(avctx->channel_layout);
}
if (avctx->channels < 0) {
av_log(avctx, AV_LOG_ERROR, "Specified number of channels %d is not supported\n",
avctx->channels);
ret = AVERROR(EINVAL);
goto free_and_end;
}
if(avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
pixdesc = av_pix_fmt_desc_get(avctx->pix_fmt);
if ( avctx->bits_per_raw_sample < 0
|| (avctx->bits_per_raw_sample > 8 && pixdesc->comp[0].depth <= 8)) {
av_log(avctx, AV_LOG_WARNING, "Specified bit depth %d not possible with the specified pixel formats depth %d\n",
avctx->bits_per_raw_sample, pixdesc->comp[0].depth);
avctx->bits_per_raw_sample = pixdesc->comp[0].depth;
}
if (avctx->width <= 0 || avctx->height <= 0) {
av_log(avctx, AV_LOG_ERROR, "dimensions not set\n");
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
if ( (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO)
&& avctx->bit_rate>0 && avctx->bit_rate<1000) {
av_log(avctx, AV_LOG_WARNING, "Bitrate %"PRId64" is extremely low, maybe you mean %"PRId64"k\n", (int64_t)avctx->bit_rate, (int64_t)avctx->bit_rate);
}
if (!avctx->rc_initial_buffer_occupancy)
avctx->rc_initial_buffer_occupancy = avctx->rc_buffer_size * 3 / 4;
if (avctx->ticks_per_frame && avctx->time_base.num &&
avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) {
av_log(avctx, AV_LOG_ERROR,
"ticks_per_frame %d too large for the timebase %d/%d.",
avctx->ticks_per_frame,
avctx->time_base.num,
avctx->time_base.den);
goto free_and_end;
}
if (avctx->hw_frames_ctx) {
AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
if (frames_ctx->format != avctx->pix_fmt) {
av_log(avctx, AV_LOG_ERROR,
"Mismatching AVCodecContext.pix_fmt and AVHWFramesContext.format\n");
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
}
avctx->pts_correction_num_faulty_pts =
avctx->pts_correction_num_faulty_dts = 0;
avctx->pts_correction_last_pts =
avctx->pts_correction_last_dts = INT64_MIN;
if ( !CONFIG_GRAY && avctx->flags & AV_CODEC_FLAG_GRAY
&& avctx->codec_descriptor->type == AVMEDIA_TYPE_VIDEO)
av_log(avctx, AV_LOG_WARNING,
"gray decoding requested but not enabled at configuration time\n");
if ( avctx->codec->init && (!(avctx->active_thread_type&FF_THREAD_FRAME)
|| avctx->internal->frame_thread_encoder)) {
ret = avctx->codec->init(avctx);
if (ret < 0) {
goto free_and_end;
}
}
ret=0;
#if FF_API_AUDIOENC_DELAY
if (av_codec_is_encoder(avctx->codec))
avctx->delay = avctx->initial_padding;
#endif
if (av_codec_is_decoder(avctx->codec)) {
if (!avctx->bit_rate)
avctx->bit_rate = get_bit_rate(avctx);
/* validate channel layout from the decoder */
if (avctx->channel_layout) {
int channels = av_get_channel_layout_nb_channels(avctx->channel_layout);
if (!avctx->channels)
avctx->channels = channels;
else if (channels != avctx->channels) {
char buf[512];
av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout);
av_log(avctx, AV_LOG_WARNING,
"Channel layout '%s' with %d channels does not match specified number of channels %d: "
"ignoring specified channel layout\n",
buf, channels, avctx->channels);
avctx->channel_layout = 0;
}
}
if (avctx->channels && avctx->channels < 0 ||
avctx->channels > FF_SANE_NB_CHANNELS) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (avctx->sub_charenc) {
if (avctx->codec_type != AVMEDIA_TYPE_SUBTITLE) {
av_log(avctx, AV_LOG_ERROR, "Character encoding is only "
"supported with subtitles codecs\n");
ret = AVERROR(EINVAL);
goto free_and_end;
} else if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) {
av_log(avctx, AV_LOG_WARNING, "Codec '%s' is bitmap-based, "
"subtitles character encoding will be ignored\n",
avctx->codec_descriptor->name);
avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_DO_NOTHING;
} else {
/* input character encoding is set for a text based subtitle
* codec at this point */
if (avctx->sub_charenc_mode == FF_SUB_CHARENC_MODE_AUTOMATIC)
avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_PRE_DECODER;
if (avctx->sub_charenc_mode == FF_SUB_CHARENC_MODE_PRE_DECODER) {
#if CONFIG_ICONV
iconv_t cd = iconv_open("UTF-8", avctx->sub_charenc);
if (cd == (iconv_t)-1) {
ret = AVERROR(errno);
av_log(avctx, AV_LOG_ERROR, "Unable to open iconv context "
"with input character encoding \"%s\"\n", avctx->sub_charenc);
goto free_and_end;
}
iconv_close(cd);
#else
av_log(avctx, AV_LOG_ERROR, "Character encoding subtitles "
"conversion needs a libavcodec built with iconv support "
"for this codec\n");
ret = AVERROR(ENOSYS);
goto free_and_end;
#endif
}
}
}
#if FF_API_AVCTX_TIMEBASE
if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
#endif
}
if (codec->priv_data_size > 0 && avctx->priv_data && codec->priv_class) {
av_assert0(*(const AVClass **)avctx->priv_data == codec->priv_class);
}
end:
ff_unlock_avcodec(codec);
if (options) {
av_dict_free(options);
*options = tmp;
}
return ret;
free_and_end:
if (avctx->codec &&
(avctx->codec->caps_internal & FF_CODEC_CAP_INIT_CLEANUP))
avctx->codec->close(avctx);
if (codec->priv_class && codec->priv_data_size)
av_opt_free(avctx->priv_data);
av_opt_free(avctx);
#if FF_API_CODED_FRAME
FF_DISABLE_DEPRECATION_WARNINGS
av_frame_free(&avctx->coded_frame);
FF_ENABLE_DEPRECATION_WARNINGS
#endif
av_dict_free(&tmp);
av_freep(&avctx->priv_data);
if (avctx->internal) {
av_packet_free(&avctx->internal->buffer_pkt);
av_frame_free(&avctx->internal->buffer_frame);
av_frame_free(&avctx->internal->to_free);
av_freep(&avctx->internal->pool);
}
av_freep(&avctx->internal);
avctx->codec = NULL;
goto end;
}
int ff_set_dimensions(AVCodecContext *s, int width, int height)
{
int ret = av_image_check_size(width, height, 0, s);
if (ret < 0)
width = height = 0;
s->coded_width = width;
s->coded_height = height;
s->width = AV_CEIL_RSHIFT(width, s->lowres);
s->height = AV_CEIL_RSHIFT(height, s->lowres);
return ret;
}
接下來(lái)就是對(duì)每一幀數(shù)據(jù)的具體處理:
/**libavutil/frame.c*/
AVFrame *av_frame_alloc(void)
{
AVFrame *frame = av_mallocz(sizeof(*frame));
if (!frame)
return NULL;
frame->extended_data = NULL;
get_frame_defaults(frame);
return frame;
}
AVFrame結(jié)構(gòu)體是對(duì)一個(gè)音頻或視頻幀的數(shù)據(jù)描述:
/**libavutil/frame.h*/
/**
* This structure describes decoded (raw) audio or video data.
*
* AVFrame must be allocated using av_frame_alloc(). Note that this only
* allocates the AVFrame itself, the buffers for the data must be managed
* through other means (see below).
* AVFrame must be freed with av_frame_free().
*
* AVFrame is typically allocated once and then reused multiple times to hold
* different data (e.g. a single AVFrame to hold frames received from a
* decoder). In such a case, av_frame_unref() will free any references held by
* the frame and reset it to its original clean state before it
* is reused again.
*
* The data described by an AVFrame is usually reference counted through the
* AVBuffer API. The underlying buffer references are stored in AVFrame.buf /
* AVFrame.extended_buf. An AVFrame is considered to be reference counted if at
* least one reference is set, i.e. if AVFrame.buf[0] != NULL. In such a case,
* every single data plane must be contained in one of the buffers in
* AVFrame.buf or AVFrame.extended_buf.
* There may be a single buffer for all the data, or one separate buffer for
* each plane, or anything in between.
*
* sizeof(AVFrame) is not a part of the public ABI, so new fields may be added
* to the end with a minor bump.
* Similarly fields that are marked as to be only accessed by
* av_opt_ptr() can be reordered. This allows 2 forks to add fields
* without breaking compatibility with each other.
*
* Fields can be accessed through AVOptions, the name string used, matches the
* C structure field name for fields accessible through AVOptions. The AVClass
* for AVFrame can be obtained from avcodec_get_frame_class()
*/
typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
/**
* pointer to the picture/channel planes.
* This might be different from the first allocated byte
*
* Some decoders access areas outside 0,0 - width,height, please
* see avcodec_align_dimensions2(). Some filters and swscale can read
* up to 16 bytes beyond the planes, if these filters are to be used,
* then 16 extra bytes must be allocated.
*
* NOTE: Except for hwaccel formats, pointers not needed by the format
* MUST be set to NULL.
*/
uint8_t *data[AV_NUM_DATA_POINTERS];
/**
* For video, size in bytes of each picture line.
* For audio, size in bytes of each plane.
*
* For audio, only linesize[0] may be set. For planar audio, each channel
* plane must be the same size.
*
* For video the linesizes should be multiples of the CPUs alignment
* preference, this is 16 or 32 for modern desktop CPUs.
* Some code requires such alignment other code can be slower without
* correct alignment, for yet other it makes no difference.
*
* @note The linesize may be larger than the size of usable data -- there
* may be extra padding present for performance reasons.
*/
int linesize[AV_NUM_DATA_POINTERS];
/**
* pointers to the data planes/channels.
*
* For video, this should simply point to data[].
*
* For planar audio, each channel has a separate data pointer, and
* linesize[0] contains the size of each channel buffer.
* For packed audio, there is just one data pointer, and linesize[0]
* contains the total size of the buffer for all channels.
*
* Note: Both data and extended_data should always be set in a valid frame,
* but for planar audio with more channels that can fit in data,
* extended_data must be used in order to access all channels.
*/
uint8_t **extended_data;
/**
* width and height of the video frame
*/
int width, height;
/**
* number of audio samples (per channel) described by this frame
*/
int nb_samples;
/**
* format of the frame, -1 if unknown or unset
* Values correspond to enum AVPixelFormat for video frames,
* enum AVSampleFormat for audio)
*/
int format;
/**
* 1 -> keyframe, 0-> not
*/
int key_frame;
/**
* Picture type of the frame.
*/
enum AVPictureType pict_type;
/**
* Sample aspect ratio for the video frame, 0/1 if unknown/unspecified.
*/
AVRational sample_aspect_ratio;
/**
* Presentation timestamp in time_base units (time when frame should be shown to user).
*/
int64_t pts;
#if FF_API_PKT_PTS
/**
* PTS copied from the AVPacket that was decoded to produce this frame.
* @deprecated use the pts field instead
*/
attribute_deprecated
int64_t pkt_pts;
#endif
/**
* DTS copied from the AVPacket that triggered returning this frame. (if frame threading isn't used)
* This is also the Presentation time of this AVFrame calculated from
* only AVPacket.dts values without pts values.
*/
int64_t pkt_dts;
/**
* picture number in bitstream order
*/
int coded_picture_number;
/**
* picture number in display order
*/
int display_picture_number;
/**
* quality (between 1 (good) and FF_LAMBDA_MAX (bad))
*/
int quality;
/**
* for some private data of the user
*/
void *opaque;
#if FF_API_ERROR_FRAME
/**
* @deprecated unused
*/
attribute_deprecated
uint64_t error[AV_NUM_DATA_POINTERS];
#endif
/**
* When decoding, this signals how much the picture must be delayed.
* extra_delay = repeat_pict / (2*fps)
*/
int repeat_pict;
/**
* The content of the picture is interlaced.
*/
int interlaced_frame;
/**
* If the content is interlaced, is top field displayed first.
*/
int top_field_first;
/**
* Tell user application that palette has changed from previous frame.
*/
int palette_has_changed;
/**
* reordered opaque 64 bits (generally an integer or a double precision float
* PTS but can be anything).
* The user sets AVCodecContext.reordered_opaque to represent the input at
* that time,
* the decoder reorders values as needed and sets AVFrame.reordered_opaque
* to exactly one of the values provided by the user through AVCodecContext.reordered_opaque
* @deprecated in favor of pkt_pts
*/
int64_t reordered_opaque;
/**
* Sample rate of the audio data.
*/
int sample_rate;
/**
* Channel layout of the audio data.
*/
uint64_t channel_layout;
/**
* AVBuffer references backing the data for this frame. If all elements of
* this array are NULL, then this frame is not reference counted. This array
* must be filled contiguously -- if buf[i] is non-NULL then buf[j] must
* also be non-NULL for all j < i.
*
* There may be at most one AVBuffer per data plane, so for video this array
* always contains all the references. For planar audio with more than
* AV_NUM_DATA_POINTERS channels, there may be more buffers than can fit in
* this array. Then the extra AVBufferRef pointers are stored in the
* extended_buf array.
*/
AVBufferRef *buf[AV_NUM_DATA_POINTERS];
/**
* For planar audio which requires more than AV_NUM_DATA_POINTERS
* AVBufferRef pointers, this array will hold all the references which
* cannot fit into AVFrame.buf.
*
* Note that this is different from AVFrame.extended_data, which always
* contains all the pointers. This array only contains the extra pointers,
* which cannot fit into AVFrame.buf.
*
* This array is always allocated using av_malloc() by whoever constructs
* the frame. It is freed in av_frame_unref().
*/
AVBufferRef **extended_buf;
/**
* Number of elements in extended_buf.
*/
int nb_extended_buf;
AVFrameSideData **side_data;
int nb_side_data;
/**
* @defgroup lavu_frame_flags AV_FRAME_FLAGS
* @ingroup lavu_frame
* Flags describing additional frame properties.
*
* @{
*/
/**
* The frame data may be corrupted, e.g. due to decoding errors.
*/
#define AV_FRAME_FLAG_CORRUPT (1 << 0)
/**
* A flag to mark the frames which need to be decoded, but shouldn't be output.
*/
#define AV_FRAME_FLAG_DISCARD (1 << 2)
/**
* @}
*/
/**
* Frame flags, a combination of @ref lavu_frame_flags
*/
int flags;
/**
* MPEG vs JPEG YUV range.
* It must be accessed using av_frame_get_color_range() and
* av_frame_set_color_range().
* - encoding: Set by user
* - decoding: Set by libavcodec
*/
enum AVColorRange color_range;
enum AVColorPrimaries color_primaries;
enum AVColorTransferCharacteristic color_trc;
/**
* YUV colorspace type.
* It must be accessed using av_frame_get_colorspace() and
* av_frame_set_colorspace().
* - encoding: Set by user
* - decoding: Set by libavcodec
*/
enum AVColorSpace colorspace;
enum AVChromaLocation chroma_location;
/**
* frame timestamp estimated using various heuristics, in stream time base
* Code outside libavutil should access this field using:
* av_frame_get_best_effort_timestamp(frame)
* - encoding: unused
* - decoding: set by libavcodec, read by user.
*/
int64_t best_effort_timestamp;
/**
* reordered pos from the last AVPacket that has been input into the decoder
* Code outside libavutil should access this field using:
* av_frame_get_pkt_pos(frame)
* - encoding: unused
* - decoding: Read by user.
*/
int64_t pkt_pos;
/**
* duration of the corresponding packet, expressed in
* AVStream->time_base units, 0 if unknown.
* Code outside libavutil should access this field using:
* av_frame_get_pkt_duration(frame)
* - encoding: unused
* - decoding: Read by user.
*/
int64_t pkt_duration;
/**
* metadata.
* Code outside libavutil should access this field using:
* av_frame_get_metadata(frame)
* - encoding: Set by user.
* - decoding: Set by libavcodec.
*/
AVDictionary *metadata;
/**
* decode error flags of the frame, set to a combination of
* FF_DECODE_ERROR_xxx flags if the decoder produced a frame, but there
* were errors during the decoding.
* Code outside libavutil should access this field using:
* av_frame_get_decode_error_flags(frame)
* - encoding: unused
* - decoding: set by libavcodec, read by user.
*/
int decode_error_flags;
#define FF_DECODE_ERROR_INVALID_BITSTREAM 1
#define FF_DECODE_ERROR_MISSING_REFERENCE 2
/**
* number of audio channels, only used for audio.
* Code outside libavutil should access this field using:
* av_frame_get_channels(frame)
* - encoding: unused
* - decoding: Read by user.
*/
int channels;
/**
* size of the corresponding packet containing the compressed
* frame. It must be accessed using av_frame_get_pkt_size() and
* av_frame_set_pkt_size().
* It is set to a negative value if unknown.
* - encoding: unused
* - decoding: set by libavcodec, read by user.
*/
int pkt_size;
#if FF_API_FRAME_QP
/**
* QP table
* Not to be accessed directly from outside libavutil
*/
attribute_deprecated
int8_t *qscale_table;
/**
* QP store stride
* Not to be accessed directly from outside libavutil
*/
attribute_deprecated
int qstride;
attribute_deprecated
int qscale_type;
/**
* Not to be accessed directly from outside libavutil
*/
AVBufferRef *qp_table_buf;
#endif
/**
* For hwaccel-format frames, this should be a reference to the
* AVHWFramesContext describing the frame.
*/
AVBufferRef *hw_frames_ctx;
} AVFrame;
那么如何把解碼的數(shù)據(jù)寫到AVFrame中:
/**doc/decoding_encoding.c*/
static int decode_write_frame(const char *outfilename, AVCodecContext *avctx,
AVFrame *frame, int *frame_count, AVPacket *pkt, int last)
{
int len, got_frame;
char buf[1024];
len = avcodec_decode_video2(avctx, frame, &got_frame, pkt);
if (len < 0) {
fprintf(stderr, "Error while decoding frame %d\n", *frame_count);
return len;
}
if (got_frame) {
printf("Saving %sframe %3d\n", last ? "last " : "", *frame_count);
fflush(stdout);
/* the picture is allocated by the decoder, no need to free it */
snprintf(buf, sizeof(buf), outfilename, *frame_count);
pgm_save(frame->data[0], frame->linesize[0],
frame->width, frame->height, buf);
(*frame_count)++;
}
if (pkt->data) {
pkt->size -= len;
pkt->data += len;
}
return 0;
}
static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,
char *filename)
{
FILE *f;
int i;
f = fopen(filename,"w");
fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
for (i = 0; i < ysize; i++)
fwrite(buf + i * wrap, 1, xsize, f);
fclose(f);
}
從AVPacket中解碼出視頻幀數(shù)據(jù):
/**libavcodec/utils.c*/
int attribute_align_arg avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
const AVPacket *avpkt)
{
AVCodecInternal *avci = avctx->internal;
int ret;
// copy to ensure we do not change avpkt
AVPacket tmp = *avpkt;
if (!avctx->codec)
return AVERROR(EINVAL);
if (avctx->codec->type != AVMEDIA_TYPE_VIDEO) {
av_log(avctx, AV_LOG_ERROR, "Invalid media type for video\n");
return AVERROR(EINVAL);
}
*got_picture_ptr = 0;
if ((avctx->coded_width || avctx->coded_height) && av_image_check_size(avctx->coded_width, avctx->coded_height, 0, avctx))
return AVERROR(EINVAL);
avctx->internal->pkt = avpkt;
ret = apply_param_change(avctx, avpkt);
if (ret < 0)
return ret;
av_frame_unref(picture);
if ((avctx->codec->capabilities & AV_CODEC_CAP_DELAY) || avpkt->size ||
(avctx->active_thread_type & FF_THREAD_FRAME)) {
int did_split = av_packet_split_side_data(&tmp);
ret = apply_param_change(avctx, &tmp);
if (ret < 0)
goto fail;
avctx->internal->pkt = &tmp;
if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME)
ret = ff_thread_decode_frame(avctx, picture, got_picture_ptr,
&tmp);
else {
ret = avctx->codec->decode(avctx, picture, got_picture_ptr,
&tmp);
if (!(avctx->codec->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS))
picture->pkt_dts = avpkt->dts;
if(!avctx->has_b_frames){
av_frame_set_pkt_pos(picture, avpkt->pos);
}
//FIXME these should be under if(!avctx->has_b_frames)
/* get_buffer is supposed to set frame parameters */
if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) {
if (!picture->sample_aspect_ratio.num) picture->sample_aspect_ratio = avctx->sample_aspect_ratio;
if (!picture->width) picture->width = avctx->width;
if (!picture->height) picture->height = avctx->height;
if (picture->format == AV_PIX_FMT_NONE) picture->format = avctx->pix_fmt;
}
}
fail:
emms_c(); //needed to avoid an emms_c() call before every return;
avctx->internal->pkt = NULL;
if (did_split) {
av_packet_free_side_data(&tmp);
if(ret == tmp.size)
ret = avpkt->size;
}
if (picture->flags & AV_FRAME_FLAG_DISCARD) {
*got_picture_ptr = 0;
}
if (*got_picture_ptr) {
if (!avctx->refcounted_frames) {
int err = unrefcount_frame(avci, picture);
if (err < 0)
return err;
}
avctx->frame_number++;
av_frame_set_best_effort_timestamp(picture,
guess_correct_pts(avctx,
picture->pts,
picture->pkt_dts));
} else
av_frame_unref(picture);
} else
ret = 0;
/* many decoders assign whole AVFrames, thus overwriting extended_data;
* make sure it's set correctly */
av_assert0(!picture->extended_data || picture->extended_data == picture->data);
#if FF_API_AVCTX_TIMEBASE
if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
#endif
return ret;
}
avcodec_decode_video2這是個(gè)關(guān)鍵的函數(shù)族阅,從函數(shù)中可以看到解碼分為兩種方式:
a)ff_thread_decode_frame(avctx, picture, got_picture_ptr,&tmp)
多線程解碼
b)avctx->codec->decode(avctx, picture, got_picture_ptr,&tmp)
調(diào)用AVCodec相應(yīng)的decode函數(shù)扰楼,完成解碼准颓。
比如針對(duì)本文DEMO中AV_CODEC_ID_MPEG1VIDEO的解碼就可調(diào)用ff_mpeg1video_decoder中的mpeg_decode_frame:
/**libavcodec/mpeg12dec.c*/
AVCodec ff_mpeg1video_decoder = {
.name = "mpeg1video",
.long_name = NULL_IF_CONFIG_SMALL("MPEG-1 video"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_MPEG1VIDEO,
.priv_data_size = sizeof(Mpeg1Context),
.init = mpeg_decode_init,
.close = mpeg_decode_end,
.decode = mpeg_decode_frame,
.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_TRUNCATED | AV_CODEC_CAP_DELAY |
AV_CODEC_CAP_SLICE_THREADS,
.flush = flush,
.max_lowres = 3,
.update_thread_context = ONLY_IF_THREADS_ENABLED(mpeg_decode_update_thread_context)
};
static int mpeg_decode_frame(AVCodecContext *avctx, void *data,
int *got_output, AVPacket *avpkt)
{
const uint8_t *buf = avpkt->data;
int ret;
int buf_size = avpkt->size;
Mpeg1Context *s = avctx->priv_data;
AVFrame *picture = data;
MpegEncContext *s2 = &s->mpeg_enc_ctx;
if (buf_size == 0 || (buf_size == 4 && AV_RB32(buf) == SEQ_END_CODE)) {
/* special case for last picture */
if (s2->low_delay == 0 && s2->next_picture_ptr) {
int ret = av_frame_ref(picture, s2->next_picture_ptr->f);
if (ret < 0)
return ret;
s2->next_picture_ptr = NULL;
*got_output = 1;
}
return buf_size;
}
if (s2->avctx->flags & AV_CODEC_FLAG_TRUNCATED) {
int next = ff_mpeg1_find_frame_end(&s2->parse_context, buf,
buf_size, NULL);
if (ff_combine_frame(&s2->parse_context, next,
(const uint8_t **) &buf, &buf_size) < 0)
return buf_size;
}
s2->codec_tag = avpriv_toupper4(avctx->codec_tag);
if (s->mpeg_enc_ctx_allocated == 0 && ( s2->codec_tag == AV_RL32("VCR2")
|| s2->codec_tag == AV_RL32("BW10")
))
vcr2_init_sequence(avctx);
s->slice_count = 0;
if (avctx->extradata && !s->extradata_decoded) {
ret = decode_chunks(avctx, picture, got_output,
avctx->extradata, avctx->extradata_size);
if (*got_output) {
av_log(avctx, AV_LOG_ERROR, "picture in extradata\n");
av_frame_unref(picture);
*got_output = 0;
}
s->extradata_decoded = 1;
if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) {
s2->current_picture_ptr = NULL;
return ret;
}
}
ret = decode_chunks(avctx, picture, got_output, buf, buf_size);
if (ret<0 || *got_output) {
s2->current_picture_ptr = NULL;
if (s2->timecode_frame_start != -1 && *got_output) {
AVFrameSideData *tcside = av_frame_new_side_data(picture,
AV_FRAME_DATA_GOP_TIMECODE,
sizeof(int64_t));
if (!tcside)
return AVERROR(ENOMEM);
memcpy(tcside->data, &s2->timecode_frame_start, sizeof(int64_t));
s2->timecode_frame_start = -1;
}
}
return ret;
}
static int decode_chunks(AVCodecContext *avctx, AVFrame *picture,
int *got_output, const uint8_t *buf, int buf_size)
{
Mpeg1Context *s = avctx->priv_data;
MpegEncContext *s2 = &s->mpeg_enc_ctx;
const uint8_t *buf_ptr = buf;
const uint8_t *buf_end = buf + buf_size;
int ret, input_size;
int last_code = 0, skip_frame = 0;
int picture_start_code_seen = 0;
for (;;) {
/* find next start code */
uint32_t start_code = -1;
buf_ptr = avpriv_find_start_code(buf_ptr, buf_end, &start_code);
if (start_code > 0x1ff) {
if (!skip_frame) {
if (HAVE_THREADS &&
(avctx->active_thread_type & FF_THREAD_SLICE) &&
!avctx->hwaccel) {
int i;
av_assert0(avctx->thread_count > 1);
avctx->execute(avctx, slice_decode_thread,
&s2->thread_context[0], NULL,
s->slice_count, sizeof(void *));
for (i = 0; i < s->slice_count; i++)
s2->er.error_count += s2->thread_context[i]->er.error_count;
}
#if FF_API_VDPAU
if ((CONFIG_MPEG_VDPAU_DECODER || CONFIG_MPEG1_VDPAU_DECODER)
&& uses_vdpau(avctx))
ff_vdpau_mpeg_picture_complete(s2, buf, buf_size, s->slice_count);
#endif
ret = slice_end(avctx, picture);
if (ret < 0)
return ret;
else if (ret) {
// FIXME: merge with the stuff in mpeg_decode_slice
if (s2->last_picture_ptr || s2->low_delay)
*got_output = 1;
}
}
s2->pict_type = 0;
if (avctx->err_recognition & AV_EF_EXPLODE && s2->er.error_count)
return AVERROR_INVALIDDATA;
return FFMAX(0, buf_ptr - buf - s2->parse_context.last_index);
}
input_size = buf_end - buf_ptr;
if (avctx->debug & FF_DEBUG_STARTCODE)
av_log(avctx, AV_LOG_DEBUG, "%3"PRIX32" at %"PTRDIFF_SPECIFIER" left %d\n",
start_code, buf_ptr - buf, input_size);
/* prepare data for next start code */
switch (start_code) {
case SEQ_START_CODE:
if (last_code == 0) {
mpeg1_decode_sequence(avctx, buf_ptr, input_size);
if (buf != avctx->extradata)
s->sync = 1;
} else {
av_log(avctx, AV_LOG_ERROR,
"ignoring SEQ_START_CODE after %X\n", last_code);
if (avctx->err_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
break;
case PICTURE_START_CODE:
if (picture_start_code_seen && s2->picture_structure == PICT_FRAME) {
/* If it's a frame picture, there can't be more than one picture header.
Yet, it does happen and we need to handle it. */
av_log(avctx, AV_LOG_WARNING, "ignoring extra picture following a frame-picture\n");
break;
}
picture_start_code_seen = 1;
if (s2->width <= 0 || s2->height <= 0) {
av_log(avctx, AV_LOG_ERROR, "Invalid frame dimensions %dx%d.\n",
s2->width, s2->height);
return AVERROR_INVALIDDATA;
}
if (s->tmpgexs){
s2->intra_dc_precision= 3;
s2->intra_matrix[0]= 1;
}
if (HAVE_THREADS && (avctx->active_thread_type & FF_THREAD_SLICE) &&
!avctx->hwaccel && s->slice_count) {
int i;
avctx->execute(avctx, slice_decode_thread,
s2->thread_context, NULL,
s->slice_count, sizeof(void *));
for (i = 0; i < s->slice_count; i++)
s2->er.error_count += s2->thread_context[i]->er.error_count;
s->slice_count = 0;
}
if (last_code == 0 || last_code == SLICE_MIN_START_CODE) {
ret = mpeg_decode_postinit(avctx);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR,
"mpeg_decode_postinit() failure\n");
return ret;
}
/* We have a complete image: we try to decompress it. */
if (mpeg1_decode_picture(avctx, buf_ptr, input_size) < 0)
s2->pict_type = 0;
s->first_slice = 1;
last_code = PICTURE_START_CODE;
} else {
av_log(avctx, AV_LOG_ERROR,
"ignoring pic after %X\n", last_code);
if (avctx->err_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
break;
case EXT_START_CODE:
init_get_bits(&s2->gb, buf_ptr, input_size * 8);
switch (get_bits(&s2->gb, 4)) {
case 0x1:
if (last_code == 0) {
mpeg_decode_sequence_extension(s);
} else {
av_log(avctx, AV_LOG_ERROR,
"ignoring seq ext after %X\n", last_code);
if (avctx->err_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
break;
case 0x2:
mpeg_decode_sequence_display_extension(s);
break;
case 0x3:
mpeg_decode_quant_matrix_extension(s2);
break;
case 0x7:
mpeg_decode_picture_display_extension(s);
break;
case 0x8:
if (last_code == PICTURE_START_CODE) {
mpeg_decode_picture_coding_extension(s);
} else {
av_log(avctx, AV_LOG_ERROR,
"ignoring pic cod ext after %X\n", last_code);
if (avctx->err_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
break;
}
break;
case USER_START_CODE:
mpeg_decode_user_data(avctx, buf_ptr, input_size);
break;
case GOP_START_CODE:
if (last_code == 0) {
s2->first_field = 0;
mpeg_decode_gop(avctx, buf_ptr, input_size);
s->sync = 1;
} else {
av_log(avctx, AV_LOG_ERROR,
"ignoring GOP_START_CODE after %X\n", last_code);
if (avctx->err_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
break;
default:
if (start_code >= SLICE_MIN_START_CODE &&
start_code <= SLICE_MAX_START_CODE && last_code == PICTURE_START_CODE) {
if (s2->progressive_sequence && !s2->progressive_frame) {
s2->progressive_frame = 1;
av_log(s2->avctx, AV_LOG_ERROR,
"interlaced frame in progressive sequence, ignoring\n");
}
if (s2->picture_structure == 0 ||
(s2->progressive_frame && s2->picture_structure != PICT_FRAME)) {
av_log(s2->avctx, AV_LOG_ERROR,
"picture_structure %d invalid, ignoring\n",
s2->picture_structure);
s2->picture_structure = PICT_FRAME;
}
if (s2->progressive_sequence && !s2->frame_pred_frame_dct)
av_log(s2->avctx, AV_LOG_WARNING, "invalid frame_pred_frame_dct\n");
if (s2->picture_structure == PICT_FRAME) {
s2->first_field = 0;
s2->v_edge_pos = 16 * s2->mb_height;
} else {
s2->first_field ^= 1;
s2->v_edge_pos = 8 * s2->mb_height;
memset(s2->mbskip_table, 0, s2->mb_stride * s2->mb_height);
}
}
if (start_code >= SLICE_MIN_START_CODE &&
start_code <= SLICE_MAX_START_CODE && last_code != 0) {
const int field_pic = s2->picture_structure != PICT_FRAME;
int mb_y = start_code - SLICE_MIN_START_CODE;
last_code = SLICE_MIN_START_CODE;
if (s2->codec_id != AV_CODEC_ID_MPEG1VIDEO && s2->mb_height > 2800/16)
mb_y += (*buf_ptr&0xE0)<<2;
mb_y <<= field_pic;
if (s2->picture_structure == PICT_BOTTOM_FIELD)
mb_y++;
if (buf_end - buf_ptr < 2) {
av_log(s2->avctx, AV_LOG_ERROR, "slice too small\n");
return AVERROR_INVALIDDATA;
}
if (mb_y >= s2->mb_height) {
av_log(s2->avctx, AV_LOG_ERROR,
"slice below image (%d >= %d)\n", mb_y, s2->mb_height);
return AVERROR_INVALIDDATA;
}
if (!s2->last_picture_ptr) {
/* Skip B-frames if we do not have reference frames and
* GOP is not closed. */
if (s2->pict_type == AV_PICTURE_TYPE_B) {
if (!s2->closed_gop) {
skip_frame = 1;
break;
}
}
}
if (s2->pict_type == AV_PICTURE_TYPE_I || (s2->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL))
s->sync = 1;
if (!s2->next_picture_ptr) {
/* Skip P-frames if we do not have a reference frame or
* we have an invalid header. */
if (s2->pict_type == AV_PICTURE_TYPE_P && !s->sync) {
skip_frame = 1;
break;
}
}
if ((avctx->skip_frame >= AVDISCARD_NONREF &&
s2->pict_type == AV_PICTURE_TYPE_B) ||
(avctx->skip_frame >= AVDISCARD_NONKEY &&
s2->pict_type != AV_PICTURE_TYPE_I) ||
avctx->skip_frame >= AVDISCARD_ALL) {
skip_frame = 1;
break;
}
if (!s->mpeg_enc_ctx_allocated)
break;
if (s2->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
if (mb_y < avctx->skip_top ||
mb_y >= s2->mb_height - avctx->skip_bottom)
break;
}
if (!s2->pict_type) {
av_log(avctx, AV_LOG_ERROR, "Missing picture start code\n");
if (avctx->err_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
break;
}
if (s->first_slice) {
skip_frame = 0;
s->first_slice = 0;
if ((ret = mpeg_field_start(s2, buf, buf_size)) < 0)
return ret;
}
if (!s2->current_picture_ptr) {
av_log(avctx, AV_LOG_ERROR,
"current_picture not initialized\n");
return AVERROR_INVALIDDATA;
}
#if FF_API_VDPAU
if (uses_vdpau(avctx)) {
s->slice_count++;
break;
}
#endif
if (HAVE_THREADS &&
(avctx->active_thread_type & FF_THREAD_SLICE) &&
!avctx->hwaccel) {
int threshold = (s2->mb_height * s->slice_count +
s2->slice_context_count / 2) /
s2->slice_context_count;
av_assert0(avctx->thread_count > 1);
if (threshold <= mb_y) {
MpegEncContext *thread_context = s2->thread_context[s->slice_count];
thread_context->start_mb_y = mb_y;
thread_context->end_mb_y = s2->mb_height;
if (s->slice_count) {
s2->thread_context[s->slice_count - 1]->end_mb_y = mb_y;
ret = ff_update_duplicate_context(thread_context, s2);
if (ret < 0)
return ret;
}
init_get_bits(&thread_context->gb, buf_ptr, input_size * 8);
s->slice_count++;
}
buf_ptr += 2; // FIXME add minimum number of bytes per slice
} else {
ret = mpeg_decode_slice(s2, mb_y, &buf_ptr, input_size);
emms_c();
if (ret < 0) {
if (avctx->err_recognition & AV_EF_EXPLODE)
return ret;
if (s2->resync_mb_x >= 0 && s2->resync_mb_y >= 0)
ff_er_add_slice(&s2->er, s2->resync_mb_x,
s2->resync_mb_y, s2->mb_x, s2->mb_y,
ER_AC_ERROR | ER_DC_ERROR | ER_MV_ERROR);
} else {
ff_er_add_slice(&s2->er, s2->resync_mb_x,
s2->resync_mb_y, s2->mb_x - 1, s2->mb_y,
ER_AC_END | ER_DC_END | ER_MV_END);
}
}
}
break;
}
}
}
簡(jiǎn)單的分析下mpeg_decode_frame及皂,首先判斷是否是最后一幀,如果是最后一幀對(duì)最后一幀做特殊處理av_frame_ref价涝,如果不是最后一幀女蜈,通過(guò)ff_mpeg1_find_frame_end找到幀數(shù)據(jù)頭的位置,否則返回錯(cuò)誤信息色瘩,找到mpeg1的頭信息后通過(guò)ff_combine_frame合成一個(gè)完整的幀數(shù)據(jù)伪窖,調(diào)用decode_chunks完成解碼,到這里泞遗,使用FFMPEG的解碼過(guò)程就分析完畢了惰许。
接下來(lái)看看編碼相關(guān):
/**doc/decoding_encoding.c*/
/*
* Video encoding example
*/
static void video_encode_example(const char *filename, int codec_id)
{
AVCodec *codec;
AVCodecContext *c= NULL;
int i, ret, x, y, got_output;
FILE *f;
AVFrame *frame;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
printf("Encode video file %s\n", filename);
/* find the video encoder */
codec = avcodec_find_encoder(codec_id);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = 352;
c->height = 288;
/* frames per second */
c->time_base = (AVRational){1,25};
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
/* the image can be allocated by any means and av_image_alloc() is
* just the most convenient way if av_malloc() is to be used */
ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height,
c->pix_fmt, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw picture buffer\n");
exit(1);
}
/* encode 1 second of video */
for (i = 0; i < 25; i++) {
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
fflush(stdout);
/* prepare a dummy image */
/* Y */
for (y = 0; y < c->height; y++) {
for (x = 0; x < c->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
}
}
/* Cb and Cr */
for (y = 0; y < c->height/2; y++) {
for (x = 0; x < c->width/2; x++) {
frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
}
}
frame->pts = i;
/* encode the image */
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_packet_unref(&pkt);
}
}
/* get the delayed frames */
for (got_output = 1; got_output; i++) {
fflush(stdout);
ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_packet_unref(&pkt);
}
}
/* add sequence end code to have a real MPEG file */
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_close(c);
av_free(c);
av_freep(&frame->data[0]);
av_frame_free(&frame);
printf("\n");
}
和解碼的流程基本一致,也是先根據(jù)codec_id去匹配最優(yōu)的編碼器:
/**libavcodec/utils.c*/
AVCodec *avcodec_find_encoder(enum AVCodecID id)
{
return find_encdec(id, 1);
}
static AVCodec *find_encdec(enum AVCodecID id, int encoder)
{
AVCodec *p, *experimental = NULL;
p = first_avcodec;
id= remap_deprecated_codec_id(id);
while (p) {
if ((encoder ? av_codec_is_encoder(p) : av_codec_is_decoder(p)) &&
p->id == id) {
if (p->capabilities & AV_CODEC_CAP_EXPERIMENTAL && !experimental) {
experimental = p;
} else
return p;
}
p = p->next;
}
return experimental;
}
/* encoder management */
static AVCodec *first_avcodec = NULL;
static AVCodec **last_avcodec = &first_avcodec;
以H264的編碼為例史辙,其中libopenh264的注冊(cè)如下:
/**libavcodec/libopenh264enc.c*/
AVCodec ff_libopenh264_encoder = {
.name = "libopenh264",
.long_name = NULL_IF_CONFIG_SMALL("OpenH264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(SVCContext),
.init = svc_encode_init,
.encode2 = svc_encode_frame,
.close = svc_encode_close,
.capabilities = AV_CODEC_CAP_AUTO_THREADS,
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE },
.priv_class = &class,
};
h264_omx的注冊(cè)是這樣的:
AVCodec ff_h264_omx_encoder = {
.name = "h264_omx",
.long_name = NULL_IF_CONFIG_SMALL("OpenMAX IL H.264 video encoder"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(OMXCodecContext),
.init = omx_encode_init,
.encode2 = omx_encode_frame,
.close = omx_encode_end,
.pix_fmts = omx_encoder_pix_fmts,
.capabilities = AV_CODEC_CAP_DELAY,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
.priv_class = &omx_h264enc_class,
};
avcodec_alloc_context3再解碼流程中已經(jīng)分析過(guò)汹买。
根據(jù)DEMO中的流程,找到匹配ID為AV_CODEC_ID_H264編碼器并初始化編碼器參數(shù)后聊倔,接下來(lái)就是去打開這個(gè)編碼器晦毙,avcodec_open2在解碼流程中已經(jīng)分析過(guò),去打開對(duì)應(yīng)的編碼器耙蔑;av_frame_alloc也是已經(jīng)分析過(guò)的函數(shù)见妒,可以看到和解碼器的差別就是AVCodecContext和AVFrame作為編碼器配置了很多編碼參數(shù),如寬高甸陌,比特率等參數(shù)的配置须揣。另外在編碼配置中av_image_alloc出現(xiàn)在我們眼前:
/**libavutil/imgutils.c*/
int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
int w, int h, enum AVPixelFormat pix_fmt, int align)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
int i, ret;
uint8_t *buf;
if (!desc)
return AVERROR(EINVAL);
if ((ret = av_image_check_size(w, h, 0, NULL)) < 0)
return ret;
if ((ret = av_image_fill_linesizes(linesizes, pix_fmt, align>7 ? FFALIGN(w, 8) : w)) < 0)
return ret;
for (i = 0; i < 4; i++)
linesizes[i] = FFALIGN(linesizes[i], align);
if ((ret = av_image_fill_pointers(pointers, pix_fmt, h, NULL, linesizes)) < 0)
return ret;
buf = av_malloc(ret + align);
if (!buf)
return AVERROR(ENOMEM);
if ((ret = av_image_fill_pointers(pointers, pix_fmt, h, buf, linesizes)) < 0) {
av_free(buf);
return ret;
}
if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {
avpriv_set_systematic_pal2((uint32_t*)pointers[1], pix_fmt);
if (align < 4) {
av_log(NULL, AV_LOG_ERROR, "Formats with a palette require a minimum alignment of 4\n");
return AVERROR(EINVAL);
}
}
if ((desc->flags & AV_PIX_FMT_FLAG_PAL ||
desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) &&
pointers[1] - pointers[0] > linesizes[0] * h) {
/* zero-initialize the padding before the palette */
memset(pointers[0] + linesizes[0] * h, 0,
pointers[1] - pointers[0] - linesizes[0] * h);
}
return ret;
}
av_init_packet去創(chuàng)建并初始化一個(gè)AVPacket結(jié)構(gòu)體。關(guān)于編碼DEMO源碼中的這個(gè)代碼钱豁,計(jì)算的數(shù)據(jù)信息代表什么還未分析:
/* prepare a dummy image */
/* Y */
for (y = 0; y < c->height; y++) {
for (x = 0; x < c->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
}
}
/* Cb and Cr */
for (y = 0; y < c->height/2; y++) {
for (x = 0; x < c->width/2; x++) {
frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
}
}
frame->pts = i;
相對(duì)于解碼avcodec_decode_video2耻卡,編碼有avcodec_encode_video2:
int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx,
AVPacket *avpkt,
const AVFrame *frame,
int *got_packet_ptr)
{
int ret;
AVPacket user_pkt = *avpkt;
int needs_realloc = !user_pkt.data;
*got_packet_ptr = 0;
if (!avctx->codec->encode2) {
av_log(avctx, AV_LOG_ERROR, "This encoder requires using the avcodec_send_frame() API.\n");
return AVERROR(ENOSYS);
}
if(CONFIG_FRAME_THREAD_ENCODER &&
avctx->internal->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))
return ff_thread_video_encode_frame(avctx, avpkt, frame, got_packet_ptr);
if ((avctx->flags&AV_CODEC_FLAG_PASS1) && avctx->stats_out)
avctx->stats_out[0] = '\0';
if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY) && !frame) {
av_packet_unref(avpkt);
av_init_packet(avpkt);
avpkt->size = 0;
return 0;
}
if (av_image_check_size(avctx->width, avctx->height, 0, avctx))
return AVERROR(EINVAL);
if (frame && frame->format == AV_PIX_FMT_NONE)
av_log(avctx, AV_LOG_WARNING, "AVFrame.format is not set\n");
if (frame && (frame->width == 0 || frame->height == 0))
av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
av_assert0(avctx->codec->encode2);
ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr);
av_assert0(ret <= 0);
emms_c();
if (avpkt->data && avpkt->data == avctx->internal->byte_buffer) {
needs_realloc = 0;
if (user_pkt.data) {
if (user_pkt.size >= avpkt->size) {
memcpy(user_pkt.data, avpkt->data, avpkt->size);
} else {
av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
avpkt->size = user_pkt.size;
ret = -1;
}
avpkt->buf = user_pkt.buf;
avpkt->data = user_pkt.data;
} else {
if (av_dup_packet(avpkt) < 0) {
ret = AVERROR(ENOMEM);
}
}
}
if (!ret) {
if (!*got_packet_ptr)
avpkt->size = 0;
else if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
avpkt->pts = avpkt->dts = frame->pts;
if (needs_realloc && avpkt->data) {
ret = av_buffer_realloc(&avpkt->buf, avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE);
if (ret >= 0)
avpkt->data = avpkt->buf->data;
}
avctx->frame_number++;
}
if (ret < 0 || !*got_packet_ptr)
av_packet_unref(avpkt);
return ret;
}
同樣編碼也是分為兩種模式:
1.ff_thread_video_encode_frame
多線程編碼
2.codec->encode2
調(diào)用編碼器的編碼API,從之前描述的libopenh264編碼器注冊(cè)可以看到牲尺,對(duì)應(yīng)H264的編碼可調(diào)用到svc_encode_frame:
/**libavcodec/libopenh264enc.c*/
static int svc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet)
{
SVCContext *s = avctx->priv_data;
SFrameBSInfo fbi = { 0 };
int i, ret;
int encoded;
SSourcePicture sp = { 0 };
int size = 0, layer, first_layer = 0;
int layer_size[MAX_LAYER_NUM_OF_FRAME] = { 0 };
sp.iColorFormat = videoFormatI420;
for (i = 0; i < 3; i++) {
sp.iStride[i] = frame->linesize[i];
sp.pData[i] = frame->data[i];
}
sp.iPicWidth = avctx->width;
sp.iPicHeight = avctx->height;
encoded = (*s->encoder)->EncodeFrame(s->encoder, &sp, &fbi);
if (encoded != cmResultSuccess) {
av_log(avctx, AV_LOG_ERROR, "EncodeFrame failed\n");
return AVERROR_UNKNOWN;
}
if (fbi.eFrameType == videoFrameTypeSkip) {
s->skipped++;
av_log(avctx, AV_LOG_DEBUG, "frame skipped\n");
return 0;
}
first_layer = 0;
// Normal frames are returned with one single layer, while IDR
// frames have two layers, where the first layer contains the SPS/PPS.
// If using global headers, don't include the SPS/PPS in the returned
// packet - thus, only return one layer.
if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
first_layer = fbi.iLayerNum - 1;
for (layer = first_layer; layer < fbi.iLayerNum; layer++) {
for (i = 0; i < fbi.sLayerInfo[layer].iNalCount; i++)
layer_size[layer] += fbi.sLayerInfo[layer].pNalLengthInByte[i];
size += layer_size[layer];
}
av_log(avctx, AV_LOG_DEBUG, "%d slices\n", fbi.sLayerInfo[fbi.iLayerNum - 1].iNalCount);
if ((ret = ff_alloc_packet2(avctx, avpkt, size, size))) {
av_log(avctx, AV_LOG_ERROR, "Error getting output packet\n");
return ret;
}
size = 0;
for (layer = first_layer; layer < fbi.iLayerNum; layer++) {
memcpy(avpkt->data + size, fbi.sLayerInfo[layer].pBsBuf, layer_size[layer]);
size += layer_size[layer];
}
avpkt->pts = frame->pts;
if (fbi.eFrameType == videoFrameTypeIDR)
avpkt->flags |= AV_PKT_FLAG_KEY;
*got_packet = 1;
return 0;
}
到這里就懵逼了(*s->encoder)->EncodeFrame(s->encoder, &sp, &fbi)調(diào)用到哪啊卵酪,整個(gè)源碼中也沒(méi)搜到...,暫時(shí)理解為完成了編碼吧幌蚊,最后編碼的數(shù)據(jù)寫入到AVPacket,在編碼的針對(duì)MPEG的編碼在最后添加了一個(gè)特殊的幀:
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
這樣編碼后的MPEG文件才能正常播放溃卡。
以上是對(duì)視頻的解碼和編碼源碼流程的簡(jiǎn)單分析溢豆,音頻的編解碼流程也是一樣,如果有興趣讀者可以自己參考decoding_encoding.c中對(duì)音頻解碼的流程瘸羡。
3.結(jié)束語(yǔ)
本文開始分析FFMPEG API實(shí)現(xiàn)編解碼漩仙,對(duì)真正的掌握FFMPEG,通過(guò)FFMPEG來(lái)實(shí)現(xiàn)業(yè)務(wù)需求還任重而道遠(yuǎn)犹赖,本文后會(huì)繼續(xù)更新如fliter讯赏,scale音視頻相關(guān)處理的文章,謝謝關(guān)注冷尉!
<<FFMPEG學(xué)習(xí)教程系列>>這個(gè)教程系列旨在和大家一起來(lái)學(xué)習(xí)FFMPEG,文章中不免有些遺漏或者錯(cuò)誤的地方系枪,希望大家可以提出來(lái)雀哨,一起學(xué)習(xí)。