#include #include #include #include typedef void(*VideoCallback)(unsigned char *buff, int size, double timestamp); typedef void(*AudioCallback)(unsigned char *buff, int size, double timestamp); typedef void(*RequestCallback)(int offset, int available); #ifdef __cplusplus extern "C" { #endif #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/fifo.h" //#include "libswscale/swscale.h" #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) const int kCustomIoBufferSize = 32 * 1024; const int kInitialPcmBufferSize = 128 * 1024; const int kDefaultFifoSize = 1 * 1024 * 1024; const int kMaxFifoSize = 16 * 1024 * 1024; 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, kErrorCode_Old_Frame } ErrorCode; typedef enum LogLevel { kLogLevel_None, //Not logging. kLogLevel_Core, //Only logging core module(without ffmpeg). kLogLevel_All //Logging all, with ffmpeg. } LogLevel; typedef struct WebDecoder { AVFormatContext *avformatContext; AVCodecContext *videoCodecContext; AVCodecContext *audioCodecContext; AVFrame *avFrame; int videoStreamIdx; int audioStreamIdx; VideoCallback videoCallback; AudioCallback audioCallback; RequestCallback requestCallback; unsigned char *yuvBuffer; //unsigned char *rgbBuffer; unsigned char *pcmBuffer; int currentPcmBufferSize; int videoBufferSize; int videoSize; //struct SwsContext* swsCtx; unsigned char *customIoBuffer; FILE *fp; char fileName[64]; int64_t fileSize; int64_t fileReadPos; int64_t fileWritePos; int64_t lastRequestOffset; double beginTimeOffset; int accurateSeek; // For streaming. int isStream; AVFifoBuffer *fifo; int fifoSize; } WebDecoder; WebDecoder *decoder = NULL; LogLevel logLevel = kLogLevel_None; int getAailableDataSize(); unsigned long getTickCount() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * (unsigned long)1000 + ts.tv_nsec / 1000000; } 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"; struct tm tmTime; struct timeb tb; ftime(&tb); localtime_r(&tb.time, &tmTime); if (1) { int tmYear = tmTime.tm_year + 1900; int tmMon = tmTime.tm_mon + 1; int tmMday = tmTime.tm_mday; int tmHour = tmTime.tm_hour; int tmMin = tmTime.tm_min; int tmSec = tmTime.tm_sec; int tmMillisec = tb.millitm; sprintf(szTime, "%d-%d-%d %d:%d:%d.%d", tmYear, tmMon, tmMday, tmHour, tmMin, tmSec, tmMillisec); } 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); } int openCodecContext(AVFormatContext *fmtCtx, enum AVMediaType type, int *streamIdx, AVCodecContext **decCtx) { int ret = 0; do { int streamIndex = -1; AVStream *st = NULL; AVCodec *dec = NULL; AVDictionary *opts = NULL; ret = av_find_best_stream(fmtCtx, type, -1, -1, NULL, 0); if (ret < 0) { simpleLog("Could not find %s stream.", av_get_media_type_string(type)); break; } streamIndex = ret; st = fmtCtx->streams[streamIndex]; dec = avcodec_find_decoder(st->codecpar->codec_id); if (!dec) { simpleLog("Failed to find %s codec %d.", av_get_media_type_string(type), st->codecpar->codec_id); ret = AVERROR(EINVAL); break; } *decCtx = avcodec_alloc_context3(dec); if (!*decCtx) { simpleLog("Failed to allocate the %s codec context.", av_get_media_type_string(type)); ret = AVERROR(ENOMEM); break; } if ((ret = avcodec_parameters_to_context(*decCtx, st->codecpar)) != 0) { simpleLog("Failed to copy %s codec parameters to decoder context.", av_get_media_type_string(type)); break; } av_dict_set(&opts, "refcounted_frames", "0", 0); if ((ret = avcodec_open2(*decCtx, dec, NULL)) != 0) { simpleLog("Failed to open %s codec.", av_get_media_type_string(type)); break; } *streamIdx = streamIndex; avcodec_flush_buffers(*decCtx); } while (0); return ret; } void closeCodecContext(AVFormatContext *fmtCtx, AVCodecContext *decCtx, int streamIdx) { do { if (fmtCtx == NULL || decCtx == NULL) { break; } if (streamIdx < 0 || streamIdx >= fmtCtx->nb_streams) { break; } fmtCtx->streams[streamIdx]->discard = AVDISCARD_ALL; avcodec_close(decCtx); } while (0); } ErrorCode copyYuvData(AVFrame *frame, unsigned char *buffer, int width, int height) { ErrorCode ret = kErrorCode_Success; unsigned char *src = NULL; unsigned char *dst = buffer; int i = 0; do { if (frame == NULL || buffer == NULL) { ret = kErrorCode_Invalid_Param; break; } if (!frame->data[0] || !frame->data[1] || !frame->data[2]) { ret = kErrorCode_Invalid_Param; break; } for (i = 0; i < height; i++) { src = frame->data[0] + i * frame->linesize[0]; memcpy(dst, src, width); dst += width; } for (i = 0; i < height / 2; i++) { src = frame->data[1] + i * frame->linesize[1]; memcpy(dst, src, width / 2); dst += width / 2; } for (i = 0; i < height / 2; i++) { src = frame->data[2] + i * frame->linesize[2]; memcpy(dst, src, width / 2); dst += width / 2; } } while (0); return ret; } /* ErrorCode yuv420pToRgb32(unsigned char *yuvBuff, unsigned char *rgbBuff, int width, int height) { ErrorCode ret = kErrorCode_Success; AVPicture yuvPicture, rgbPicture; uint8_t *ptmp = NULL; do { if (yuvBuff == NULL || rgbBuff == NULL) { ret = kErrorCode_Invalid_Param break; } if (decoder == NULL || decoder->swsCtx == NULL) { ret = kErrorCode_Invalid_Param break; } avpicture_fill(&yuvPicture, yuvBuff, AV_PIX_FMT_YUV420P, width, height); avpicture_fill(&rgbPicture, rgbBuff, AV_PIX_FMT_RGB32, width, height); ptmp = yuvPicture.data[1]; yuvPicture.data[1] = yuvPicture.data[2]; yuvPicture.data[2] = ptmp; sws_scale(decoder->swsCtx, yuvPicture.data, yuvPicture.linesize, 0, height, rgbPicture.data, rgbPicture.linesize); } while (0); return ret; } */ int roundUp(int numToRound, int multiple) { return (numToRound + multiple - 1) & -multiple; } ErrorCode processDecodedVideoFrame(AVFrame *frame) { ErrorCode ret = kErrorCode_Success; double timestamp = 0.0f; do { if (frame == NULL || decoder->videoCallback == NULL || decoder->yuvBuffer == NULL || decoder->videoBufferSize <= 0) { ret = kErrorCode_Invalid_Param; break; } if (decoder->videoCodecContext->pix_fmt != AV_PIX_FMT_YUV420P) { simpleLog("Not YUV420P, but unsupported format %d.", decoder->videoCodecContext->pix_fmt); ret = kErrorCode_Invalid_Format; break; } ret = copyYuvData(frame, decoder->yuvBuffer, decoder->videoCodecContext->width, decoder->videoCodecContext->height); if (ret != kErrorCode_Success) { break; } /* ret = yuv420pToRgb32(decoder->yuvBuffer, decoder->rgbBuffer, decoder->videoCodecContext->width, decoder->videoCodecContext->height); if (ret != kErrorCode_Success) { break; } */ timestamp = (double)frame->pts * av_q2d(decoder->avformatContext->streams[decoder->videoStreamIdx]->time_base); if (decoder->accurateSeek && timestamp < decoder->beginTimeOffset) { //simpleLog("video timestamp %lf < %lf", timestamp, decoder->beginTimeOffset); ret = kErrorCode_Old_Frame; break; } decoder->videoCallback(decoder->yuvBuffer, decoder->videoSize, timestamp); } while (0); return ret; } ErrorCode processDecodedAudioFrame(AVFrame *frame) { ErrorCode ret = kErrorCode_Success; int sampleSize = 0; int audioDataSize = 0; int targetSize = 0; int offset = 0; int i = 0; int ch = 0; double timestamp = 0.0f; do { if (frame == NULL) { ret = kErrorCode_Invalid_Param; break; } sampleSize = av_get_bytes_per_sample(decoder->audioCodecContext->sample_fmt); if (sampleSize < 0) { simpleLog("Failed to calculate data size."); ret = kErrorCode_Invalid_Data; break; } if (decoder->pcmBuffer == NULL) { decoder->pcmBuffer = (unsigned char*)av_mallocz(kInitialPcmBufferSize); decoder->currentPcmBufferSize = kInitialPcmBufferSize; simpleLog("Initial PCM buffer size %d.", decoder->currentPcmBufferSize); } audioDataSize = frame->nb_samples * decoder->audioCodecContext->channels * sampleSize; if (decoder->currentPcmBufferSize < audioDataSize) { targetSize = roundUp(audioDataSize, 4); simpleLog("Current PCM buffer size %d not sufficient for data size %d, round up to target %d.", decoder->currentPcmBufferSize, audioDataSize, targetSize); decoder->currentPcmBufferSize = targetSize; av_free(decoder->pcmBuffer); decoder->pcmBuffer = (unsigned char*)av_mallocz(decoder->currentPcmBufferSize); } for (i = 0; i < frame->nb_samples; i++) { for (ch = 0; ch < decoder->audioCodecContext->channels; ch++) { memcpy(decoder->pcmBuffer + offset, frame->data[ch] + sampleSize * i, sampleSize); offset += sampleSize; } } timestamp = (double)frame->pts * av_q2d(decoder->avformatContext->streams[decoder->audioStreamIdx]->time_base); if (decoder->accurateSeek && timestamp < decoder->beginTimeOffset) { //simpleLog("audio timestamp %lf < %lf", timestamp, decoder->beginTimeOffset); ret = kErrorCode_Old_Frame; break; } if (decoder->audioCallback != NULL) { decoder->audioCallback(decoder->pcmBuffer, audioDataSize, timestamp); } } while (0); return ret; } ErrorCode decodePacket(AVPacket *pkt, int *decodedLen) { int ret = 0; int isVideo = 0; AVCodecContext *codecContext = NULL; if (pkt == NULL || decodedLen == NULL) { simpleLog("decodePacket invalid param."); return kErrorCode_Invalid_Param; } *decodedLen = 0; if (pkt->stream_index == decoder->videoStreamIdx) { codecContext = decoder->videoCodecContext; isVideo = 1; } else if (pkt->stream_index == decoder->audioStreamIdx) { codecContext = decoder->audioCodecContext; isVideo = 0; } else { return kErrorCode_Invalid_Data; } ret = avcodec_send_packet(codecContext, pkt); if (ret < 0) { simpleLog("Error sending a packet for decoding %d.", ret); return kErrorCode_FFmpeg_Error; } while (ret >= 0) { ret = avcodec_receive_frame(codecContext, decoder->avFrame); if (ret == AVERROR(EAGAIN)) { return kErrorCode_Success; } else if (ret == AVERROR_EOF) { return kErrorCode_Eof; } else if (ret < 0) { simpleLog("Error during decoding %d.", ret); return kErrorCode_FFmpeg_Error; } else { int r = isVideo ? processDecodedVideoFrame(decoder->avFrame) : processDecodedAudioFrame(decoder->avFrame); if (r == kErrorCode_Old_Frame) { return r; } } } *decodedLen = pkt->size; return kErrorCode_Success; } int readFromFile(uint8_t *data, int len) { //simpleLog("readFromFile %d.", len); int32_t ret = -1; int availableBytes = 0; int canReadLen = 0; do { if (decoder->fp == NULL) { break; } availableBytes = decoder->fileWritePos - decoder->fileReadPos; if (availableBytes <= 0) { break; } fseek(decoder->fp, decoder->fileReadPos, SEEK_SET); canReadLen = MIN(availableBytes, len); fread(data, canReadLen, 1, decoder->fp); decoder->fileReadPos += canReadLen; ret = canReadLen; } while (0); //simpleLog("readFromFile ret %d.", ret); return ret; } int readFromFifo(uint8_t *data, int len) { //simpleLog("readFromFifo %d.", len); int32_t ret = -1; int availableBytes = 0; int canReadLen = 0; do { if (decoder->fifo == NULL) { break; } availableBytes = av_fifo_size(decoder->fifo); if (availableBytes <= 0) { break; } canReadLen = MIN(availableBytes, len); av_fifo_generic_read(decoder->fifo, data, canReadLen, NULL); ret = canReadLen; } while (0); //simpleLog("readFromFifo ret %d, left %d.", ret, av_fifo_size(decoder->fifo)); return ret; } int readCallback(void *opaque, uint8_t *data, int len) { //simpleLog("readCallback %d.", len); int32_t ret = -1; do { if (decoder == NULL) { break; } if (data == NULL || len <= 0) { break; } ret = decoder->isStream ? readFromFifo(data, len) : readFromFile(data, len); } while (0); //simpleLog("readCallback ret %d.", ret); return ret; } int64_t seekCallback(void *opaque, int64_t offset, int whence) { int64_t ret = -1; int64_t pos = -1; int64_t req_pos = -1; //simpleLog("seekCallback %lld %d.", offset, whence); do { if (decoder == NULL || decoder->isStream || decoder->fp == NULL) { break; } if (whence == AVSEEK_SIZE) { ret = decoder->fileSize; break; } if (whence != SEEK_END && whence != SEEK_SET && whence != SEEK_CUR) { break; } ret = fseek(decoder->fp, (long)offset, whence); if (ret == -1) { break; } pos = (int64_t)ftell(decoder->fp); if (pos < decoder->lastRequestOffset || pos > decoder->fileWritePos) { decoder->lastRequestOffset = pos; decoder->fileReadPos = pos; decoder->fileWritePos = pos; req_pos = pos; ret = -1; // Forcing not to call read at once. decoder->requestCallback(pos, getAailableDataSize()); simpleLog("Will request %lld and return %lld.", pos, ret); break; } decoder->fileReadPos = pos; ret = pos; } while (0); //simpleLog("seekCallback return %lld.", ret); if (decoder != NULL && decoder->requestCallback != NULL) { decoder->requestCallback(req_pos, getAailableDataSize()); } return ret; } int writeToFile(unsigned char *buff, int size) { int ret = 0; int64_t leftBytes = 0; int canWriteBytes = 0; do { if (decoder->fp == NULL) { ret = -1; break; } leftBytes = decoder->fileSize - decoder->fileWritePos; if (leftBytes <= 0) { break; } canWriteBytes = MIN(leftBytes, size); fseek(decoder->fp, decoder->fileWritePos, SEEK_SET); fwrite(buff, canWriteBytes, 1, decoder->fp); decoder->fileWritePos += canWriteBytes; ret = canWriteBytes; } while (0); return ret; } int writeToFifo(unsigned char *buff, int size) { int ret = 0; do { if (decoder->fifo == NULL) { ret = -1; break; } int64_t leftSpace = av_fifo_space(decoder->fifo); if (leftSpace < size) { int growSize = 0; do { leftSpace += decoder->fifoSize; growSize += decoder->fifoSize; decoder->fifoSize += decoder->fifoSize; } while (leftSpace < size); av_fifo_grow(decoder->fifo, growSize); simpleLog("Fifo size growed to %d.", decoder->fifoSize); if (decoder->fifoSize >= kMaxFifoSize) { simpleLog("[Warn] Fifo size larger than %d.", kMaxFifoSize); } } //simpleLog("Wrote %d bytes to fifo, total %d.", size, av_fifo_size(decoder->fifo)); ret = av_fifo_generic_write(decoder->fifo, buff, size, NULL); } while (0); return ret; } int getAailableDataSize() { int ret = 0; do { if (decoder == NULL) { break; } if (decoder->isStream) { ret = decoder->fifo == NULL ? 0 : av_fifo_size(decoder->fifo); } else { ret = decoder->fileWritePos - decoder->fileReadPos; } } while (0); return ret; } //////////////////////////////////Export methods//////////////////////////////////////// ErrorCode initDecoder(int fileSize, int logLv) { ErrorCode ret = kErrorCode_Success; do { //Log level. logLevel = logLv; if (decoder != NULL) { break; } decoder = (WebDecoder *)av_mallocz(sizeof(WebDecoder)); if (fileSize >= 0) { decoder->fileSize = fileSize; sprintf(decoder->fileName, "tmp-%lu.mp4", getTickCount()); decoder->fp = fopen(decoder->fileName, "wb+"); if (decoder->fp == NULL) { simpleLog("Open file %s failed, err: %d.", decoder->fileName, errno); ret = kErrorCode_Open_File_Error; av_free(decoder); decoder = NULL; } } else { decoder->isStream = 1; decoder->fifoSize = kDefaultFifoSize; decoder->fifo = av_fifo_alloc(decoder->fifoSize); } } while (0); simpleLog("Decoder initialized %d.", ret); return ret; } ErrorCode uninitDecoder() { if (decoder != NULL) { if (decoder->fp != NULL) { fclose(decoder->fp); decoder->fp = NULL; remove(decoder->fileName); } if (decoder->fifo != NULL) { av_fifo_freep(&decoder->fifo); } av_freep(&decoder); } av_log_set_callback(NULL); simpleLog("Decoder uninitialized."); return kErrorCode_Success; } ErrorCode openDecoder(int *paramArray, int paramCount, long videoCallback, long audioCallback, long requestCallback) { ErrorCode ret = kErrorCode_Success; int r = 0; int i = 0; int params[7] = { 0 }; do { simpleLog("打开编码器."); av_register_all(); avcodec_register_all(); if (logLevel == kLogLevel_All) { av_log_set_callback(ffmpegLogCallback); } decoder->avformatContext = avformat_alloc_context(); decoder->customIoBuffer = (unsigned char*)av_mallocz(kCustomIoBufferSize); AVIOContext* ioContext = avio_alloc_context( decoder->customIoBuffer, kCustomIoBufferSize, 0, NULL, readCallback, NULL, seekCallback); if (ioContext == NULL) { ret = kErrorCode_FFmpeg_Error; simpleLog("avio_alloc_context failed."); break; } decoder->avformatContext->pb = ioContext; decoder->avformatContext->flags = AVFMT_FLAG_CUSTOM_IO; simpleLog("avformat_open_input."); r = avformat_open_input(&decoder->avformatContext, NULL, NULL, NULL); if (r != 0) { ret = kErrorCode_FFmpeg_Error; char err_info[32] = { 0 }; av_strerror(ret, err_info, 32); simpleLog("avformat_open_input failed %d %s.", ret, err_info); break; } simpleLog("avformat_find_stream_info"); r = avformat_find_stream_info(decoder->avformatContext, NULL); if (r != 0) { ret = kErrorCode_FFmpeg_Error; simpleLog("av_find_stream_info failed %d.", ret); break; } simpleLog("avformat_find_stream_info 成功."); for (i = 0; i < decoder->avformatContext->nb_streams; i++) { decoder->avformatContext->streams[i]->discard = AVDISCARD_DEFAULT; } r = openCodecContext( decoder->avformatContext, AVMEDIA_TYPE_VIDEO, &decoder->videoStreamIdx, &decoder->videoCodecContext); if (r != 0) { ret = kErrorCode_FFmpeg_Error; simpleLog("Open video codec context failed %d.", ret); break; } simpleLog("Open video codec context success, video stream index %d %x.", decoder->videoStreamIdx, (unsigned int)decoder->videoCodecContext); simpleLog("Video stream index:%d pix_fmt:%d resolution:%d*%d.", decoder->videoStreamIdx, decoder->videoCodecContext->pix_fmt, decoder->videoCodecContext->width, decoder->videoCodecContext->height); r = openCodecContext( decoder->avformatContext, AVMEDIA_TYPE_AUDIO, &decoder->audioStreamIdx, &decoder->audioCodecContext); if (r != 0) { ret = kErrorCode_FFmpeg_Error; simpleLog("Open audio codec context failed %d.", ret); break; } simpleLog("Open audio codec context success, audio stream index %d %x.", decoder->audioStreamIdx, (unsigned int)decoder->audioCodecContext); simpleLog("Audio stream index:%d sample_fmt:%d channel:%d, sample rate:%d.", decoder->audioStreamIdx, decoder->audioCodecContext->sample_fmt, decoder->audioCodecContext->channels, decoder->audioCodecContext->sample_rate); av_seek_frame(decoder->avformatContext, -1, 0, AVSEEK_FLAG_BACKWARD); /* For RGB Renderer(2D WebGL). decoder->swsCtx = sws_getContext( decoder->videoCodecContext->width, decoder->videoCodecContext->height, decoder->videoCodecContext->pix_fmt, decoder->videoCodecContext->width, decoder->videoCodecContext->height, AV_PIX_FMT_RGB32, SWS_BILINEAR, 0, 0, 0); if (decoder->swsCtx == NULL) { simpleLog("sws_getContext failed."); ret = kErrorCode_FFmpeg_Error; break; } */ decoder->videoSize = avpicture_get_size( decoder->videoCodecContext->pix_fmt, decoder->videoCodecContext->width, decoder->videoCodecContext->height); decoder->videoBufferSize = 3 * decoder->videoSize; decoder->yuvBuffer = (unsigned char *)av_mallocz(decoder->videoBufferSize); decoder->avFrame = av_frame_alloc(); params[0] = 1000 * (decoder->avformatContext->duration + 5000) / AV_TIME_BASE; params[1] = decoder->videoCodecContext->pix_fmt; params[2] = decoder->videoCodecContext->width; params[3] = decoder->videoCodecContext->height; params[4] = decoder->audioCodecContext->sample_fmt; params[5] = decoder->audioCodecContext->channels; params[6] = decoder->audioCodecContext->sample_rate; enum AVSampleFormat sampleFmt = decoder->audioCodecContext->sample_fmt; if (av_sample_fmt_is_planar(sampleFmt)) { const char *packed = av_get_sample_fmt_name(sampleFmt); params[4] = av_get_packed_sample_fmt(sampleFmt); } if (paramArray != NULL && paramCount > 0) { for (int i = 0; i < paramCount; ++i) { paramArray[i] = params[i]; } } decoder->videoCallback = (VideoCallback)videoCallback; decoder->audioCallback = (AudioCallback)audioCallback; decoder->requestCallback = (RequestCallback)requestCallback; simpleLog("Decoder opened, duration %ds, picture size %d.", params[0], decoder->videoSize); } while (0); if (ret != kErrorCode_Success && decoder != NULL) { av_freep(&decoder); } return ret; } ErrorCode closeDecoder() { ErrorCode ret = kErrorCode_Success; do { if (decoder == NULL || decoder->avformatContext == NULL) { break; } if (decoder->videoCodecContext != NULL) { closeCodecContext(decoder->avformatContext, decoder->videoCodecContext, decoder->videoStreamIdx); decoder->videoCodecContext = NULL; simpleLog("Video codec context closed."); } if (decoder->audioCodecContext != NULL) { closeCodecContext(decoder->avformatContext, decoder->audioCodecContext, decoder->audioStreamIdx); decoder->audioCodecContext = NULL; simpleLog("Audio codec context closed."); } AVIOContext *pb = decoder->avformatContext->pb; if (pb != NULL) { if (pb->buffer != NULL) { av_freep(&pb->buffer); decoder->customIoBuffer = NULL; } av_freep(&decoder->avformatContext->pb); simpleLog("IO context released."); } avformat_close_input(&decoder->avformatContext); decoder->avformatContext = NULL; simpleLog("Input closed."); if (decoder->yuvBuffer != NULL) { av_freep(&decoder->yuvBuffer); } if (decoder->pcmBuffer != NULL) { av_freep(&decoder->pcmBuffer); } if (decoder->avFrame != NULL) { av_freep(&decoder->avFrame); } simpleLog("All buffer released."); } while (0); return ret; } int sendData(unsigned char *buff, int size) { int ret = 0; int64_t leftBytes = 0; int canWriteBytes = 0; do { if (decoder == NULL) { ret = -1; break; } if (buff == NULL || size == 0) { ret = -2; break; } ret = decoder->isStream ? writeToFifo(buff, size) : writeToFile(buff, size); } while (0); return ret; } ErrorCode decodeOnePacket() { ErrorCode ret = kErrorCode_Success; int decodedLen = 0; int r = 0; AVPacket packet; av_init_packet(&packet); do { if (decoder == NULL) { ret = kErrorCode_Invalid_State; break; } if (getAailableDataSize() <= 0) { ret = kErrorCode_Invalid_State; break; } packet.data = NULL; packet.size = 0; r = av_read_frame(decoder->avformatContext, &packet); if (r == AVERROR_EOF) { ret = kErrorCode_Eof; break; } if (r < 0 || packet.size == 0) { break; } do { ret = decodePacket(&packet, &decodedLen); if (ret != kErrorCode_Success) { break; } if (decodedLen <= 0) { break; } packet.data += decodedLen; packet.size -= decodedLen; } while (packet.size > 0); } while (0); av_packet_unref(&packet); return ret; } ErrorCode seekTo(int ms, int accurateSeek) { int ret = 0; int64_t pts = (int64_t)ms * 1000; decoder->accurateSeek = accurateSeek; ret = avformat_seek_file(decoder->avformatContext, -1, INT64_MIN, pts, pts, AVSEEK_FLAG_BACKWARD); simpleLog("Native seek to %d return %d %d.", ms, ret, decoder->accurateSeek); if (ret == -1) { return kErrorCode_FFmpeg_Error; } else { avcodec_flush_buffers(decoder->videoCodecContext); avcodec_flush_buffers(decoder->audioCodecContext); // Trigger seek callback AVPacket packet; av_init_packet(&packet); av_read_frame(decoder->avformatContext, &packet); decoder->beginTimeOffset = (double)ms / 1000; return kErrorCode_Success; } } int main() { //simpleLog("Native loaded."); return 0; } #ifdef __cplusplus } #endif