blob: c0a0a25a98c4367ac8a779b8ee20a3fc0952de8e [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/mediaplayer/ffmpeg/av_codec_context.h"
#include "garnet/bin/mediaplayer/ffmpeg/ffmpeg_init.h"
#include "garnet/bin/mediaplayer/graph/types/audio_stream_type.h"
#include "garnet/bin/mediaplayer/graph/types/subpicture_stream_type.h"
#include "garnet/bin/mediaplayer/graph/types/text_stream_type.h"
#include "garnet/bin/mediaplayer/graph/types/video_stream_type.h"
#include "lib/fxl/logging.h"
extern "C" {
#include "libavformat/avformat.h"
}
namespace media_player {
namespace {
// Converts an AVSampleFormat into an AudioStreamType::SampleFormat.
AudioStreamType::SampleFormat Convert(AVSampleFormat av_sample_format) {
switch (av_sample_format) {
case AV_SAMPLE_FMT_NONE:
return AudioStreamType::SampleFormat::kNone;
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
return AudioStreamType::SampleFormat::kUnsigned8;
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
return AudioStreamType::SampleFormat::kSigned16;
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
return AudioStreamType::SampleFormat::kSigned24In32;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
return AudioStreamType::SampleFormat::kFloat;
case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_DBLP:
case AV_SAMPLE_FMT_NB:
default:
FXL_LOG(ERROR) << "unsupported av_sample_format " << av_sample_format;
abort();
}
}
// Copies a buffer from Bytes into context->extradata. The result is malloc'ed
// and must be freed.
void ExtraDataFromBytes(const Bytes& bytes, const AvCodecContextPtr& context) {
size_t byte_count = bytes.size();
uint8_t* copy = reinterpret_cast<uint8_t*>(malloc(byte_count));
std::memcpy(copy, bytes.data(), byte_count);
context->extradata = copy;
context->extradata_size = byte_count;
}
// Gets the encoding from a codec_id.
const char* EncodingFromCodecId(AVCodecID from) {
switch (from) {
case AV_CODEC_ID_AAC:
return StreamType::kAudioEncodingAac;
case AV_CODEC_ID_AMR_NB:
return StreamType::kAudioEncodingAmrNb;
case AV_CODEC_ID_AMR_WB:
return StreamType::kAudioEncodingAmrWb;
case AV_CODEC_ID_APTX:
return StreamType::kAudioEncodingAptX;
case AV_CODEC_ID_FLAC:
return StreamType::kAudioEncodingFlac;
case AV_CODEC_ID_GSM_MS:
return StreamType::kAudioEncodingGsmMs;
case AV_CODEC_ID_MP3:
return StreamType::kAudioEncodingMp3;
case AV_CODEC_ID_PCM_ALAW:
return StreamType::kAudioEncodingPcmALaw;
case AV_CODEC_ID_PCM_MULAW:
return StreamType::kAudioEncodingPcmMuLaw;
case AV_CODEC_ID_SBC:
return StreamType::kAudioEncodingSbc;
case AV_CODEC_ID_VORBIS:
return StreamType::kAudioEncodingVorbis;
case AV_CODEC_ID_H263:
return StreamType::kVideoEncodingH263;
case AV_CODEC_ID_H264:
return StreamType::kVideoEncodingH264;
case AV_CODEC_ID_MPEG4:
return StreamType::kVideoEncodingMpeg4;
case AV_CODEC_ID_THEORA:
return StreamType::kVideoEncodingTheora;
case AV_CODEC_ID_VP3:
return StreamType::kVideoEncodingVp3;
case AV_CODEC_ID_VP8:
return StreamType::kVideoEncodingVp8;
case AV_CODEC_ID_VP9:
return StreamType::kVideoEncodingVp9;
default:
FXL_LOG(WARNING) << "unsupported codec_id " << from;
return StreamType::kMediaEncodingUnsupported;
}
}
// Determines if codec_id represents an LPCM audio format.
bool IsLpcm(AVCodecID codec_id) {
switch (codec_id) {
case AV_CODEC_ID_PCM_S16BE:
case AV_CODEC_ID_PCM_S16LE:
case AV_CODEC_ID_PCM_S24BE:
case AV_CODEC_ID_PCM_S24LE:
case AV_CODEC_ID_PCM_U8:
return true;
default:
return false;
}
}
// Creates a StreamType from an AVCodecContext describing an audio type.
std::unique_ptr<StreamType> StreamTypeFromAudioCodecContext(
const AVCodecContext& from) {
bool decoded = from.codec != nullptr || IsLpcm(from.codec_id);
return AudioStreamType::Create(
decoded ? StreamType::kAudioEncodingLpcm
: EncodingFromCodecId(from.codec_id),
(decoded || from.extradata_size == 0)
? nullptr
: Bytes::Create(from.extradata, from.extradata_size),
Convert(from.sample_fmt), from.channels, from.sample_rate);
}
// Creates a StreamType from an AVCodecContext describing an audio type.
std::unique_ptr<StreamType> StreamTypeFromAudioCodecParameters(
const AVCodecParameters& from) {
bool decoded = IsLpcm(from.codec_id);
return AudioStreamType::Create(
decoded ? StreamType::kAudioEncodingLpcm
: EncodingFromCodecId(from.codec_id),
(decoded || from.extradata_size == 0)
? nullptr
: Bytes::Create(from.extradata, from.extradata_size),
Convert(static_cast<AVSampleFormat>(from.format)), from.channels,
from.sample_rate);
}
// Converts AVColorSpace and AVColorRange to ColorSpace.
VideoStreamType::ColorSpace ColorSpaceFromAVColorSpaceAndRange(
AVColorSpace color_space, AVColorRange color_range) {
// TODO(dalesat): Blindly copied from Chromium.
if (color_range == AVCOL_RANGE_JPEG) {
return VideoStreamType::ColorSpace::kJpeg;
}
switch (color_space) {
case AVCOL_SPC_UNSPECIFIED:
return VideoStreamType::ColorSpace::kNotApplicable;
case AVCOL_SPC_BT709:
return VideoStreamType::ColorSpace::kHdRec709;
case AVCOL_SPC_SMPTE170M:
case AVCOL_SPC_BT470BG:
return VideoStreamType::ColorSpace::kSdRec601;
default:
return VideoStreamType::ColorSpace::kUnknown;
}
}
// Creates a StreamType from an AVCodecContext describing a video type.
std::unique_ptr<StreamType> StreamTypeFromVideoCodecContext(
const AVCodecContext& from) {
int coded_width = from.coded_width;
int coded_height = from.coded_height;
avcodec_align_dimensions(const_cast<AVCodecContext*>(&from), &coded_width,
&coded_height);
FXL_DCHECK(coded_width >= from.coded_width);
FXL_DCHECK(coded_height >= from.coded_height);
uint32_t aspect_ratio_width = from.sample_aspect_ratio.num;
uint32_t aspect_ratio_height = from.sample_aspect_ratio.den;
if (aspect_ratio_width == 0 || aspect_ratio_height == 0) {
aspect_ratio_width = 1;
aspect_ratio_height = 1;
}
uint32_t line_stride;
switch (from.pix_fmt) {
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P:
line_stride = coded_width;
break;
default:
line_stride = 0;
FXL_LOG(FATAL) << "Unrecognized pixel format " << from.pix_fmt;
}
return VideoStreamType::Create(
from.codec == nullptr ? EncodingFromCodecId(from.codec_id)
: StreamType::kVideoEncodingUncompressed,
(from.codec != nullptr || from.extradata_size == 0)
? nullptr
: Bytes::Create(from.extradata, from.extradata_size),
PixelFormatFromAVPixelFormat(from.pix_fmt),
ColorSpaceFromAVColorSpaceAndRange(from.colorspace, from.color_range),
from.width, from.height, coded_width, coded_height, aspect_ratio_width,
aspect_ratio_height, line_stride);
}
// Creates a StreamType from an AVStream describing a video type.
std::unique_ptr<StreamType> StreamTypeFromVideoStream(const AVStream& from) {
const AVCodecParameters parameters = *from.codecpar;
VideoStreamType::PixelFormat pixel_format = PixelFormatFromAVPixelFormat(
static_cast<AVPixelFormat>(parameters.format));
AVRational pixel_aspect_ratio = {1, 1};
if (from.sample_aspect_ratio.num != 0 && from.sample_aspect_ratio.den != 0) {
pixel_aspect_ratio = from.sample_aspect_ratio;
} else if (parameters.sample_aspect_ratio.num != 0 &&
parameters.sample_aspect_ratio.den != 0) {
pixel_aspect_ratio = parameters.sample_aspect_ratio;
}
return VideoStreamType::Create(
EncodingFromCodecId(parameters.codec_id),
parameters.extradata_size == 0
? nullptr
: Bytes::Create(parameters.extradata, parameters.extradata_size),
pixel_format,
ColorSpaceFromAVColorSpaceAndRange(parameters.color_space,
parameters.color_range),
parameters.width, parameters.height, 0, 0, pixel_aspect_ratio.num,
pixel_aspect_ratio.den, 0);
}
// Creates a StreamType from an AVCodecContext describing a data type.
std::unique_ptr<StreamType> StreamTypeFromDataCodecContext(
const AVCodecContext& from) {
// TODO(dalesat): Implement.
return TextStreamType::Create("UNSUPPORTED TYPE (FFMPEG DATA)", nullptr);
}
// Creates a StreamType from AVCodecParameters describing a data type.
std::unique_ptr<StreamType> StreamTypeFromDataCodecParameters(
const AVCodecParameters& from) {
// TODO(dalesat): Implement.
return TextStreamType::Create("UNSUPPORTED TYPE (FFMPEG DATA)", nullptr);
}
// Creates a StreamType from an AVCodecContext describing a subtitle type.
std::unique_ptr<StreamType> StreamTypeFromSubtitleCodecContext(
const AVCodecContext& from) {
// TODO(dalesat): Implement.
return SubpictureStreamType::Create("UNSUPPORTED TYPE (FFMPEG SUBTITLE)",
nullptr);
}
// Creates a StreamType from AVCodecParameters describing a subtitle type.
std::unique_ptr<StreamType> StreamTypeFromSubtitleCodecParameters(
const AVCodecParameters& from) {
// TODO(dalesat): Implement.
return SubpictureStreamType::Create("UNSUPPORTED TYPE (FFMPEG SUBTITLE)",
nullptr);
}
// Creates an AVCodecContext from an AudioStreamType.
AvCodecContextPtr AVCodecContextFromAudioStreamType(
const AudioStreamType& stream_type) {
FXL_DCHECK(stream_type.medium() == StreamType::Medium::kAudio);
AVCodecID codec_id;
AVSampleFormat sample_format = AV_SAMPLE_FMT_NONE;
if (stream_type.encoding() == StreamType::kAudioEncodingLpcm) {
switch (stream_type.sample_format()) {
case AudioStreamType::SampleFormat::kUnsigned8:
codec_id = AV_CODEC_ID_PCM_U8;
sample_format = AV_SAMPLE_FMT_U8;
break;
case AudioStreamType::SampleFormat::kSigned16:
codec_id = AV_CODEC_ID_PCM_S16LE;
sample_format = AV_SAMPLE_FMT_S16;
break;
case AudioStreamType::SampleFormat::kSigned24In32:
codec_id = AV_CODEC_ID_PCM_S24LE;
sample_format = AV_SAMPLE_FMT_S32;
break;
case AudioStreamType::SampleFormat::kFloat:
codec_id = AV_CODEC_ID_PCM_F32LE;
sample_format = AV_SAMPLE_FMT_FLT;
break;
default:
FXL_LOG(ERROR) << "unsupported sample format";
abort();
}
} else if (stream_type.encoding() == StreamType::kAudioEncodingAac) {
codec_id = AV_CODEC_ID_AAC;
} else if (stream_type.encoding() == StreamType::kAudioEncodingAmrNb) {
codec_id = AV_CODEC_ID_AMR_NB;
} else if (stream_type.encoding() == StreamType::kAudioEncodingAmrWb) {
codec_id = AV_CODEC_ID_AMR_WB;
} else if (stream_type.encoding() == StreamType::kAudioEncodingAptX) {
codec_id = AV_CODEC_ID_APTX;
} else if (stream_type.encoding() == StreamType::kAudioEncodingFlac) {
codec_id = AV_CODEC_ID_FLAC;
} else if (stream_type.encoding() == StreamType::kAudioEncodingGsmMs) {
codec_id = AV_CODEC_ID_GSM_MS;
} else if (stream_type.encoding() == StreamType::kAudioEncodingMp3) {
codec_id = AV_CODEC_ID_MP3;
} else if (stream_type.encoding() == StreamType::kAudioEncodingPcmALaw) {
codec_id = AV_CODEC_ID_PCM_ALAW;
} else if (stream_type.encoding() == StreamType::kAudioEncodingPcmMuLaw) {
codec_id = AV_CODEC_ID_PCM_MULAW;
} else if (stream_type.encoding() == StreamType::kAudioEncodingSbc) {
codec_id = AV_CODEC_ID_SBC;
switch (stream_type.sample_format()) {
case AudioStreamType::SampleFormat::kUnsigned8:
sample_format = AV_SAMPLE_FMT_U8P;
break;
case AudioStreamType::SampleFormat::kSigned16:
sample_format = AV_SAMPLE_FMT_S16P;
break;
case AudioStreamType::SampleFormat::kSigned24In32:
sample_format = AV_SAMPLE_FMT_S32P;
break;
case AudioStreamType::SampleFormat::kFloat:
sample_format = AV_SAMPLE_FMT_FLTP;
break;
default:
FXL_LOG(ERROR) << "unsupported sample format";
abort();
}
} else if (stream_type.encoding() == StreamType::kAudioEncodingVorbis) {
codec_id = AV_CODEC_ID_VORBIS;
} else if (stream_type.encoding() == StreamType::kMediaEncodingUnsupported) {
codec_id = AV_CODEC_ID_NONE;
} else {
FXL_LOG(WARNING) << "unsupported encoding " << stream_type.encoding();
codec_id = AV_CODEC_ID_NONE;
}
AvCodecContextPtr context(avcodec_alloc_context3(nullptr));
context->codec_type = AVMEDIA_TYPE_AUDIO;
context->codec_id = codec_id;
context->sample_fmt = sample_format;
context->channels = stream_type.channels();
context->sample_rate = stream_type.frames_per_second();
if (stream_type.encoding_parameters()) {
ExtraDataFromBytes(*stream_type.encoding_parameters(), context);
}
return context;
}
// Creats an AVCodecContext from a VideoStreamType.
AvCodecContextPtr AVCodecContextFromVideoStreamType(
const VideoStreamType& stream_type) {
AVCodecID codec_id = AV_CODEC_ID_NONE;
if (stream_type.encoding() == StreamType::kVideoEncodingH263) {
codec_id = AV_CODEC_ID_H263;
} else if (stream_type.encoding() == StreamType::kVideoEncodingH264) {
codec_id = AV_CODEC_ID_H264;
} else if (stream_type.encoding() == StreamType::kVideoEncodingMpeg4) {
codec_id = AV_CODEC_ID_MPEG4;
} else if (stream_type.encoding() == StreamType::kVideoEncodingTheora) {
codec_id = AV_CODEC_ID_THEORA;
} else if (stream_type.encoding() == StreamType::kVideoEncodingVp3) {
codec_id = AV_CODEC_ID_VP3;
} else if (stream_type.encoding() == StreamType::kVideoEncodingVp8) {
codec_id = AV_CODEC_ID_VP8;
} else if (stream_type.encoding() == StreamType::kVideoEncodingVp9) {
codec_id = AV_CODEC_ID_VP9;
} else if (stream_type.encoding() == StreamType::kMediaEncodingUnsupported) {
codec_id = AV_CODEC_ID_NONE;
} else {
FXL_LOG(WARNING) << "unsupported encoding " << stream_type.encoding();
codec_id = AV_CODEC_ID_NONE;
}
if (codec_id == AV_CODEC_ID_NONE) {
return nullptr;
}
AvCodecContextPtr context(avcodec_alloc_context3(nullptr));
context->codec_type = AVMEDIA_TYPE_VIDEO;
context->codec_id = codec_id;
context->pix_fmt = AVPixelFormatFromPixelFormat(stream_type.pixel_format());
if (stream_type.color_space() == VideoStreamType::ColorSpace::kJpeg) {
context->color_range = AVCOL_RANGE_JPEG;
}
context->coded_width = stream_type.coded_width();
context->coded_height = stream_type.coded_height();
context->sample_aspect_ratio.num = stream_type.pixel_aspect_ratio_width();
context->sample_aspect_ratio.den = stream_type.pixel_aspect_ratio_height();
if (stream_type.encoding_parameters()) {
ExtraDataFromBytes(*stream_type.encoding_parameters(), context);
}
return context;
}
// Creats an AVCodecContext from a TextStreamType.
AvCodecContextPtr AVCodecContextFromTextStreamType(
const TextStreamType& stream_type) {
// TODO(dalesat): Implement.
FXL_LOG(ERROR) << "AVCodecContextFromTextStreamType not implemented";
abort();
}
// Creats an AVCodecContext from a SubpictureStreamType.
AvCodecContextPtr AVCodecContextFromSubpictureStreamType(
const SubpictureStreamType& stream_type) {
// TODO(dalesat): Implement.
FXL_LOG(ERROR) << "AVCodecContextFromSupictureStreamType not implemented";
abort();
}
} // namespace
VideoStreamType::PixelFormat PixelFormatFromAVPixelFormat(
AVPixelFormat av_pixel_format) {
switch (av_pixel_format) {
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P:
return VideoStreamType::PixelFormat::kYv12;
default:
return VideoStreamType::PixelFormat::kUnknown;
}
}
AVPixelFormat AVPixelFormatFromPixelFormat(
VideoStreamType::PixelFormat pixel_format) {
switch (pixel_format) {
case VideoStreamType::PixelFormat::kYv12:
return AV_PIX_FMT_YUV420P;
case VideoStreamType::PixelFormat::kUnknown:
case VideoStreamType::PixelFormat::kArgb:
case VideoStreamType::PixelFormat::kYuy2:
case VideoStreamType::PixelFormat::kNv12:
default:
return AV_PIX_FMT_NONE;
}
}
// static
std::unique_ptr<StreamType> AvCodecContext::GetStreamType(
const AVCodecContext& from) {
switch (from.codec_type) {
case AVMEDIA_TYPE_AUDIO:
return StreamTypeFromAudioCodecContext(from);
case AVMEDIA_TYPE_VIDEO:
return StreamTypeFromVideoCodecContext(from);
case AVMEDIA_TYPE_UNKNOWN:
// Treated as AVMEDIA_TYPE_DATA.
case AVMEDIA_TYPE_DATA:
return StreamTypeFromDataCodecContext(from);
case AVMEDIA_TYPE_SUBTITLE:
return StreamTypeFromSubtitleCodecContext(from);
case AVMEDIA_TYPE_ATTACHMENT:
case AVMEDIA_TYPE_NB:
default:
FXL_LOG(ERROR) << "unsupported code type " << from.codec_type;
abort();
}
}
// static
std::unique_ptr<StreamType> AvCodecContext::GetStreamType(
const AVStream& from) {
switch (from.codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
return StreamTypeFromAudioCodecParameters(*from.codecpar);
case AVMEDIA_TYPE_VIDEO:
return StreamTypeFromVideoStream(from);
case AVMEDIA_TYPE_UNKNOWN:
// Treated as AVMEDIA_TYPE_DATA.
case AVMEDIA_TYPE_DATA:
return StreamTypeFromDataCodecParameters(*from.codecpar);
case AVMEDIA_TYPE_SUBTITLE:
return StreamTypeFromSubtitleCodecParameters(*from.codecpar);
case AVMEDIA_TYPE_ATTACHMENT:
case AVMEDIA_TYPE_NB:
default:
FXL_LOG(ERROR) << "unsupported code type " << from.codecpar->codec_type;
abort();
}
}
// static
AvCodecContextPtr AvCodecContext::Create(const StreamType& stream_type) {
InitFfmpeg();
switch (stream_type.medium()) {
case StreamType::Medium::kAudio:
return AVCodecContextFromAudioStreamType(*stream_type.audio());
case StreamType::Medium::kVideo:
return AVCodecContextFromVideoStreamType(*stream_type.video());
case StreamType::Medium::kText:
return AVCodecContextFromTextStreamType(*stream_type.text());
case StreamType::Medium::kSubpicture:
return AVCodecContextFromSubpictureStreamType(*stream_type.subpicture());
default:
return nullptr;
}
}
} // namespace media_player