Commit 66aa6340 authored by Stefan Westerfeld's avatar Stefan Westerfeld

Implement video aac expermintal libavcodec decoder.

Signed-off-by: Stefan Westerfeld's avatarStefan Westerfeld <stefan@space.twc.de>
parent b37776df
......@@ -17,6 +17,14 @@
#include <string.h>
#include <stdio.h>
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/timestamp.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#undef av_err2str
#define av_err2str(errnum) av_make_error_string((char*)__builtin_alloca(AV_ERROR_MAX_STRING_SIZE), AV_ERROR_MAX_STRING_SIZE, errnum)
}
#include <regex>
......@@ -45,6 +53,130 @@ xsystem (const string& cmd)
return Error::Code::NONE;
}
/* Enable or disable frame reference counting. You are not supposed to support
* both paths in your application but pick the one most appropriate to your
* needs. Look for the use of refcount in this example to see what are the
* differences of API usage between them. */
static int refcount = 0;
static int open_codec_context(int *stream_idx,
AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
{
int ret, stream_index;
AVStream *st;
AVCodec *dec = NULL;
AVDictionary *opts = NULL;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
if (ret < 0) {
error ("Could not find %s stream in input file\n", av_get_media_type_string(type));
return ret;
} else {
stream_index = ret;
st = fmt_ctx->streams[stream_index];
/* find decoder for the stream */
dec = avcodec_find_decoder(st->codecpar->codec_id);
if (!dec) {
fprintf(stderr, "Failed to find %s codec\n",
av_get_media_type_string(type));
return AVERROR(EINVAL);
}
/* Allocate a codec context for the decoder */
*dec_ctx = avcodec_alloc_context3(dec);
if (!*dec_ctx) {
fprintf(stderr, "Failed to allocate the %s codec context\n",
av_get_media_type_string(type));
return AVERROR(ENOMEM);
}
/* Copy codec parameters from input stream to output codec context */
if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
av_get_media_type_string(type));
return ret;
}
/* Init the decoders, with or without reference counting */
av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);
if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
fprintf(stderr, "Failed to open %s codec\n",
av_get_media_type_string(type));
return ret;
}
*stream_idx = stream_index;
}
return 0;
}
static int audio_frame_count = 0;
AVCodecContext *audio_dec_ctx = nullptr;
AVFrame *frame = nullptr;
int audio_stream_idx = -1;
AVPacket pkt = {0,};
static int decode_packet(int *got_frame, int cached)
{
int ret = 0;
int decoded = pkt.size;
*got_frame = 0;
if (pkt.stream_index == audio_stream_idx) {
/* decode audio frame */
ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
fprintf(stderr, "Error decoding audio frame (%s)\n", av_err2str(ret));
return ret;
}
/* Some audio decoders decode only part of the packet, and have to be
* called again with the remainder of the packet data.
* Sample: fate-suite/lossless-audio/luckynight-partial.shn
* Also, some decoders might over-read the packet. */
decoded = FFMIN(ret, pkt.size);
if (*got_frame) {
printf("audio_frame%s n:%d nb_samples:%d\n",
cached ? "(cached)" : "",
audio_frame_count++, frame->nb_samples);
printf ("%s\n", av_get_sample_fmt_name (AVSampleFormat (frame->format)));
// Set up SWR context once you've got codec information
static SwrContext *swr = 0;
if (!swr)
{
swr=swr_alloc();
av_opt_set_int(swr, "in_channel_layout", frame->channel_layout, 0);
av_opt_set_int(swr, "out_channel_layout", frame->channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", frame->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", frame->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AVSampleFormat (frame->format), 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
swr_init(swr);
}
float o[frame->nb_samples * frame->channels];
uint8_t *ut = (uint8_t *) o;
swr_convert(swr, &ut, frame->nb_samples, (const uint8_t**) frame->extended_data, frame->nb_samples);
for (int i = 0; i < frame->nb_samples; i++)
{
for (int j = 0; j < frame->channels; j++)
printf ("%f ", o[i * frame->channels + j]);
printf (" #audio\n");
}
}
}
/* If we use frame reference counting, we own the data and need
* to de-reference it when we don't use it anymore */
if (*got_frame && refcount)
av_frame_unref(frame);
return decoded;
}
Error
ff_decode (const string& filename, const TSReader& reader, WavData& out_wav_data)
{
......@@ -81,6 +213,50 @@ ff_decode (const string& filename, const TSReader& reader, WavData& out_wav_data
return Error (string_printf ("unable to write ff_decode:next.ts to %s\n", input_tmp_file_name.c_str()));
}
fflush (input_tmp_file);
static AVFormatContext *fmt_ctx = NULL;
static AVStream *audio_stream = NULL;
if (avformat_open_input (&fmt_ctx, input_tmp_file_name.c_str(), NULL, NULL) < 0)
return Error (string_printf ("open input %s failed", input_tmp_file_name.c_str()));
if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
return Error ("could not find stream information");
if (open_codec_context (&audio_stream_idx, &audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0)
{
audio_stream = fmt_ctx->streams[audio_stream_idx];
}
if (!audio_stream)
return Error ("could not find audio stream");
frame = av_frame_alloc();
if (!frame)
return Error ("could not allocate frame");
/* initialize packet, set data to NULL, let the demuxer fill it */
av_init_packet (&pkt);
pkt.data = NULL;
pkt.size = 0;
/* read frames from the file */
int got_frame;
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
AVPacket orig_pkt = pkt;
do {
int ret = decode_packet(&got_frame, 0);
if (ret < 0)
break;
pkt.data += ret;
pkt.size -= ret;
} while (pkt.size > 0);
av_packet_unref(&orig_pkt);
}
/* flush cached frames */
pkt.data = NULL;
pkt.size = 0;
do {
decode_packet(&got_frame, 1);
} while (got_frame);
string cmd = string_printf ("ffmpeg -v error -y -f mpegts -i %s -f wav %s", input_tmp_file_name.c_str(), tmp_file_name.c_str());
Error err = xsystem (cmd.c_str());
if (err)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment