You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
9.2 KiB
395 lines
9.2 KiB
/**
|
|
* @file
|
|
* video decoding with libavcodec API example
|
|
*
|
|
* decode_video.c
|
|
*/
|
|
//error: call to undeclared function 'avpicture_get_size'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
|
|
// https://blog.csdn.net/jacke121/article/details/79299129
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef void(*VideoCallback)(unsigned char* data_y, unsigned char* data_u, unsigned char* data_v, int line1, int line2, int line3, int width, int height, long pts);
|
|
|
|
#include <libavcodec/avcodec.h>
|
|
#include <libavutil/imgutils.h>
|
|
#define INBUF_SIZE 4096
|
|
|
|
typedef enum ErrorCode {
|
|
kErrorCode_Success = 0,
|
|
kErrorCode_Invalid_Param,
|
|
kErrorCode_Invalid_State,
|
|
kErrorCode_Invalid_Data,
|
|
kErrorCode_Invalid_Format,
|
|
kErrorCode_NULL_Pointer,
|
|
kErrorCode_Open_File_Error,
|
|
kErrorCode_Eof,
|
|
kErrorCode_FFmpeg_Error
|
|
}ErrorCode;
|
|
|
|
typedef enum LogLevel {
|
|
kLogLevel_None, //Not logging.
|
|
kLogLevel_Core, //Only logging core module(without ffmpeg).
|
|
kLogLevel_All //Logging all, with ffmpeg.
|
|
}LogLevel;
|
|
|
|
typedef enum DecoderType {
|
|
kDecoderType_H264,
|
|
kDecoderType_H265
|
|
}DecoderType;
|
|
|
|
LogLevel logLevel = kLogLevel_None;
|
|
DecoderType decoderType = kDecoderType_H265;
|
|
|
|
void simpleLog(const char* format, ...) {
|
|
if (logLevel == kLogLevel_None) {
|
|
return;
|
|
}
|
|
|
|
char szBuffer[1024] = { 0 };
|
|
char szTime[32] = { 0 };
|
|
char* p = NULL;
|
|
int prefixLength = 0;
|
|
const char* tag = "Core";
|
|
|
|
prefixLength = sprintf(szBuffer, "[%s][%s][DT] ", szTime, tag);
|
|
p = szBuffer + prefixLength;
|
|
|
|
if (1) {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
vsnprintf(p, 1024 - prefixLength, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
printf("%s\n", szBuffer);
|
|
}
|
|
|
|
void ffmpegLogCallback(void* ptr, int level, const char* fmt, va_list vl) {
|
|
static int printPrefix = 1;
|
|
static int count = 0;
|
|
static char prev[1024] = { 0 };
|
|
char line[1024] = { 0 };
|
|
static int is_atty;
|
|
AVClass* avc = ptr ? *(AVClass**)ptr : NULL;
|
|
if (level > AV_LOG_DEBUG) {
|
|
return;
|
|
}
|
|
|
|
line[0] = 0;
|
|
|
|
if (printPrefix && avc) {
|
|
if (avc->parent_log_context_offset) {
|
|
AVClass** parent = *(AVClass***)(((uint8_t*)ptr) + avc->parent_log_context_offset);
|
|
if (parent && *parent) {
|
|
snprintf(line, sizeof(line), "[%s @ %p] ", (*parent)->item_name(parent), parent);
|
|
}
|
|
}
|
|
snprintf(line + strlen(line), sizeof(line) - strlen(line), "[%s @ %p] ", avc->item_name(ptr), ptr);
|
|
}
|
|
|
|
vsnprintf(line + strlen(line), sizeof(line) - strlen(line), fmt, vl);
|
|
line[strlen(line) + 1] = 0;
|
|
simpleLog("%s", line);
|
|
}
|
|
|
|
VideoCallback videoCallback = NULL;
|
|
|
|
ErrorCode copyFrameData(AVFrame* src, AVFrame* dst, long ptslist[]) {
|
|
ErrorCode ret = kErrorCode_Success;
|
|
memcpy(dst->data, src->data, sizeof(src->data));
|
|
dst->linesize[0] = src->linesize[0];
|
|
dst->linesize[1] = src->linesize[1];
|
|
dst->linesize[2] = src->linesize[2];
|
|
dst->width = src->width;
|
|
dst->height = src->height;
|
|
long pts = LONG_MAX;
|
|
int index = -1;
|
|
for (int i = 0; i < 10; i++) {
|
|
if (ptslist[i] < pts) {
|
|
pts = ptslist[i];
|
|
index = i;
|
|
}
|
|
}
|
|
if (index > -1) {
|
|
ptslist[index] = LONG_MAX;
|
|
}
|
|
dst->pts = pts;
|
|
return ret;
|
|
}
|
|
|
|
unsigned char* yuvBuffer;
|
|
int videoSize = 0;
|
|
int initBuffer(int width, int height) {
|
|
videoSize=av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1);
|
|
// videoSize = avpicture_get_size(AV_PIX_FMT_YUV420P, width, height);
|
|
int bufferSize = 3 * videoSize;
|
|
yuvBuffer = (unsigned char*)av_mallocz(bufferSize);
|
|
return bufferSize;
|
|
}
|
|
|
|
static ErrorCode decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt, AVFrame* outFrame, long ptslist[])
|
|
{
|
|
ErrorCode res = kErrorCode_Success;
|
|
char buf[1024];
|
|
int ret;
|
|
|
|
ret = avcodec_send_packet(dec_ctx, pkt);
|
|
if (ret < 0) {
|
|
simpleLog("Error sending a packet for decoding\n");
|
|
res = kErrorCode_FFmpeg_Error;
|
|
}
|
|
else {
|
|
while (ret >= 0) {
|
|
ret = avcodec_receive_frame(dec_ctx, frame);
|
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
|
break;
|
|
}
|
|
else if (ret < 0) {
|
|
simpleLog("Error during decoding\n");
|
|
res = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
res = copyFrameData(frame, outFrame, ptslist);
|
|
if (res != kErrorCode_Success) {
|
|
break;
|
|
}
|
|
|
|
videoCallback(outFrame->data[0], outFrame->data[1], outFrame->data[2], outFrame->linesize[0], outFrame->linesize[1], outFrame->linesize[2], outFrame->width, outFrame->height, outFrame->pts);
|
|
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int isInit = 0;
|
|
const AVCodec* codec;
|
|
AVCodecParserContext* parser;
|
|
AVCodecContext* c = NULL;
|
|
AVPacket* pkt;
|
|
AVFrame* frame;
|
|
AVFrame* outFrame;
|
|
long ptslist[10];
|
|
|
|
ErrorCode openDecoder(int codecType, long callback, int logLv) {
|
|
ErrorCode ret = kErrorCode_Success;
|
|
do {
|
|
simpleLog("Initialize decoder.");
|
|
|
|
if (isInit != 0) {
|
|
break;
|
|
}
|
|
|
|
decoderType = codecType;
|
|
logLevel = logLv;
|
|
|
|
if (logLevel == kLogLevel_All) {
|
|
av_log_set_callback(ffmpegLogCallback);
|
|
}
|
|
|
|
/* find the video decoder */
|
|
if (decoderType == kDecoderType_H264) {
|
|
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
|
} else {
|
|
codec = avcodec_find_decoder(AV_CODEC_ID_H265);
|
|
}
|
|
|
|
if (!codec) {
|
|
simpleLog("Codec not found\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
parser = av_parser_init(codec->id);
|
|
if (!parser) {
|
|
simpleLog("parser not found\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
c = avcodec_alloc_context3(codec);
|
|
if (!c) {
|
|
simpleLog("Could not allocate video codec context\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
if (avcodec_open2(c, codec, NULL) < 0) {
|
|
simpleLog("Could not open codec\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
frame = av_frame_alloc();
|
|
if (!frame) {
|
|
simpleLog("Could not allocate video frame\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
outFrame = av_frame_alloc();
|
|
if (!outFrame) {
|
|
simpleLog("Could not allocate video frame\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
pkt = av_packet_alloc();
|
|
if (!pkt) {
|
|
simpleLog("Could not allocate video packet\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
ptslist[i] = LONG_MAX;
|
|
}
|
|
|
|
videoCallback = (VideoCallback)callback;
|
|
|
|
} while (0);
|
|
simpleLog("Decoder initialized %d.", ret);
|
|
return ret;
|
|
}
|
|
|
|
ErrorCode decodeData(unsigned char* data, size_t data_size, long pts) {
|
|
ErrorCode ret = kErrorCode_Success;
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
if (ptslist[i] == LONG_MAX) {
|
|
ptslist[i] = pts;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (data_size > 0) {
|
|
int size = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
|
|
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
|
|
if (size < 0) {
|
|
simpleLog("Error while parsing\n");
|
|
ret = kErrorCode_FFmpeg_Error;
|
|
break;
|
|
}
|
|
data += size;
|
|
data_size -= size;
|
|
|
|
if (pkt->size) {
|
|
ret = decode(c, frame, pkt, outFrame, ptslist);
|
|
if (ret != kErrorCode_Success) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ErrorCode flushDecoder() {
|
|
/* flush the decoder */
|
|
return decode(c, frame, NULL, outFrame, ptslist);
|
|
}
|
|
|
|
ErrorCode closeDecoder() {
|
|
ErrorCode ret = kErrorCode_Success;
|
|
|
|
do {
|
|
if (parser != NULL) {
|
|
av_parser_close(parser);
|
|
simpleLog("Video codec context closed.");
|
|
}
|
|
if (c != NULL) {
|
|
avcodec_free_context(&c);
|
|
simpleLog("Video codec context closed.");
|
|
}
|
|
if (frame != NULL) {
|
|
av_frame_free(&frame);
|
|
}
|
|
if (pkt != NULL) {
|
|
av_packet_free(&pkt);
|
|
}
|
|
if (yuvBuffer != NULL) {
|
|
av_freep(&yuvBuffer);
|
|
}
|
|
if (outFrame != NULL) {
|
|
av_frame_free(&outFrame);
|
|
}
|
|
simpleLog("All buffer released.");
|
|
} while (0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int callbackIndex = 0;
|
|
void vcb_frame(unsigned char* data_y, unsigned char* data_u, unsigned char* data_v, int line1, int line2, int line3, int width, int height, long pts) {
|
|
simpleLog("[%d]In video call back, size = %d * %d, pts = %ld\n", ++callbackIndex, width, height, pts);
|
|
int i = 0;
|
|
unsigned char* src = NULL;
|
|
FILE* dst = fopen("vcb.yuv", "a");
|
|
for (i = 0; i < height; i++) {
|
|
src = data_y + i * line1;
|
|
fwrite(src, width, 1, dst);
|
|
}
|
|
|
|
for (i = 0; i < height / 2; i++) {
|
|
src = data_u + i * line2;
|
|
fwrite(src, width/2, 1, dst);
|
|
}
|
|
|
|
for (i = 0; i < height / 2; i++) {
|
|
src = data_v + i * line3;
|
|
fwrite(src, width/2, 1, dst);
|
|
}
|
|
|
|
fclose(dst);
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
// openDecoder(1, vcb_frame, kLogLevel_Core);
|
|
//
|
|
// //const char* filename = "Forrest_Gump_IMAX.h264";
|
|
// const char* filename = "FourPeople_1280x720_60_1M.265";
|
|
//
|
|
// FILE* f;
|
|
//
|
|
// // uint8_t *data;
|
|
// size_t data_size;
|
|
//
|
|
// //uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
|
|
// ///* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
|
|
// //memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
|
|
//
|
|
// char* buffer;
|
|
// buffer = (char*)malloc(sizeof(char) * (INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE));
|
|
// if (buffer == NULL) {
|
|
// simpleLog("Memory error");
|
|
// exit(2);
|
|
// }
|
|
//
|
|
// f = fopen(filename, "rb");
|
|
// if (!f) {
|
|
// simpleLog("Could not open %s\n", filename);
|
|
// exit(1);
|
|
// }
|
|
//
|
|
// while (!feof(f)) {
|
|
// /* read raw data from the input file */
|
|
// //data_size = fread(inbuf, 1, INBUF_SIZE, f);
|
|
// data_size = fread(buffer, 1, INBUF_SIZE, f);
|
|
//
|
|
// if (!data_size)
|
|
// break;
|
|
//
|
|
// /* use the parser to split the data into frames */
|
|
// //data = inbuf;
|
|
// decodeData(buffer, data_size, 0);
|
|
// }
|
|
// fclose(f);
|
|
// free(buffer);
|
|
//
|
|
// flushDecoder();
|
|
// closeDecoder();
|
|
|
|
return 0;
|
|
}
|
|
|