Created
January 12, 2019 19:59
-
-
Save nlgranger/377e4309db3bdfd949bf0581b1a4dd99 to your computer and use it in GitHub Desktop.
ffmpeg demo using modern decoding API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <libavutil/avutil.h> | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
char err_str[1024]; | |
int decode_frames(AVCodecContext* decoder_ctx, AVFrame* frame) { | |
int nb_decoded = 0; | |
int ret = 0; | |
while (ret == 0) { | |
ret = avcodec_receive_frame(decoder_ctx, frame); | |
if (ret == AVERROR(EAGAIN)) { // no more content in this packet | |
printf("end of packet, read %2d frames\n", nb_decoded); | |
return 0; | |
} else if (ret < 0) { // error | |
break; | |
} else { | |
nb_decoded++; | |
printf("decoded frame %4d of size %3d x %3d\n", | |
decoder_ctx->frame_number, frame->width, frame->height); | |
} | |
} | |
return ret; | |
} | |
int main(int argc, char* argv[]) { | |
// Open container | |
AVFormatContext* fmt_ctx; | |
fmt_ctx = avformat_alloc_context(); | |
if (fmt_ctx == NULL) { | |
printf("failed to allocate format context\n"); | |
return 0; | |
} | |
int ret = avformat_open_input(&fmt_ctx, argv[argc-1], NULL, NULL); | |
if (ret != 0) { | |
printf("failed to open input file %s\n", argv[argc-1]); | |
goto free_fmt_ctx; | |
} | |
// Parse header/probe content | |
ret = avformat_find_stream_info(fmt_ctx, NULL); | |
if (ret != 0) { | |
printf("failed to parse stream info\n"); | |
goto close_fmt_ctx; | |
} | |
// Open video stream | |
int stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); | |
if (stream_idx < 0) { | |
printf("failed to find stream\n"); | |
goto close_fmt_ctx; | |
} | |
AVCodecParameters* codecpar = fmt_ctx->streams[stream_idx]->codecpar; | |
enum AVMediaType media_type = codecpar->codec_type; | |
// allocate/configure/initialize decoder context | |
AVCodec* codec; | |
codec = avcodec_find_decoder(codecpar->codec_id); | |
if (codec == NULL) { | |
printf("failed to load codec for stream %d\n", stream_idx); | |
goto close_fmt_ctx; | |
} | |
AVCodecContext* decoder_ctx; | |
decoder_ctx = avcodec_alloc_context3(codec); | |
if (decoder_ctx == NULL) { | |
printf("failed to allocate decoder\n"); | |
goto close_decoder; | |
}; | |
ret = avcodec_parameters_to_context(decoder_ctx, codecpar); | |
if (ret < 0) { | |
printf("failed to configure decoder\n"); | |
goto close_decoder; | |
} | |
ret = avcodec_open2(decoder_ctx, codec, NULL); | |
if (ret < 0) { | |
printf("failed to initialize decoder\n"); | |
goto close_decoder; | |
} | |
// Decode frames | |
AVPacket pkt; | |
av_init_packet(&pkt); | |
pkt.data = NULL; | |
pkt.size = 0; | |
AVFrame* frame; | |
frame = av_frame_alloc(); | |
if (frame == NULL) { | |
printf("failed to allocate frame\n"); | |
goto close_decoder; | |
} | |
while (1) { | |
// Fetch a packet | |
ret = av_read_frame(fmt_ctx, &pkt); | |
while (ret == 0 && pkt.stream_index != stream_idx) { | |
av_packet_unref(&pkt); | |
ret = av_read_frame(fmt_ctx, &pkt); | |
} | |
if (ret != 0) { | |
printf("failed to read packet or end of file\n"); | |
goto free_frame; | |
} else { | |
printf("decoded packet at timestamp %d\n", pkt.pts); | |
} | |
// Send packet to decoder | |
ret = avcodec_send_packet(decoder_ctx, &pkt); | |
// process previous packet first | |
if (ret == AVERROR(EAGAIN)) { | |
printf("must finish current packet first"); | |
ret = decode_frames(decoder_ctx, frame); | |
if (ret < 0) { | |
goto free_frame; | |
} | |
ret = avcodec_send_packet(decoder_ctx, &pkt); | |
} | |
// error cases | |
if (ret == AVERROR_EOF) { | |
printf("no more packets\n"); | |
goto free_frame; | |
} else if (ret == AVERROR(EINVAL)) { | |
printf("problem with decoder\n"); | |
goto free_frame; | |
} else if (ret == AVERROR(ENOMEM)) { | |
printf("failed to push packet to decoder\n"); | |
goto free_frame; | |
} else if (ret < 0) { | |
printf("some packet decoding error\n"); | |
goto free_frame; | |
} | |
// Decode frames | |
ret = decode_frames(decoder_ctx, frame); | |
if (ret < 0) { | |
goto free_frame; | |
} | |
av_packet_unref(&pkt); | |
} | |
// Cleanup | |
free_frame: | |
av_packet_unref(&pkt); | |
av_frame_free(&frame); | |
close_decoder: | |
avcodec_flush_buffers(decoder_ctx); | |
avcodec_free_context(&decoder_ctx); | |
close_fmt_ctx: | |
avformat_close_input(&fmt_ctx); | |
free_fmt_ctx: | |
avformat_free_context(fmt_ctx); | |
if (ret != 0) { | |
err_str[0] = '\n'; | |
av_strerror(ret, err_str, sizeof(err_str)); | |
printf("%s\n", err_str); | |
} | |
return 0; | |
} | |
// compile with: gcc -lavutil -lavcodec -lavformat decode.c |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment