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 = -1;
83 decoder->height = -1;
84#if LIBAVCODEC_VERSION_MAJOR >= 55
85 decoder->videoFrame = av_frame_alloc();
86#else
87 decoder->videoFrame = avcodec_alloc_frame();
88#endif
89 }
90
91 if (type == AVMEDIA_TYPE_AUDIO) {
92 decoder->audioStream = i;
93#if LIBAVCODEC_VERSION_MAJOR >= 55
94 decoder->audioFrame = av_frame_alloc();
95#else
96 decoder->audioFrame = avcodec_alloc_frame();
97#endif
98 }
99 }
100 return true;
101}
102
103void FFmpegDecoderClose(struct FFmpegDecoder* decoder) {
104 if (decoder->audioFrame) {
105#if LIBAVCODEC_VERSION_MAJOR >= 55
106 av_frame_free(&decoder->audioFrame);
107#else
108 avcodec_free_frame(&decoder->audioFrame);
109#endif
110 }
111
112 if (decoder->audio) {
113#ifdef FFMPEG_USE_CODECPAR
114 avcodec_free_context(&decoder->audio);
115#else
116 avcodec_close(decoder->audio);
117 decoder->audio = NULL;
118#endif
119 }
120
121 if (decoder->scaleContext) {
122 sws_freeContext(decoder->scaleContext);
123 decoder->scaleContext = NULL;
124 }
125
126 if (decoder->videoFrame) {
127#if LIBAVCODEC_VERSION_MAJOR >= 55
128 av_frame_free(&decoder->videoFrame);
129#else
130 avcodec_free_frame(&decoder->videoFrame);
131#endif
132 }
133
134 if (decoder->pixels) {
135 free(decoder->pixels);
136 decoder->pixels = NULL;
137 }
138
139 if (decoder->video) {
140#ifdef FFMPEG_USE_CODECPAR
141 avcodec_free_context(&decoder->video);
142#else
143 avcodec_close(decoder->video);
144 decoder->video = NULL;
145#endif
146 }
147
148 if (decoder->context) {
149 avformat_close_input(&decoder->context);
150 }
151}
152
153bool FFmpegDecoderIsOpen(struct FFmpegDecoder* decoder) {
154 return !!decoder->context;
155}
156
157bool FFmpegDecoderRead(struct FFmpegDecoder* decoder) {
158 bool readPacket = false;
159 while (!readPacket) {
160 AVPacket packet = {
161 .stream_index = -2
162 };
163 if (av_read_frame(decoder->context, &packet) < 0) {
164 break;
165 }
166
167 readPacket = true;
168 if (packet.stream_index == decoder->audioStream) {
169 // TODO
170 } else if (packet.stream_index == decoder->videoStream) {
171#ifdef FFMPEG_USE_CODECPAR
172 if (avcodec_send_packet(decoder->video, &packet) < 0) {
173 // TODO
174 }
175 if (avcodec_receive_frame(decoder->video, decoder->videoFrame) < 0) {
176 readPacket = false;
177 }
178#else
179 int gotData;
180 if (avcodec_decode_video2(decoder->video, decoder->videoFrame, &gotData, &packet) < 0 || !gotData) {
181 readPacket = false;
182 }
183#endif
184 if (readPacket) {
185 if (decoder->width != decoder->videoFrame->width || decoder->height != decoder->videoFrame->height) {
186 decoder->width = decoder->videoFrame->width;
187 decoder->height = decoder->videoFrame->height;
188 if (decoder->out->videoDimensionsChanged) {
189 decoder->out->videoDimensionsChanged(decoder->out, decoder->width, decoder->height);
190 }
191 if (decoder->pixels) {
192 free(decoder->pixels);
193 }
194 decoder->pixels = calloc(decoder->width * decoder->height, BYTES_PER_PIXEL);
195 if (decoder->scaleContext) {
196 sws_freeContext(decoder->scaleContext);
197 decoder->scaleContext = NULL;
198 }
199 }
200 if (decoder->out->postVideoFrame) {
201 if (!decoder->scaleContext) {
202 decoder->scaleContext = sws_getContext(decoder->width, decoder->height, decoder->videoFrame->format,
203 decoder->width, decoder->height, AV_PIX_FMT_BGR32,
204 SWS_POINT, 0, 0, 0);
205 }
206 int stride = decoder->width * BYTES_PER_PIXEL;
207 sws_scale(decoder->scaleContext, (const uint8_t* const*) decoder->videoFrame->data, decoder->videoFrame->linesize, 0, decoder->videoFrame->height, &decoder->pixels, &stride);
208 decoder->out->postVideoFrame(decoder->out, (const color_t*) decoder->pixels, decoder->width);
209 }
210 }
211 }
212#ifdef FFMPEG_USE_PACKET_UNREF
213 av_packet_unref(&packet);
214#else
215 av_free_packet(&packet);
216#endif
217 }
218 return readPacket;
219}