blob: bec040b424299eeff291c6d1e1300af0d4f79f6b [file] [log] [blame]
/*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Description:
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <pthread.h>
#define INBUF_SIZE (4096)
#define DUMP_DIR "/data/video_frames"
static int dump;
typedef struct VcodecCtx {
AVFormatContext *fmt_ctx;
AVStream *stream;
int nb_streams;
int vst_idx;
char *filename;
pthread_t tid;
pthread_mutex_t pthread_mutex;
pthread_cond_t pthread_cond;
} VcodecCtx;
static void dump_yuv(AVFrame *frame, char *filename)
{
FILE *f;
printf("name: %s, resolution: %dx%d, size: %d\n",
filename, frame->width, frame->height,
frame->buf[0]->size + frame->buf[1]->size);
f = fopen(filename,"w+");
fwrite(frame->buf[0]->data, 1, frame->buf[0]->size, f);
fwrite(frame->buf[1]->data, 1, frame->buf[1]->size, f);
fclose(f);
}
static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
const char *filename)
{
char buf[1024];
int ret;
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error sending a packet for decoding\n");
return;
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decoding, ret: %s\n", av_err2str(ret));
break;
}
//fprintf(stderr, "saving frame %3d\n", dec_ctx->frame_number);
fflush(stdout);
/* the picture is allocated by the decoder. no need to free it */
snprintf(buf, sizeof(buf), "%s/frame-%d", filename, dec_ctx->frame_number);
if (dump)
dump_yuv(frame, buf);
}
}
static void* read_thread(void *arg)
{
int ret, err;
AVFormatContext *ic = NULL;
AVCodecContext *dec_ctx = NULL;
AVStream *stream = NULL;
AVCodec *codec = NULL;
AVPacket pkt1, *pkt = &pkt1;
AVFrame *frame = NULL;
int vst_idx = 0;
int has_video = 0;
unsigned int st_idx = 0;
VcodecCtx *vctx = arg;
const char *forced_codec_name = NULL;
printf("entry read thread, tid: %ld.\n", vctx->tid);
ic = avformat_alloc_context();
if (!ic) {
fprintf(stderr, "Could not allocate avformat context.\n");
goto out;
}
err = avformat_open_input(&ic, vctx->filename, NULL, NULL);
if (err < 0) {
fprintf(stderr, "Could not open avformat input.\n");
goto out;
}
err = avformat_find_stream_info(ic, NULL);
if (err < 0) {
fprintf(stderr, "find stream info err.\n");
goto out;
}
for (st_idx = 0; st_idx < ic->nb_streams; st_idx++) {
AVStream *st = ic->streams[st_idx];
enum AVMediaType type = st->codecpar->codec_type;
st->discard = AVDISCARD_ALL;
if (type == AVMEDIA_TYPE_VIDEO) {
st->discard = AVDISCARD_NONE;
vctx->vst_idx = st_idx;
has_video = 1;
break;
}
}
if (!has_video) {
fprintf(stderr, "no video stream.\n");
goto out;
}
stream = ic->streams[vctx->vst_idx];
//codec = avcodec_find_decoder(stream->codecpar->codec_id);
switch (stream->codecpar->codec_id) {
case AV_CODEC_ID_H264:
forced_codec_name = "h264_v4l2m2m";
break;
case AV_CODEC_ID_HEVC:
forced_codec_name = "hevc_v4l2m2m";
break;
case AV_CODEC_ID_VP9:
forced_codec_name = "vp9_v4l2m2m";
break;
case AV_CODEC_ID_MPEG1VIDEO:
forced_codec_name = "mpeg1_v4l2m2m";
break;
case AV_CODEC_ID_MPEG2VIDEO:
forced_codec_name = "mpeg2_v4l2m2m";
break;
case AV_CODEC_ID_VC1:
forced_codec_name = "vc1_v4l2m2m";
break;
case AV_CODEC_ID_H263:
forced_codec_name = "h263_v4l2m2m";
break;
case AV_CODEC_ID_MPEG4:
forced_codec_name = "mpeg4_v4l2m2m";
break;
case AV_CODEC_ID_MJPEG:
forced_codec_name = "mjpeg_v4l2m2m";
break;
}
codec = avcodec_find_decoder_by_name(forced_codec_name);
if (!codec) {
fprintf(stderr, "Unsupported codec with id %d for input stream %d\n",
stream->codecpar->codec_id, stream->index);
goto out;
}
dec_ctx = avcodec_alloc_context3(codec);
if (!dec_ctx) {
fprintf(stderr, "Could not allocate video codec context\n");
goto out;
}
err = avcodec_parameters_to_context(dec_ctx, stream->codecpar);
if (err < 0) {
fprintf(stderr, "Could not set paras to context\n");
goto out;
}
av_codec_set_pkt_timebase(dec_ctx, stream->time_base);
dec_ctx->framerate = stream->avg_frame_rate;
if (avcodec_open2(dec_ctx, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec for input stream %d\n",
stream->index);
goto out;
}
printf("fmt ctx: %p, stream: %p, video st idx: %d, num: %d\n",
ic, stream, vst_idx, ic->nb_streams);
printf("format: %s\n",ic->iformat->name);
ic->flags |= AVFMT_FLAG_GENPTS;
ic->debug = 0xff;
//if (ic->pb)
// ic->pb->eof_reached = 0;
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
goto out;
}
for (;;) {
ret = av_read_frame(ic, pkt);
if (ret < 0) {
if (ret == AVERROR_EOF || avio_feof(ic->pb)) {
printf("read data end, ret: %d.\n", ret);
break;
}
if (ic->pb && ic->pb->error)
break;
printf("read data fail, ret: %d.\n", ret);
continue;
}
if (pkt->stream_index == vctx->vst_idx) {
//packet_queue_put(&is->audioq, pkt);
//printf("read video data size: %d.\n", pkt->size);
if (pkt->size)
decode(dec_ctx, frame, pkt, DUMP_DIR);
}
av_packet_unref(pkt);
usleep(8 * 1000);
}
/* flush the decoder */
decode(dec_ctx, frame, NULL, DUMP_DIR);
out:
if (dec_ctx)
avcodec_free_context(&dec_ctx);
if (frame)
av_frame_free(&frame);
if (ic) {
avformat_close_input(&ic);
avformat_free_context(ic);
}
printf("read thread exit.\n");
pthread_mutex_lock(&vctx->pthread_mutex);
pthread_cond_signal(&vctx->pthread_cond);
pthread_mutex_unlock(&vctx->pthread_mutex);
return NULL;
}
static int open_input_file(const char *filename)
{
int ret;
VcodecCtx *vctx;
pthread_t pid = pthread_self();
vctx = av_mallocz(sizeof(VcodecCtx));
if (!vctx)
return -1;
vctx->filename = av_strdup(filename);
if (!vctx->filename) {
av_free(vctx);
return -1;
}
pthread_mutex_init(&vctx->pthread_mutex, NULL);
pthread_cond_init(&vctx->pthread_cond, NULL);
ret = pthread_create(&vctx->tid, NULL, read_thread, (void *)vctx);
if (ret == 0) {
pthread_setname_np(pid, "read_thread");
pthread_mutex_lock(&vctx->pthread_mutex);
pthread_cond_wait(&vctx->pthread_cond, &vctx->pthread_mutex);
pthread_join(vctx->tid, NULL);
pthread_mutex_unlock(&vctx->pthread_mutex);
}
av_free(vctx->filename);
av_free(vctx);
printf("creat the read thread, ret: %d.\n", ret);
return 0;
}
int main(int argc, char **argv)
{
int ret;
const char *filename;
int log_level = 0;
if (argc < 2) {
fprintf(stderr, "Usage: %s <input file>\n ==> %s/frame-123\n", argv[0], DUMP_DIR);
exit(0);
}
filename = argv[1];
if (argv[2]) {
if (!strcmp(argv[2], "dump"))
dump = 1;
else
log_level = atoi(argv[2]);
}
mkdir(DUMP_DIR, 0664);
/*set debug level*/
av_log_set_level(log_level); //AV_LOG_DEBUG
/* register all the codecs */
avcodec_register_all();
ret = open_input_file(filename);
if (ret < 0)
fprintf(stderr, "open input file fail.\n");
return 0;
}