all repos — mgba @ 1b755e17d062cef0e94c1e509e58a62e3a94d03a

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 = 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}