src/feature/ffmpeg/ffmpeg-decoder.c (view raw)
1/* Copyright (c) 2013-2020 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "ffmpeg-decoder.h"
7
8#include <libswscale/swscale.h>
9
10void FFmpegDecoderInit(struct FFmpegDecoder* decoder) {
11#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
12 av_register_all();
13#endif
14
15 memset(decoder, 0, sizeof(*decoder));
16 decoder->audioStream = -1;
17 decoder->videoStream = -1;
18}
19
20bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) {
21 if (FFmpegDecoderIsOpen(decoder)) {
22 return false;
23 }
24
25 if (avformat_open_input(&decoder->context, infile, NULL, NULL) < 0) {
26 return false;
27 }
28
29 if (avformat_find_stream_info(decoder->context, NULL) < 0) {
30 FFmpegDecoderClose(decoder);
31 return false;
32 }
33
34 unsigned i;
35 for (i = 0; i < decoder->context->nb_streams; ++i) {
36#ifdef FFMPEG_USE_CODECPAR
37 enum AVMediaType type = decoder->context->streams[i]->codecpar->codec_type;
38#else
39 enum AVMediaType type = decoder->context->streams[i]->codec->codec_type;
40#endif
41 struct AVCodec* codec;
42 struct AVCodecContext* context = NULL;
43 if (type == AVMEDIA_TYPE_VIDEO && decoder->videoStream < 0) {
44 decoder->video = avcodec_alloc_context3(NULL);
45 if (!decoder->video) {
46 FFmpegDecoderClose(decoder);
47 return false;
48 }
49 context = decoder->video;
50 }
51
52 if (type == AVMEDIA_TYPE_AUDIO && decoder->audioStream < 0) {
53 decoder->audio = avcodec_alloc_context3(NULL);
54 if (!decoder->audio) {
55 FFmpegDecoderClose(decoder);
56 return false;
57 }
58 context = decoder->audio;
59 }
60 if (!context) {
61 continue;
62 }
63
64#ifdef FFMPEG_USE_CODECPAR
65 if (avcodec_parameters_to_context(context, decoder->context->streams[i]->codecpar) < 0) {
66 FFmpegDecoderClose(decoder);
67 return false;
68 }
69#endif
70 codec = avcodec_find_decoder(context->codec_id);
71 if (!codec) {
72 FFmpegDecoderClose(decoder);
73 return false;
74 }
75 if (avcodec_open2(context, codec, NULL) < 0) {
76 FFmpegDecoderClose(decoder);
77 return false;
78 }
79
80 if (type == AVMEDIA_TYPE_VIDEO) {
81 decoder->videoStream = i;
82 decoder->width = context->coded_width;
83 decoder->height = context->coded_height;
84 if (decoder->out->videoDimensionsChanged) {
85 decoder->out->videoDimensionsChanged(decoder->out, decoder->width, decoder->height);
86 }
87#if LIBAVCODEC_VERSION_MAJOR >= 55
88 decoder->videoFrame = av_frame_alloc();
89#else
90 decoder->videoFrame = avcodec_alloc_frame();
91#endif
92 decoder->pixels = malloc(decoder->width * decoder->height * BYTES_PER_PIXEL);
93 }
94
95 if (type == AVMEDIA_TYPE_AUDIO) {
96 decoder->audioStream = i;
97#if LIBAVCODEC_VERSION_MAJOR >= 55
98 decoder->audioFrame = av_frame_alloc();
99#else
100 decoder->audioFrame = avcodec_alloc_frame();
101#endif
102 }
103 }
104 return true;
105}
106
107void FFmpegDecoderClose(struct FFmpegDecoder* decoder) {
108 if (decoder->audioFrame) {
109#if LIBAVCODEC_VERSION_MAJOR >= 55
110 av_frame_free(&decoder->audioFrame);
111#else
112 avcodec_free_frame(&decoder->audioFrame);
113#endif
114 }
115
116 if (decoder->audio) {
117#ifdef FFMPEG_USE_CODECPAR
118 avcodec_free_context(&decoder->audio);
119#else
120 avcodec_close(decoder->audio);
121 decoder->audio = NULL;
122#endif
123 }
124
125 if (decoder->scaleContext) {
126 sws_freeContext(decoder->scaleContext);
127 decoder->scaleContext = NULL;
128 }
129
130 if (decoder->videoFrame) {
131#if LIBAVCODEC_VERSION_MAJOR >= 55
132 av_frame_free(&decoder->videoFrame);
133#else
134 avcodec_free_frame(&decoder->videoFrame);
135#endif
136 }
137
138 if (decoder->pixels) {
139 free(decoder->pixels);
140 decoder->pixels = NULL;
141 }
142
143 if (decoder->video) {
144#ifdef FFMPEG_USE_CODECPAR
145 avcodec_free_context(&decoder->video);
146#else
147 avcodec_close(decoder->video);
148 decoder->video = NULL;
149#endif
150 }
151
152 if (decoder->context) {
153 avformat_close_input(&decoder->context);
154 }
155}
156
157bool FFmpegDecoderIsOpen(struct FFmpegDecoder* decoder) {
158 return !!decoder->context;
159}
160
161bool FFmpegDecoderRead(struct FFmpegDecoder* decoder) {
162 bool readPacket = false;
163 while (!readPacket) {
164 AVPacket packet;
165 if (av_read_frame(decoder->context, &packet) < 0) {
166 break;
167 }
168
169 readPacket = true;
170 if (packet.stream_index == decoder->audioStream) {
171 // TODO
172 } else if (packet.stream_index == decoder->videoStream) {
173#ifdef FFMPEG_USE_CODECPAR
174 if (avcodec_send_packet(decoder->video, &packet) < 0) {
175 // TODO
176 }
177 if (avcodec_receive_frame(decoder->video, decoder->videoFrame) < 0) {
178 readPacket = false;
179 }
180#else
181 int gotData;
182 if (avcodec_decode_video2(decoder->video, decoder->videoFrame, &gotData, &packet) < 0 || !gotData) {
183 readPacket = false;
184 }
185#endif
186 if (readPacket && decoder->out->postVideoFrame) {
187 if (!decoder->scaleContext) {
188 decoder->scaleContext = sws_getContext(decoder->width, decoder->height, decoder->videoFrame->format,
189 decoder->width, decoder->height, AV_PIX_FMT_BGR32,
190 SWS_POINT, 0, 0, 0);
191 }
192 int stride = decoder->width * BYTES_PER_PIXEL;
193 sws_scale(decoder->scaleContext, (const uint8_t* const*) decoder->videoFrame->data, decoder->videoFrame->linesize, 0, decoder->videoFrame->height, &decoder->pixels, &stride);
194 decoder->out->postVideoFrame(decoder->out, (const color_t*) decoder->pixels, decoder->width);
195 }
196 }
197#ifdef FFMPEG_USE_PACKET_UNREF
198 av_packet_unref(&packet);
199#else
200 av_free_packet(&packet);
201#endif
202 }
203 return readPacket;
204}