all repos — mgba @ c34e3246742754317d05d6117c2df3ca2d71e4e8

mGBA Game Boy Advance Emulator

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}