blob: cd8a3a019366ad36362485c0ae8c915941a5ae59 [file] [log] [blame]
// Copyright 2016 The Chromium 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 "base/logging.h"
#include "services/media/framework_ffmpeg/av_codec_context.h"
#include "services/media/framework_ffmpeg/ffmpeg_init.h"
extern "C" {
#include "third_party/ffmpeg/libavformat/avformat.h"
}
// Ffmeg defines this...undefine.
#undef PixelFormat
namespace mojo {
namespace media {
namespace {
// Converts an AVSampleFormat into an LpcmStreamType::SampleFormat.
LpcmStreamType::SampleFormat Convert(AVSampleFormat av_sample_format) {
switch (av_sample_format) {
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
return LpcmStreamType::SampleFormat::kUnsigned8;
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
return LpcmStreamType::SampleFormat::kSigned16;
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
return LpcmStreamType::SampleFormat::kSigned24In32;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
return LpcmStreamType::SampleFormat::kFloat;
case AV_SAMPLE_FMT_NONE:
case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_DBLP:
case AV_SAMPLE_FMT_NB:
default:
NOTREACHED() << "unsupported av_sample_format " << av_sample_format;
return LpcmStreamType::SampleFormat::kUnknown;
}
}
// 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;
}
// Creates a StreamType from an AVCodecContext describing an LPCM type.
std::unique_ptr<StreamType> StreamTypeFromLpcmCodecContext(
const AVCodecContext& from) {
return LpcmStreamType::Create(
Convert(from.sample_fmt),
from.channels,
from.sample_rate);
}
// Creates a StreamType from an AVCodecContext describing a compressed audio
// type.
std::unique_ptr<StreamType>
StreamTypeFromCompressedAudioCodecContext(const AVCodecContext& from) {
CompressedAudioStreamType::AudioEncoding encoding;
switch (from.codec_id) {
case CODEC_ID_VORBIS:
encoding = CompressedAudioStreamType::AudioEncoding::kVorbis;
break;
default:
encoding = CompressedAudioStreamType::AudioEncoding::kUnknown;
break;
}
return CompressedAudioStreamType::Create(
encoding,
Convert(from.sample_fmt),
from.channels,
from.sample_rate,
from.extradata_size == 0 ?
nullptr :
Bytes::Create(from.extradata, from.extradata_size));
}
// 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;
}
}
// Converts VideoProfile to an ffmpeg profile.
int FfmpegProfileFromVideoProfile(VideoStreamType::VideoProfile video_profile) {
// TODO(dalesat): Blindly copied from Chromium.
switch (video_profile) {
case VideoStreamType::VideoProfile::kH264Baseline:
return FF_PROFILE_H264_BASELINE;
case VideoStreamType::VideoProfile::kH264Main:
return FF_PROFILE_H264_MAIN;
case VideoStreamType::VideoProfile::kH264Extended:
return FF_PROFILE_H264_EXTENDED;
case VideoStreamType::VideoProfile::kH264High:
return FF_PROFILE_H264_HIGH;
case VideoStreamType::VideoProfile::kH264High10:
return FF_PROFILE_H264_HIGH_10;
case VideoStreamType::VideoProfile::kH264High422:
return FF_PROFILE_H264_HIGH_422;
case VideoStreamType::VideoProfile::kH264High444Predictive:
return FF_PROFILE_H264_HIGH_444_PREDICTIVE;
case VideoStreamType::VideoProfile::kUnknown:
case VideoStreamType::VideoProfile::kNotApplicable:
case VideoStreamType::VideoProfile::kH264ScalableBaseline:
case VideoStreamType::VideoProfile::kH264ScalableHigh:
case VideoStreamType::VideoProfile::kH264StereoHigh:
case VideoStreamType::VideoProfile::kH264MultiviewHigh:
default:
return FF_PROFILE_UNKNOWN;
}
}
// Converts an AVPixelFormat to a PixelFormat.
VideoStreamType::PixelFormat PixelFormatFromAVPixelFormat(
AVPixelFormat av_pixel_format) {
// TODO(dalesat): Blindly copied from Chromium.
switch (av_pixel_format) {
case AV_PIX_FMT_YUV422P:
case AV_PIX_FMT_YUVJ422P:
return VideoStreamType::PixelFormat::kYv16;
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUVJ444P:
return VideoStreamType::PixelFormat::kYv24;
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVJ420P:
return VideoStreamType::PixelFormat::kYv12;
case AV_PIX_FMT_YUVA420P:
return VideoStreamType::PixelFormat::kYv12A;
default:
return VideoStreamType::PixelFormat::kUnknown;
}
}
// Converts a PixelFormat to an AVPixelFormat.
AVPixelFormat AVPixelFormatFromPixelFormat(
VideoStreamType::PixelFormat pixel_format) {
// TODO(dalesat): Blindly copied from Chromium.
switch (pixel_format) {
case VideoStreamType::PixelFormat::kYv12:
return AV_PIX_FMT_YUV420P;
case VideoStreamType::PixelFormat::kYv16:
return AV_PIX_FMT_YUV422P;
case VideoStreamType::PixelFormat::kYv12A:
return AV_PIX_FMT_YUVA420P;
case VideoStreamType::PixelFormat::kYv24:
return AV_PIX_FMT_YUV444P;
case VideoStreamType::PixelFormat::kUnknown:
case VideoStreamType::PixelFormat::kI420:
case VideoStreamType::PixelFormat::kNv12:
case VideoStreamType::PixelFormat::kNv21:
case VideoStreamType::PixelFormat::kUyvy:
case VideoStreamType::PixelFormat::kYuy2:
case VideoStreamType::PixelFormat::kArgb:
case VideoStreamType::PixelFormat::kXrgb:
case VideoStreamType::PixelFormat::kRgb24:
case VideoStreamType::PixelFormat::kRgb32:
case VideoStreamType::PixelFormat::kMjpeg:
case VideoStreamType::PixelFormat::kMt21:
default:
return AV_PIX_FMT_NONE;
}
}
// Creates a StreamType from an AVCodecContext describing a video type.
std::unique_ptr<StreamType> StreamTypeFromVideoCodecContext(
const AVCodecContext& from) {
VideoStreamType::VideoEncoding encoding;
switch (from.codec_id) {
case AV_CODEC_ID_THEORA :
encoding = VideoStreamType::VideoEncoding::kTheora;
break;
case CODEC_ID_VP8:
encoding = VideoStreamType::VideoEncoding::kVp8;
break;
default:
encoding = VideoStreamType::VideoEncoding::kUnknown;
break;
}
return VideoStreamType::Create(
encoding,
VideoStreamType::VideoProfile::kNotApplicable,
PixelFormatFromAVPixelFormat(from.pix_fmt),
ColorSpaceFromAVColorSpaceAndRange(from.colorspace, from.color_range),
from.width,
from.height,
from.coded_width,
from.coded_height,
from.extradata_size == 0 ?
nullptr :
Bytes::Create(from.extradata, from.extradata_size));
}
// Creates a StreamType from an AVCodecContext describing a data type.
std::unique_ptr<StreamType> StreamTypeFromDataCodecContext(
const AVCodecContext& from) {
// TODO(dalesat): Implement.
return StreamType::Create(StreamType::Scheme::kUnknown);
}
// Creates a StreamType from an AVCodecContext describing a subtitle type.
std::unique_ptr<StreamType> StreamTypeFromSubtitleCodecContext(
const AVCodecContext& from) {
// TODO(dalesat): Implement.
return StreamType::Create(StreamType::Scheme::kUnknown);
}
// Creates an AVCodecContext from LpcmStreamType.
AvCodecContextPtr CodecContextFromLpcmDetails(
const LpcmStreamType& stream_type) {
AVCodecID codec_id;
AVSampleFormat sample_format;
switch (stream_type.sample_format()) {
case LpcmStreamType::SampleFormat::kUnsigned8:
codec_id = AV_CODEC_ID_PCM_U8;
sample_format = AV_SAMPLE_FMT_U8;
break;
case LpcmStreamType::SampleFormat::kSigned16:
codec_id = AV_CODEC_ID_PCM_S16LE;
sample_format = AV_SAMPLE_FMT_S16;
break;
case LpcmStreamType::SampleFormat::kSigned24In32:
codec_id = AV_CODEC_ID_PCM_S24LE;
sample_format = AV_SAMPLE_FMT_S32;
break;
case LpcmStreamType::SampleFormat::kFloat:
codec_id = AV_CODEC_ID_PCM_F32LE;
sample_format = AV_SAMPLE_FMT_FLT;
break;
default:
return nullptr;
}
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();
return context;
}
// Creates an AVCodecContext from CompressedAudioStreamType.
AvCodecContextPtr AVCodecContextFromCompressedAudioStreamType(
const CompressedAudioStreamType& stream_type) {
AVCodecID codec_id = AV_CODEC_ID_NONE;
AVSampleFormat sample_format;
switch (stream_type.encoding()) {
case CompressedAudioStreamType::AudioEncoding::kVorbis:
codec_id = AV_CODEC_ID_VORBIS;
sample_format = AV_SAMPLE_FMT_S16;
break;
default:
return nullptr;
}
if (codec_id == AV_CODEC_ID_NONE) {
return nullptr;
}
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_details()) {
ExtraDataFromBytes(*stream_type.encoding_details(), context);
}
return context;
}
// Creats an AVCodecContext from VideoStreamTypeDetails.
AvCodecContextPtr AVCodecContextFromVideoStreamType(
const VideoStreamType& stream_type) {
AVCodecID codec_id = AV_CODEC_ID_NONE;
// TODO(dalesat): codec_id
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->profile = FfmpegProfileFromVideoProfile(stream_type.profile());
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();
if (stream_type.encoding_details()) {
ExtraDataFromBytes(*stream_type.encoding_details(), context);
}
return context;
}
} // namespace
// static
std::unique_ptr<StreamType> AvCodecContext::GetStreamType(
const AVCodecContext& from) {
switch (from.codec_type) {
case AVMEDIA_TYPE_AUDIO:
switch (from.codec_id) {
case CODEC_ID_PCM_S16BE:
case CODEC_ID_PCM_S16LE:
case CODEC_ID_PCM_S24BE:
case CODEC_ID_PCM_S24LE:
case CODEC_ID_PCM_U8:
return StreamTypeFromLpcmCodecContext(from);
default:
if (from.codec == nullptr) {
return StreamTypeFromCompressedAudioCodecContext(from);
} else {
return StreamTypeFromLpcmCodecContext(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:
return StreamType::Create(StreamType::Scheme::kUnknown);
}
}
// static
AvCodecContextPtr AvCodecContext::Create(const StreamType& stream_type) {
InitFfmpeg();
switch (stream_type.scheme()) {
case StreamType::Scheme::kLpcm:
return CodecContextFromLpcmDetails(*stream_type.lpcm());
case StreamType::Scheme::kCompressedAudio:
return AVCodecContextFromCompressedAudioStreamType(
*stream_type.compressed_audio());
case StreamType::Scheme::kVideo:
return AVCodecContextFromVideoStreamType(*stream_type.video());
default:
return nullptr;
}
}
} // namespace media
} // namespace mojo