diff options
Diffstat (limited to 'ffmpeg_backend.cpp')
-rw-r--r-- | ffmpeg_backend.cpp | 386 |
1 files changed, 0 insertions, 386 deletions
diff --git a/ffmpeg_backend.cpp b/ffmpeg_backend.cpp deleted file mode 100644 index a54fb51..0000000 --- a/ffmpeg_backend.cpp +++ /dev/null @@ -1,386 +0,0 @@ -extern "C" { -#include <libavcodec/avcodec.h> -#include <libavformat/avformat.h> -#include <libavformat/avio.h> -#include <libavutil/avutil.h> -#include <libswscale/swscale.h> -} - -// workaround to make libav error printing work -#ifdef av_err2str -#undef av_err2str -#include <string> -av_always_inline std::string av_err2string(int errnum) { - char str[AV_ERROR_MAX_STRING_SIZE]; - return av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, errnum); -} -#define av_err2str(err) av_err2string(err).c_str() -#endif // av_err2str - -#include "ffmpeg_backend.h" - -bool32 AV_TryFrame(av_info *AV, int32 *err) -{ - *err = av_read_frame(AV->FileFormatContext, AV->VideoPacket); - if (*err >= 0 && AV->VideoPacket->stream_index != AV->VideoStream->index) { - av_packet_unref(AV->VideoPacket); - return 0; - } - - if (*err < 0) - *err = avcodec_send_packet(AV->VideoCodecContext, AV->VideoPacket); - else { - *err = avcodec_send_packet(AV->VideoCodecContext, AV->VideoPacket); - } - av_packet_unref(AV->VideoPacket); - - if (*err < 0) - { - fprintf(stderr, "Libav *error: (%s)\n", av_err2str(*err)); - Assert(0); - } - - while (*err >= 0) { - *err = avcodec_receive_frame(AV->VideoCodecContext, AV->VideoFrame); - if (*err == AVERROR_EOF) { - av_seek_frame(AV->FileFormatContext, -1, 0, AVSEEK_FLAG_BACKWARD); - *err = 0; - break; - } else if (*err == AVERROR(EAGAIN)) { - *err = 0; - break; - } else if (*err < 0) { - Assert(0); - } - return 1; - } - return 0; -} - -bool32 AV_IsFileSupported(char *filename, bool32 *IsVideo) -{ - int32 err = 0; - - // enum AVHWDeviceType type; - // while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) - // printf("%s\n", av_hwdevice_get_type_name(type)); - - AVFormatContext *temp = avformat_alloc_context(); - err = avformat_open_input(&temp, filename, NULL, NULL);; - - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - avformat_free_context(temp); - return 0; - } - - err = avformat_find_stream_info(temp, NULL); - - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - avformat_free_context(temp); - return 0; - } - - for (uint32 i = 0; i < temp->nb_streams; i++) - { - AVCodecParameters *LocalCodecParameters = NULL; - LocalCodecParameters = temp->streams[i]->codecpar; - if (LocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) { - if (temp->streams[i]->duration > 1) { - *IsVideo = true; - } - avformat_free_context(temp); - return 1; - } - } - - printf("Libav error: No video track found."); - - return 0; -} - -// Note that we can't get away with not having to keep track of the AV pointer -// for undos by clearing/reallocing these contexts every frame since the state -// of the PTS value is stored inside FileFormatContext. -void AV_Dealloc(av_info *AV) -{ - Assert(AV); - avformat_free_context(AV->FileFormatContext); - avcodec_free_context(&AV->VideoCodecContext); - av_packet_free(&AV->VideoPacket); - av_frame_free(&AV->VideoFrame); -}; - -void AV_Init(block_source *Source, av_info *AV, memory *Memory) -{ - *AV = {}; - - int32 err = 0; - - // enum AVHWDeviceType type; - // while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) - // printf("%s\n", av_hwdevice_get_type_name(type)); - - // The two calls below theoretically shouldn't fail since we already tested them in IsFileSupported. - - AV->FileFormatContext = avformat_alloc_context(); - char *Path = (char *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index); - err = avformat_open_input(&AV->FileFormatContext, Path, NULL, NULL);; - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - Assert(0); - } - - err = avformat_find_stream_info(AV->FileFormatContext, NULL); - - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - Assert(0); - } - - for (uint32 i = 0; i < AV->FileFormatContext->nb_streams; i++) - { - AVCodecParameters *LocalCodecParameters = NULL; - LocalCodecParameters = AV->FileFormatContext->streams[i]->codecpar; - if (LocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) { - AV->VideoCodecParameters = LocalCodecParameters; - AV->VideoStream = AV->FileFormatContext->streams[i]; - break; - } - } - - if (!AV->VideoCodecParameters) { - printf("Libav error: No video track found."); - } - - AV->VideoCodec = avcodec_find_decoder(AV->VideoCodecParameters->codec_id); - - if (!AV->VideoCodec) { - printf("Libav error: Suitable decoder could not be identified for codec:\n"); - } -/* - int16 codecs = 0; - for (;;) { - AV->VideoHWConfig = avcodec_get_hw_config(AV->VideoCodec, codecs); - if (!AV->VideoHWConfig) { - printf("Libav error: Hardware acceleration not found for decoder %s.", - AV->VideoCodec->name); - break; - } - AV->HWPixFormat = AV->VideoHWConfig->pix_fmt; - break; - // if (AV->VideoHWConfig->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && - // AV->VideoHWConfig->device_type == type) { - // } - codecs++; - } -*/ - AV->VideoCodecContext = avcodec_alloc_context3(AV->VideoCodec); - if (!AV->VideoCodecContext) { - printf("Libav error: Decoder context allocation failed."); - } - err = avcodec_parameters_to_context(AV->VideoCodecContext, AV->VideoCodecParameters); - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - } - avcodec_open2(AV->VideoCodecContext, AV->VideoCodec, NULL); - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - } - - AV->VideoPacket = av_packet_alloc(); - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - } - AV->VideoFrame = av_frame_alloc(); - if (err < 0) { - fprintf(stderr, "Libav error: (%s)\n", av_err2str(err)); - } - - Source->BytesPerPixel = 4; - Source->FPS = (real32)AV->VideoStream->r_frame_rate.num / AV->VideoStream->r_frame_rate.den; - Source->Width = AV->VideoCodecContext->width; - Source->Height = AV->VideoCodecContext->height; -}; - - - -void AV_GetPTSAverage(block_source *Source, av_info *AV, int32 *err) -{ - // TODO(fox): This PTS average isn't exact and causes an occasional - // frame skip. See libav remarks in forum for more details. - - if (AV->VideoStream->duration == 1) { - Source->AvgPTSPerFrame = 1; - return; - } - - // TODO(fox): Handle footage under five seconds. - int16 TestAmount = 5; - - real32 FPS = (real32)AV->VideoStream->r_frame_rate.num / AV->VideoStream->r_frame_rate.den; - - int16 i = 0; - real32 AvgPTSPerSecond = 0; - for (;;) { - if (AV_TryFrame(AV, err)) { - if (i >= FPS * TestAmount) { - AvgPTSPerSecond = (real32)AV->VideoFrame->pts / TestAmount; - printf("frame: %i, pts: %li\n", i, AV->VideoFrame->pts); - break; - } - i++; - av_frame_unref(AV->VideoFrame); - } - } - - Source->AvgPTSPerFrame = (real32)AvgPTSPerSecond / (int32)(FPS + 0.5f); - printf("Avg PTS per sec: %.06f, Avg PTS per frame: %.06f\n", AvgPTSPerSecond, Source->AvgPTSPerFrame); - - av_seek_frame(AV->FileFormatContext, -1, 0, AVSEEK_FLAG_BACKWARD); -} - -#if 0 -cached_bitmap * AV_LoadStill(block_source *Source, layer_bitmap_info *BitmapInfo, memory *Memory) -{ - av_info *AV = (av_info *)BitmapInfo->AVInfo; - int32 *CurrentlyRenderedFrame = &BitmapInfo->CurrentFrame; - - int32 err = 0; - - *CurrentlyRenderedFrame = 0; - - av_seek_frame(AV->FileFormatContext, -1, 0, AVSEEK_FLAG_FRAME); - - while (err >= 0) { - if (AV_TryFrame(AV, &err)) - { - uint16 Width = Source->Width; - uint16 Height = Source->Height; - uint16 BytesPerPixel = Source->BytesPerPixel; - int32 Pitch = Width*BytesPerPixel; - - cached_bitmap *Bitmap = Memory_RollingBitmap(Memory, Source, 0); - void *Buffer = Bitmap->Data; - - int out_linesize[4] = { Pitch, Pitch, Pitch, Pitch }; - uint8 *dst_data[4] = { (uint8 *)Buffer, (uint8 *)Buffer + Width*Height*BytesPerPixel, - (uint8 *)Buffer + Width*Height*BytesPerPixel*2, (uint8 *)Buffer + Width*Height*BytesPerPixel*3 }; - - // NOTE(fox): This function will be replaced in the future. - AV->RGBContext = sws_getContext(AV->VideoFrame->width, AV->VideoFrame->height, (AVPixelFormat)AV->VideoFrame->format, - AV->VideoFrame->width, AV->VideoFrame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, - NULL, NULL, NULL); - - if(!AV->RGBContext) { - printf("Libav error: SwsContext creation failed."); - } - - sws_scale(AV->RGBContext, AV->VideoFrame->data, AV->VideoFrame->linesize, 0, AV->VideoFrame->height, - dst_data, out_linesize); - - av_frame_unref(AV->VideoFrame); - - Assert(BitmapInfo->BitmapBuffer); - return Bitmap; - } - av_frame_unref(AV->VideoFrame); - } - return 0; -} - -cached_bitmap * AV_LoadVideoFrame(source *Source, layer_bitmap_info *BitmapInfo, memory *Memory, int32 TimelineFrame) -{ - av_info *AV = (av_info *)BitmapInfo->AVInfo; - int32 *CurrentlyRenderedFrame = &BitmapInfo->CurrentFrame; - - int32 err = 0; - - if (!Source->AvgPTSPerFrame) { - AV_GetPTSAverage(Source, AV, &err); - } - Assert(Source->AvgPTSPerFrame); - - int32 FrameToSeek = TimelineFrame - BitmapInfo->FrameOffset; - if (*CurrentlyRenderedFrame == FrameToSeek || FrameToSeek < 0) - return 0; - - // NOTE(fox): The decoder automatically advances to the next frame, so we - // don't need to call av_seek_frame under normal playback. - // This function only seeks to the nearest "keyframe." - - if (*CurrentlyRenderedFrame != FrameToSeek - 1) { - int64 SeekSeconds = (int64)(FrameToSeek / (int32)(Source->FPS + 0.5f) * AV_TIME_BASE); - av_seek_frame(AV->FileFormatContext, -1, SeekSeconds, AVSEEK_FLAG_BACKWARD); - printf("Seek activated\n"); - } else if (*CurrentlyRenderedFrame < 0) { - av_seek_frame(AV->FileFormatContext, -1, 0, AVSEEK_FLAG_BACKWARD); - } - - *CurrentlyRenderedFrame = FrameToSeek; - - int64 SeekPTS = (int64)(Source->AvgPTSPerFrame*FrameToSeek + 0.5f); - - while (err >= 0) { - if (AV_TryFrame(AV, &err)) { - - // The first frame that gets loaded isn't always the actual - // first frame, so we need to check until it's correct. - if (FrameToSeek == 0 && AV->VideoFrame->pts != AV->VideoStream->start_time) { - av_frame_unref(AV->VideoFrame); - // printf("NON-START: avg: %li, real pts: %li", SeekPTS, AV->VideoFrame->pts); - continue; - } - - int64 Difference = AV->VideoFrame->pts - SeekPTS; - if (abs(Difference) < Source->AvgPTSPerFrame) - { - if (AV->PreviousPTS == -1) { - AV->PreviousPTS = AV->VideoFrame->pts; - // printf("avg: %li, real pts: %li, difference: %li\n", SeekPTS, AV->VideoFrame->pts, Difference); - } else { - // printf("avg: %li, real pts: %li, difference: %li difference from last pts: %li\n", SeekPTS, AV->VideoFrame->pts, AV->VideoFrame->pts - SeekPTS, AV->VideoFrame->pts - AV->PreviousPTS); - AV->PreviousPTS = AV->VideoFrame->pts; - } - - uint16 Width = Source->Width; - uint16 Height = Source->Height; - uint16 BytesPerPixel = Source->BytesPerPixel; - int32 Pitch = Width*BytesPerPixel; - - cached_bitmap *Bitmap = Memory_RollingBitmap(Memory, Source, FrameToSeek); - void *Buffer = Bitmap->Data; - - int out_linesize[4] = { Pitch, Pitch, Pitch, Pitch }; - uint8 *dst_data[4] = { (uint8 *)Buffer, (uint8 *)Buffer + Width*Height*BytesPerPixel, - (uint8 *)Buffer + Width*Height*BytesPerPixel*2, (uint8 *)Buffer + Width*Height*BytesPerPixel*3 }; - - // NOTE(fox): This function will be replaced in the future. - AV->RGBContext = sws_getContext(AV->VideoFrame->width, AV->VideoFrame->height, (AVPixelFormat)AV->VideoFrame->format, - AV->VideoFrame->width, AV->VideoFrame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, - NULL, NULL, NULL); - - if(!AV->RGBContext) { - printf("Libav error: SwsContext creation failed."); - } - - sws_scale(AV->RGBContext, AV->VideoFrame->data, AV->VideoFrame->linesize, 0, AV->VideoFrame->height, - dst_data, out_linesize); - - av_frame_unref(AV->VideoFrame); - - Assert(BitmapInfo->BitmapBuffer); - return Bitmap; - } - else - { - // If this gets printed when not seeking, a frame has been skipped! - // printf("FRAME SKIP: avg: %li, real pts: %li, difference: %li\n", SeekPTS, AV->VideoFrame->pts, Difference); - } - av_frame_unref(AV->VideoFrame); - } - } - return 0; -} -#endif |