b8561bf01d
Originally committed to SVN as r3820.
252 lines
8.1 KiB
C++
252 lines
8.1 KiB
C++
// Copyright (c) 2007-2009 Fredrik Mellbin
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#include "videosource.h"
|
|
|
|
void FFMS_VideoSource::GetFrameCheck(int n) {
|
|
if (n < 0 || n >= VP.NumFrames)
|
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
|
|
"Out of bounds frame requested");
|
|
}
|
|
|
|
void FFMS_VideoSource::SetPP(const char *PP) {
|
|
if (PPMode)
|
|
pp_free_mode(PPMode);
|
|
PPMode = NULL;
|
|
|
|
if (PP != NULL && strcmp(PP, "")) {
|
|
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);
|
|
if (!PPMode) {
|
|
ResetPP();
|
|
throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_INVALID_ARGUMENT,
|
|
"Invalid postprocesing settings");
|
|
}
|
|
|
|
}
|
|
|
|
ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height);
|
|
OutputFrame(DecodeFrame);
|
|
}
|
|
|
|
void FFMS_VideoSource::ResetPP() {
|
|
if (PPContext)
|
|
pp_free_context(PPContext);
|
|
PPContext = NULL;
|
|
|
|
if (PPMode)
|
|
pp_free_mode(PPMode);
|
|
PPMode = NULL;
|
|
|
|
OutputFrame(DecodeFrame);
|
|
}
|
|
|
|
void FFMS_VideoSource::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height) {
|
|
if (PPContext)
|
|
pp_free_context(PPContext);
|
|
PPContext = NULL;
|
|
|
|
if (!PPMode)
|
|
return;
|
|
|
|
int Flags = GetPPCPUFlags();
|
|
|
|
switch (VPixelFormat) {
|
|
case PIX_FMT_YUV420P: Flags |= PP_FORMAT_420; break;
|
|
case PIX_FMT_YUV422P: Flags |= PP_FORMAT_422; break;
|
|
case PIX_FMT_YUV411P: Flags |= PP_FORMAT_411; break;
|
|
case PIX_FMT_YUV444P: Flags |= PP_FORMAT_444; break;
|
|
default:
|
|
ResetPP();
|
|
throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED,
|
|
"The video does not have a colorspace suitable for postprocessing");
|
|
}
|
|
|
|
PPContext = pp_get_context(Width, Height, Flags);
|
|
|
|
avpicture_free(&PPFrame);
|
|
avpicture_alloc(&PPFrame, VPixelFormat, Width, Height);
|
|
}
|
|
|
|
static void CopyAVPictureFields(AVPicture &Picture, FFMS_Frame &Dst) {
|
|
for (int i = 0; i < 4; i++) {
|
|
Dst.Data[i] = Picture.data[i];
|
|
Dst.Linesize[i] = Picture.linesize[i];
|
|
}
|
|
}
|
|
|
|
FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) {
|
|
if (LastFrameWidth != CodecContext->width || LastFrameHeight != CodecContext->height || LastFramePixelFormat != CodecContext->pix_fmt) {
|
|
ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height);
|
|
|
|
if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0)
|
|
ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer);
|
|
}
|
|
|
|
if (PPMode) {
|
|
pp_postprocess(const_cast<const uint8_t **>(Frame->data), Frame->linesize, PPFrame.data, PPFrame.linesize, CodecContext->width, CodecContext->height, Frame->qscale_table, Frame->qstride, PPMode, PPContext, Frame->pict_type | (Frame->qscale_type ? PP_PICT_TYPE_QP2 : 0));
|
|
if (SWS) {
|
|
sws_scale(SWS, PPFrame.data, PPFrame.linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize);
|
|
CopyAVPictureFields(SWSFrame, LocalFrame);
|
|
} else {
|
|
CopyAVPictureFields(PPFrame, LocalFrame);
|
|
}
|
|
} else {
|
|
if (SWS) {
|
|
sws_scale(SWS, Frame->data, Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize);
|
|
CopyAVPictureFields(SWSFrame, LocalFrame);
|
|
} else {
|
|
// Special case to avoid ugly casts
|
|
for (int i = 0; i < 4; i++) {
|
|
LocalFrame.Data[i] = Frame->data[i];
|
|
LocalFrame.Linesize[i] = Frame->linesize[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFrame.EncodedWidth = CodecContext->width;
|
|
LocalFrame.EncodedHeight = CodecContext->height;
|
|
LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt;
|
|
LocalFrame.ScaledWidth = TargetWidth;
|
|
LocalFrame.ScaledHeight = TargetHeight;
|
|
LocalFrame.ConvertedPixelFormat = OutputFormat;
|
|
LocalFrame.KeyFrame = Frame->key_frame;
|
|
LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type);
|
|
LocalFrame.RepeatPict = Frame->repeat_pict;
|
|
LocalFrame.InterlacedFrame = Frame->interlaced_frame;
|
|
LocalFrame.TopFieldFirst = Frame->top_field_first;
|
|
|
|
LastFrameHeight = CodecContext->height;
|
|
LastFrameWidth = CodecContext->width;
|
|
LastFramePixelFormat = CodecContext->pix_fmt;
|
|
|
|
return &LocalFrame;
|
|
}
|
|
|
|
FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track) {
|
|
if (Track < 0 || Track >= static_cast<int>(Index->size()))
|
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
|
"Out of bounds track index selected");
|
|
|
|
if (Index->at(Track).TT != FFMS_TYPE_VIDEO)
|
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
|
"Not a video track");
|
|
|
|
if (Index[Track].size() == 0)
|
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
|
"Video track contains no frames");
|
|
|
|
if (!Index->CompareFileSignature(SourceFile))
|
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
|
|
"The index does not match the source file");
|
|
|
|
memset(&VP, 0, sizeof(VP));
|
|
PPContext = NULL;
|
|
PPMode = NULL;
|
|
SWS = NULL;
|
|
LastFrameNum = 0;
|
|
CurrentFrame = 1;
|
|
MPEG4Counter = 0;
|
|
CodecContext = NULL;
|
|
LastFrameHeight = -1;
|
|
LastFrameWidth = -1;
|
|
LastFramePixelFormat = PIX_FMT_NONE;
|
|
TargetHeight = -1;
|
|
TargetWidth = -1;
|
|
TargetPixelFormats = 0;
|
|
TargetResizer = 0;
|
|
OutputFormat = PIX_FMT_NONE;
|
|
DecodeFrame = avcodec_alloc_frame();
|
|
|
|
// Dummy allocations so the unallocated case doesn't have to be handled later
|
|
avpicture_alloc(&PPFrame, PIX_FMT_GRAY8, 16, 16);
|
|
avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16);
|
|
}
|
|
|
|
FFMS_VideoSource::~FFMS_VideoSource() {
|
|
if (PPMode)
|
|
pp_free_mode(PPMode);
|
|
|
|
if (PPContext)
|
|
pp_free_context(PPContext);
|
|
|
|
if (SWS)
|
|
sws_freeContext(SWS);
|
|
|
|
avpicture_free(&PPFrame);
|
|
avpicture_free(&SWSFrame);
|
|
av_freep(&DecodeFrame);
|
|
}
|
|
|
|
FFMS_Frame *FFMS_VideoSource::GetFrameByTime(double Time) {
|
|
int Frame = Frames.ClosestFrameFromPTS(static_cast<int64_t>((Time * 1000 * Frames.TB.Den) / Frames.TB.Num));
|
|
return GetFrame(Frame);
|
|
}
|
|
|
|
void FFMS_VideoSource::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) {
|
|
this->TargetWidth = Width;
|
|
this->TargetHeight = Height;
|
|
this->TargetPixelFormats = TargetFormats;
|
|
this->TargetResizer = Resizer;
|
|
ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer);
|
|
OutputFrame(DecodeFrame);
|
|
}
|
|
|
|
void FFMS_VideoSource::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) {
|
|
if (SWS) {
|
|
sws_freeContext(SWS);
|
|
SWS = NULL;
|
|
}
|
|
|
|
int Loss;
|
|
OutputFormat = avcodec_find_best_pix_fmt(TargetFormats,
|
|
CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
|
|
if (OutputFormat == PIX_FMT_NONE) {
|
|
ResetOutputFormat();
|
|
throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
|
|
"No suitable output format found");
|
|
}
|
|
|
|
if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) {
|
|
SWS = sws_getContext(CodecContext->width, CodecContext->height, CodecContext->pix_fmt, Width, Height,
|
|
OutputFormat, GetSWSCPUFlags() | Resizer, NULL, NULL, NULL);
|
|
if (SWS == NULL) {
|
|
ResetOutputFormat();
|
|
throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
|
|
"Failed to allocate SWScale context");
|
|
}
|
|
}
|
|
|
|
avpicture_free(&SWSFrame);
|
|
avpicture_alloc(&SWSFrame, OutputFormat, Width, Height);
|
|
}
|
|
|
|
void FFMS_VideoSource::ResetOutputFormat() {
|
|
if (SWS) {
|
|
sws_freeContext(SWS);
|
|
SWS = NULL;
|
|
}
|
|
|
|
TargetWidth = -1;
|
|
TargetHeight = -1;
|
|
TargetPixelFormats = 0;
|
|
OutputFormat = PIX_FMT_NONE;
|
|
|
|
OutputFrame(DecodeFrame);
|
|
}
|