blob: bec040b424299eeff291c6d1e1300af0d4f79f6b
1 | /* |
2 | * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
12 | * more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License along |
15 | * with this program; if not, write to the Free Software Foundation, Inc., |
16 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | * |
18 | * Description: |
19 | */ |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | #include <libavcodec/avcodec.h> |
24 | #include <libavformat/avformat.h> |
25 | #include <pthread.h> |
26 | #include <unistd.h> |
27 | #include <sys/stat.h> |
28 | #include <pthread.h> |
29 | |
30 | #define INBUF_SIZE (4096) |
31 | #define DUMP_DIR "/data/video_frames" |
32 | static int dump; |
33 | |
34 | typedef struct VcodecCtx { |
35 | AVFormatContext *fmt_ctx; |
36 | AVStream *stream; |
37 | int nb_streams; |
38 | int vst_idx; |
39 | char *filename; |
40 | pthread_t tid; |
41 | pthread_mutex_t pthread_mutex; |
42 | pthread_cond_t pthread_cond; |
43 | } VcodecCtx; |
44 | |
45 | static void dump_yuv(AVFrame *frame, char *filename) |
46 | { |
47 | FILE *f; |
48 | |
49 | printf("name: %s, resolution: %dx%d, size: %d\n", |
50 | filename, frame->width, frame->height, |
51 | frame->buf[0]->size + frame->buf[1]->size); |
52 | |
53 | f = fopen(filename,"w+"); |
54 | |
55 | fwrite(frame->buf[0]->data, 1, frame->buf[0]->size, f); |
56 | fwrite(frame->buf[1]->data, 1, frame->buf[1]->size, f); |
57 | |
58 | fclose(f); |
59 | } |
60 | |
61 | static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt, |
62 | const char *filename) |
63 | { |
64 | char buf[1024]; |
65 | int ret; |
66 | |
67 | ret = avcodec_send_packet(dec_ctx, pkt); |
68 | if (ret < 0) { |
69 | fprintf(stderr, "Error sending a packet for decoding\n"); |
70 | return; |
71 | } |
72 | |
73 | while (ret >= 0) { |
74 | ret = avcodec_receive_frame(dec_ctx, frame); |
75 | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) |
76 | return; |
77 | else if (ret < 0) { |
78 | fprintf(stderr, "Error during decoding, ret: %s\n", av_err2str(ret)); |
79 | break; |
80 | } |
81 | |
82 | //fprintf(stderr, "saving frame %3d\n", dec_ctx->frame_number); |
83 | fflush(stdout); |
84 | |
85 | /* the picture is allocated by the decoder. no need to free it */ |
86 | snprintf(buf, sizeof(buf), "%s/frame-%d", filename, dec_ctx->frame_number); |
87 | |
88 | if (dump) |
89 | dump_yuv(frame, buf); |
90 | } |
91 | } |
92 | |
93 | static void* read_thread(void *arg) |
94 | { |
95 | int ret, err; |
96 | AVFormatContext *ic = NULL; |
97 | AVCodecContext *dec_ctx = NULL; |
98 | AVStream *stream = NULL; |
99 | AVCodec *codec = NULL; |
100 | AVPacket pkt1, *pkt = &pkt1; |
101 | AVFrame *frame = NULL; |
102 | int vst_idx = 0; |
103 | int has_video = 0; |
104 | unsigned int st_idx = 0; |
105 | VcodecCtx *vctx = arg; |
106 | const char *forced_codec_name = NULL; |
107 | |
108 | printf("entry read thread, tid: %ld.\n", vctx->tid); |
109 | |
110 | ic = avformat_alloc_context(); |
111 | if (!ic) { |
112 | fprintf(stderr, "Could not allocate avformat context.\n"); |
113 | goto out; |
114 | } |
115 | |
116 | err = avformat_open_input(&ic, vctx->filename, NULL, NULL); |
117 | if (err < 0) { |
118 | fprintf(stderr, "Could not open avformat input.\n"); |
119 | goto out; |
120 | } |
121 | |
122 | err = avformat_find_stream_info(ic, NULL); |
123 | if (err < 0) { |
124 | fprintf(stderr, "find stream info err.\n"); |
125 | goto out; |
126 | } |
127 | |
128 | for (st_idx = 0; st_idx < ic->nb_streams; st_idx++) { |
129 | AVStream *st = ic->streams[st_idx]; |
130 | |
131 | enum AVMediaType type = st->codecpar->codec_type; |
132 | st->discard = AVDISCARD_ALL; |
133 | |
134 | if (type == AVMEDIA_TYPE_VIDEO) { |
135 | st->discard = AVDISCARD_NONE; |
136 | vctx->vst_idx = st_idx; |
137 | has_video = 1; |
138 | break; |
139 | } |
140 | } |
141 | |
142 | if (!has_video) { |
143 | fprintf(stderr, "no video stream.\n"); |
144 | goto out; |
145 | } |
146 | |
147 | stream = ic->streams[vctx->vst_idx]; |
148 | |
149 | //codec = avcodec_find_decoder(stream->codecpar->codec_id); |
150 | switch (stream->codecpar->codec_id) { |
151 | case AV_CODEC_ID_H264: |
152 | forced_codec_name = "h264_v4l2m2m"; |
153 | break; |
154 | case AV_CODEC_ID_HEVC: |
155 | forced_codec_name = "hevc_v4l2m2m"; |
156 | break; |
157 | case AV_CODEC_ID_VP9: |
158 | forced_codec_name = "vp9_v4l2m2m"; |
159 | break; |
160 | case AV_CODEC_ID_MPEG1VIDEO: |
161 | forced_codec_name = "mpeg1_v4l2m2m"; |
162 | break; |
163 | case AV_CODEC_ID_MPEG2VIDEO: |
164 | forced_codec_name = "mpeg2_v4l2m2m"; |
165 | break; |
166 | case AV_CODEC_ID_VC1: |
167 | forced_codec_name = "vc1_v4l2m2m"; |
168 | break; |
169 | case AV_CODEC_ID_H263: |
170 | forced_codec_name = "h263_v4l2m2m"; |
171 | break; |
172 | case AV_CODEC_ID_MPEG4: |
173 | forced_codec_name = "mpeg4_v4l2m2m"; |
174 | break; |
175 | case AV_CODEC_ID_MJPEG: |
176 | forced_codec_name = "mjpeg_v4l2m2m"; |
177 | break; |
178 | } |
179 | |
180 | codec = avcodec_find_decoder_by_name(forced_codec_name); |
181 | if (!codec) { |
182 | fprintf(stderr, "Unsupported codec with id %d for input stream %d\n", |
183 | stream->codecpar->codec_id, stream->index); |
184 | goto out; |
185 | } |
186 | |
187 | dec_ctx = avcodec_alloc_context3(codec); |
188 | if (!dec_ctx) { |
189 | fprintf(stderr, "Could not allocate video codec context\n"); |
190 | goto out; |
191 | } |
192 | |
193 | err = avcodec_parameters_to_context(dec_ctx, stream->codecpar); |
194 | if (err < 0) { |
195 | fprintf(stderr, "Could not set paras to context\n"); |
196 | goto out; |
197 | } |
198 | |
199 | av_codec_set_pkt_timebase(dec_ctx, stream->time_base); |
200 | dec_ctx->framerate = stream->avg_frame_rate; |
201 | |
202 | if (avcodec_open2(dec_ctx, codec, NULL) < 0) { |
203 | fprintf(stderr, "Could not open codec for input stream %d\n", |
204 | stream->index); |
205 | goto out; |
206 | } |
207 | |
208 | printf("fmt ctx: %p, stream: %p, video st idx: %d, num: %d\n", |
209 | ic, stream, vst_idx, ic->nb_streams); |
210 | printf("format: %s\n",ic->iformat->name); |
211 | |
212 | ic->flags |= AVFMT_FLAG_GENPTS; |
213 | ic->debug = 0xff; |
214 | |
215 | //if (ic->pb) |
216 | // ic->pb->eof_reached = 0; |
217 | |
218 | frame = av_frame_alloc(); |
219 | if (!frame) { |
220 | fprintf(stderr, "Could not allocate video frame\n"); |
221 | goto out; |
222 | } |
223 | |
224 | for (;;) { |
225 | ret = av_read_frame(ic, pkt); |
226 | if (ret < 0) { |
227 | if (ret == AVERROR_EOF || avio_feof(ic->pb)) { |
228 | printf("read data end, ret: %d.\n", ret); |
229 | break; |
230 | } |
231 | |
232 | if (ic->pb && ic->pb->error) |
233 | break; |
234 | |
235 | printf("read data fail, ret: %d.\n", ret); |
236 | continue; |
237 | } |
238 | |
239 | if (pkt->stream_index == vctx->vst_idx) { |
240 | //packet_queue_put(&is->audioq, pkt); |
241 | //printf("read video data size: %d.\n", pkt->size); |
242 | if (pkt->size) |
243 | decode(dec_ctx, frame, pkt, DUMP_DIR); |
244 | } |
245 | |
246 | av_packet_unref(pkt); |
247 | |
248 | usleep(8 * 1000); |
249 | } |
250 | |
251 | /* flush the decoder */ |
252 | decode(dec_ctx, frame, NULL, DUMP_DIR); |
253 | out: |
254 | if (dec_ctx) |
255 | avcodec_free_context(&dec_ctx); |
256 | |
257 | if (frame) |
258 | av_frame_free(&frame); |
259 | |
260 | if (ic) { |
261 | avformat_close_input(&ic); |
262 | avformat_free_context(ic); |
263 | } |
264 | |
265 | printf("read thread exit.\n"); |
266 | |
267 | pthread_mutex_lock(&vctx->pthread_mutex); |
268 | pthread_cond_signal(&vctx->pthread_cond); |
269 | pthread_mutex_unlock(&vctx->pthread_mutex); |
270 | |
271 | return NULL; |
272 | } |
273 | |
274 | static int open_input_file(const char *filename) |
275 | { |
276 | int ret; |
277 | VcodecCtx *vctx; |
278 | pthread_t pid = pthread_self(); |
279 | |
280 | vctx = av_mallocz(sizeof(VcodecCtx)); |
281 | if (!vctx) |
282 | return -1; |
283 | |
284 | vctx->filename = av_strdup(filename); |
285 | if (!vctx->filename) { |
286 | av_free(vctx); |
287 | return -1; |
288 | } |
289 | |
290 | pthread_mutex_init(&vctx->pthread_mutex, NULL); |
291 | pthread_cond_init(&vctx->pthread_cond, NULL); |
292 | |
293 | ret = pthread_create(&vctx->tid, NULL, read_thread, (void *)vctx); |
294 | if (ret == 0) { |
295 | pthread_setname_np(pid, "read_thread"); |
296 | |
297 | pthread_mutex_lock(&vctx->pthread_mutex); |
298 | pthread_cond_wait(&vctx->pthread_cond, &vctx->pthread_mutex); |
299 | pthread_join(vctx->tid, NULL); |
300 | pthread_mutex_unlock(&vctx->pthread_mutex); |
301 | } |
302 | |
303 | av_free(vctx->filename); |
304 | av_free(vctx); |
305 | |
306 | printf("creat the read thread, ret: %d.\n", ret); |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | int main(int argc, char **argv) |
312 | { |
313 | int ret; |
314 | const char *filename; |
315 | int log_level = 0; |
316 | |
317 | if (argc < 2) { |
318 | fprintf(stderr, "Usage: %s <input file>\n ==> %s/frame-123\n", argv[0], DUMP_DIR); |
319 | exit(0); |
320 | } |
321 | |
322 | filename = argv[1]; |
323 | if (argv[2]) { |
324 | if (!strcmp(argv[2], "dump")) |
325 | dump = 1; |
326 | else |
327 | log_level = atoi(argv[2]); |
328 | } |
329 | |
330 | mkdir(DUMP_DIR, 0664); |
331 | |
332 | /*set debug level*/ |
333 | av_log_set_level(log_level); //AV_LOG_DEBUG |
334 | |
335 | /* register all the codecs */ |
336 | avcodec_register_all(); |
337 | |
338 | ret = open_input_file(filename); |
339 | if (ret < 0) |
340 | fprintf(stderr, "open input file fail.\n"); |
341 | |
342 | return 0; |
343 | } |
344 |